GithubHelp home page GithubHelp logo

literal's Introduction

I’m a full-stack Ruby/TypeScript/CSS Engineer — formerly at Clearscope and Shopify. I maintain a few open source Ruby gems such as Phlex, Quickdraw and Literal. I’m also building a new DOM morphing library, Morphlex.

I write a (very) irregular newsletter, Naming Things, and cohost the Rooftop Ruby Podcast.

literal's People

Contributors

adam12 avatar erjanmx avatar joeldrapper avatar ljuti avatar stevegeek avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

literal's Issues

Literal Ivars

  • Figure out how to hook into code loading
  • Create a SyntaxTree processor for instance variable references
  • Ignore blocks protected by conditionals, e.g. if defined?(@ivar)
  • Ignore blocks protected by guard clauses, e.g. raise unless defined?(@ivar) or return unless defined?(@ivar) or next unless defined?(@ivar).

Inherited object and kwargs

Thanks for the solution proposed for supporting kwargs (#4)

I tried it and I am facing an error. It looks like the position of the kwargs attribute is important and this doesn't play well with inheritance.

What I am trying to achieve is to define an ApplicationComponent and within this ApplicationComponent define and atttributes attribute for the kwargs

Example

class ApplicationComponent < ViewComponent::Base
  extend Literal::Attributes

  attribute :display, _Boolean, default: true
  attribute :attributes, Hash, :**
end

This works as expected.
But then I would like my ButtonComponent to be defined like that

class ButtonComponent < ApplicationComponent
    attribute :text, String, default: "Hello, world!"

    def call
      content_tag(:button, @text, **@attributes)
    end
  end

but I am getting this error
CleanShot 2023-11-03 at 14 23 32@2x

Rails support

I’m not exactly sure what Rails support will look like — and whether it should be in this gem or perhaps a literal-rails gem — but here are a few ideas to kick things off:

  • ActiveJob serializers for Literal::Data and Literal::Struct objects
  • Generic ActiveRecord::Relation type (extract form Clearscope)
  • ActiveRecord types for Literal::Data and Literal::Struct objects
  • Ability to define type-checked attributes on ActiveRecord models (see #21)

Starting over

Literal is full of so many half-baked experiments. These experiments have taught us a lot about what’s possible. I’m creating a branch called legacy, which will be locked to the current state of main. If you’re using literal from git in your project, you should point to that branch for the next few months.

After that, I’m going to be ripping everything out and starting again, using the legacy branch as reference. This time, I’m only interested in implementing fully tested, documented features. Nothing half-baked.

The initial focus will be on:

  1. Types
  2. Attributes
  3. Structs
  4. Data Objects
  5. Unions
  6. Enums

Pattern matching + literal types

This is very much me thinking out loud, but I have found myself bouncing between 3 tools to gain confidence while implementing complex method signatures:

  1. tests
  2. types
  3. pattern matching

Pattern matching is core the implementation, as it allows to cleanly branch the various signature types/groups. While it does throw errors for unmatched patterns (presuming no else branch), it doesn't "publish" the implicit shape of the method for the outside world. This is a power of types. Not only do they provide errors for unmatched inputs, they publish an explicit shape for the method to the outside world. Tests, finally, exercise the code paths to ensure that inputs that match the various shape configurations produce the desired outcome.

Currently, there is no code-level connection between any of these 3 layers. I would love to somehow draw them more closely together.

When thinking about connecting types and patterns, I am wondering if it would be possible to somehow define a named type that could act as a pattern, e.g.

class SelectionParser
  SelectableColumn = Symbol
  SelectableExpression = String
  SelectableAssociation = Hash
  
  Selectable = _Union(SelectableColumn, SelectableExpression, SelectableAssociation)
  
  def self.call(input)
    case input
    in SelectableColumn => column
      parse_column(column)
    in SelectableExpression => expression
      parse_expression(expression)
    in SelectableAssociation => association
      parse_association(association)
    end
  end
end

I don't currently have a thought for connecting types and tests, but even just types as patterns would be amazing.

undefined method `after_defined'

Just upgraded from 211eb65 to fd94c7d (latest) and running my tests using sus...

/Users/joelmoss/.rbenv/versions/3.2.2/lib/ruby/gems/3.2.0/bundler/gems/literal-fd94c7d4f36c/lib/literal/module_defined.rb:16:in `block in <top (required)>': undefined method `after_defined' for Sus::Be:Class (NoMethodError)

                tp.self.after_defined
                       ^^^^^^^^^^^^^^
        from /Users/joelmoss/.rbenv/versions/3.2.2/lib/ruby/gems/3.2.0/gems/sus-0.24.2/lib/sus/be.rb:59:in `<class:Be>'
        from /Users/joelmoss/.rbenv/versions/3.2.2/lib/ruby/gems/3.2.0/gems/sus-0.24.2/lib/sus/be.rb:7:in `<module:Sus>'
        from /Users/joelmoss/.rbenv/versions/3.2.2/lib/ruby/gems/3.2.0/gems/sus-0.24.2/lib/sus/be.rb:6:in `<top (required)>'
        from /Users/joelmoss/.rbenv/versions/3.2.2/lib/ruby/gems/3.2.0/gems/sus-0.24.2/lib/sus.rb:13:in `require_relative'
        from /Users/joelmoss/.rbenv/versions/3.2.2/lib/ruby/gems/3.2.0/gems/sus-0.24.2/lib/sus.rb:13:in `<top (required)>'
        from /Users/joelmoss/.rbenv/versions/3.2.2/lib/ruby/gems/3.2.0/gems/zeitwerk-2.6.12/lib/zeitwerk/kernel.rb:38:in `require'
        from /Users/joelmoss/.rbenv/versions/3.2.2/lib/ruby/gems/3.2.0/gems/zeitwerk-2.6.12/lib/zeitwerk/kernel.rb:38:in `require'
        from /Users/joelmoss/.rbenv/versions/3.2.2/lib/ruby/gems/3.2.0/gems/bundler-2.4.21/lib/bundler/runtime.rb:60:in `block (2 levels) in require'
        from /Users/joelmoss/.rbenv/versions/3.2.2/lib/ruby/gems/3.2.0/gems/bundler-2.4.21/lib/bundler/runtime.rb:55:in `each'
        from /Users/joelmoss/.rbenv/versions/3.2.2/lib/ruby/gems/3.2.0/gems/bundler-2.4.21/lib/bundler/runtime.rb:55:in `block in require'
        from /Users/joelmoss/.rbenv/versions/3.2.2/lib/ruby/gems/3.2.0/gems/bundler-2.4.21/lib/bundler/runtime.rb:44:in `each'
        from /Users/joelmoss/.rbenv/versions/3.2.2/lib/ruby/gems/3.2.0/gems/bundler-2.4.21/lib/bundler/runtime.rb:44:in `require'
        from /Users/joelmoss/.rbenv/versions/3.2.2/lib/ruby/gems/3.2.0/gems/bundler-2.4.21/lib/bundler.rb:187:in `require'
        from /Users/joelmoss/dev/clients/harleytherapy/hue/config/sus.rb:5:in `load'
        from /Users/joelmoss/.rbenv/versions/3.2.2/lib/ruby/gems/3.2.0/gems/sus-0.24.2/lib/sus/config.rb:26:in `module_eval'
        from /Users/joelmoss/.rbenv/versions/3.2.2/lib/ruby/gems/3.2.0/gems/sus-0.24.2/lib/sus/config.rb:26:in `load'
        from /Users/joelmoss/.rbenv/versions/3.2.2/lib/ruby/gems/3.2.0/gems/sus-0.24.2/bin/sus:7:in `<top (required)>'
        from ./bin/sus:27:in `load'
        from ./bin/sus:27:in `<main>'

Doesn't look like this is a sus problem? 🤔

Switch to `prop` instead of `attribute`

The attribute macro is defined on Active Model and various other gems. I think we should consider using a different name. The best I’ve been able to come up with is prop, short for property — and Literal::Attributes could be renamed Literal::Properties.

I’d appreciate hearing any thoughts on this.

AttributesProxy

A module that behaves like Literal::Attributes, but proxies all the setting and getting to super. This could be used on ActiveRecord models, for example.

Add more context to error messages

We can usually add some additional context such as the name of the attribute. Additionally, collection types could implement an alternative interface that allows them to highlight specifically which member failed and why.

Undocumented Zeitwerk dependency

Running literal without zeitwerk installed raises the error:

.rbenv/versions/3.2.1/lib/ruby/gems/3.2.0/gems/literal-0.1.0/lib/literal.rb:6:in `<module:Literal>': uninitialized constant Literal::Zeitwerk (NameError)

  Loader = Zeitwerk::Loader.for_gem.tap(&:setup)

Sample:

require "bundler/inline"

gemfile do
  source "https://rubygems.org"
  gem "literal"
end

class Address < Literal::Struct
  attribute :street, String
  attribute :city, String
  attribute :state, String
end

save that in a file called main.rb a run ruby main.rb

Added gem 'zeitwork' to the gem file fixes it :

gemfile do
  source "https://rubygems.org"

  gem 'zeitwerk', '~> 2.4.2'
  gem "literal"
end

`attribute` `default` option doesn't work with `:&`

This doesn't work:

class Procy
 extend Literal::Attributes
 attribute :some_proc, Proc, :&, default: -> { proc {} }
end
irb(main):016> Procy.new
/ruby/gems/3.2.0/bundler/gems/literal-455f225574db/lib/literal/attributable.rb:81:in `initialize': Expected `nil` to be of type: `Proc`. (Literal::TypeError)

Support default values

I think attribute could take a block to calculate the default value. Alternatively, the default: keyword argument with a Proc.

Production options

We should provide the following:

  1. Disable all type-checking in production
  2. Disable expensive type-checking in production
  3. Switch expensive type-checking to use samples instead (e.g. check that one item from an array matches the expected type)

allow for optional kwargs

Thanks for the new Gem it looks very promising

When working with components I often add a kwargs argument to my component initializer that I pass down to my element builder

class Link < Phlex::HTML
  def initialize(href:, **kwargs)
    @href = href
    @kwargs = kwargs
  end

  def template(&)
    a href: @href, **@kwargs, &
  end
end

I was wondering if this is something Literal Gem would like to support so that we could dry up our components initializers with attributes while preserving the ability to pass kwargs to component

We could then potentially write our component like that

class Link < Phlex::HTML
  extend Literal::Attributes

  attribute :href, String

  def template(&)
    a href: @href, **@kwargs, &
  end
end

not sure if we should magically define kwargs or make it available more explicitly by including another module or adding a new method to the dsl. Maybe also kwargs is not the best name. In JS world, I often used rest for that

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.