These are chat archives for dry-rb/chat

14th
Apr 2016
Andy Holland
@AMHOL
Apr 14 2016 00:15
class Dry::Types::Registry < Dry::Container::Registry
  def call(container, key, item, options)
    puts "registering under #{key}"
    super
  end
end

Dry::Types.container.class.class_eval do
  configure do |config|
    config.registry = Dry::Types::Registry.new
  end
end

module Dry
  module Types
    class Struct
      def self.before_remove_const
        puts "Removing #{Dry::Types.identifier(self).inspect}"
        Dry::Types.container._container.delete(Dry::Types.identifier(self))
        puts Dry::Types.container.key?(Dry::Types.identifier(self)).inspect
      end
    end
  end
end

irb(main):001:0> Types::Address.new
registering under types.address
=> #<Types::Address zip=nil city=nil>
irb(main):002:0> reload!
Reloading...
=> true
irb(main):003:0> Types::Address.new
registering under types.address
Removing "types.address"
false
Dry::Container::Error: There is already an item registered with the key "types.address"
Fucking inherited is called before .before_remove_const
Nikita Shilnikov
@flash-gordon
Apr 14 2016 00:24
lol
Andy Holland
@AMHOL
Apr 14 2016 00:26
Honestly tho, WTF is this
I have no idea what's going on
But it's loading constants before removing them it would seem
Andy Holland
@AMHOL
Apr 14 2016 00:49
So, the best thing I could come up with in this shitstorm is
module Dry
  module Types
    REGISTERED_STRUCTS = Concurrent::Array.new
    STRUCT_INHERITED = Struct.method(:inherited)

    class Struct
      def self.inherited(klass)
        REGISTERED_STRUCTS << klass
        STRUCT_INHERITED.call(klass)
      end

      def self.unregister
        Dry::Types.container._container.delete(Dry::Types.identifier(self))
      end
    end

    module Rails
      class Railtie < ::Rails::Railtie
        config.to_prepare do
          REGISTERED_STRUCTS.uniq.each(&:unregister)
        end
      end
    end
  end
end
In an initializer
Simon Schmid
@sled
Apr 14 2016 00:56
bad news, it seems that this also causes trouble with one struct having another struct as attribute
Andy Holland
@AMHOL
Apr 14 2016 00:57
With the above ^
?
Simon Schmid
@sled
Apr 14 2016 00:57
module Types
  class Country < Dry::Types::Struct
    attribute :name, Types::String
    attribute :code, Types::String
  end
end

module Types
  class Address < Dry::Types::Struct
    attribute :zip,  Types::Int
    attribute :city, Types::String
    attribute :country, Types::Country
  end
end
Andy Holland
@AMHOL
Apr 14 2016 00:58
Works
Simon Schmid
@sled
Apr 14 2016 01:00
2.2.1 :001 > Types::Address.new(country: { name: 'Switzerland', code: 'CH' }, zip: '1234', city: 'Zurich').country.code
 => "CH"

now I change the country code to Types::Strict::Int

2.2.1 :002 > reload!
Reloading...
 => true
2.2.1 :003 > Types::Address.new(country: { name: 'Switzerland', code: 'CH' }, zip: '1234', city: 'Zurich').country.code
 => "CH"

now I quit and restart with rails c:

2.2.1 :004 > quit
dry-reloading$ rails c
Loading development environment (Rails 4.2.6)
2.2.1 :001 > Types::Address.new(country: { name: 'Switzerland', code: 'CH' }, zip: '1234', city: 'Zurich').country.code
Dry::Types::StructError: [Types::Address.new] {:name=>"Switzerland", :code=>"CH"} (Hash) has invalid type for :country
@AMHOL
this is what I was afraid of :/
Andy Holland
@AMHOL
Apr 14 2016 01:03
You got anything else in there?
That works fine for me
Simon Schmid
@sled
Apr 14 2016 01:03
did you put the country in a separate file?
Andy Holland
@AMHOL
Apr 14 2016 01:03
Oh, it doesn't reload them?
I guess this is because the inherited hook is called before the constant is removed :/
Andy Holland
@AMHOL
Apr 14 2016 01:11
Hmm, yeah, reloading just doesn't work at all
Even if I do
class Dry::Types::Registry < Dry::Container::Registry
  def call(container, key, item, options)
    container[key] = ::Dry::Container::Item.new(item, options)
  end
end

Dry::Types.container.class.class_eval do
  configure do |config|
    config.registry = Dry::Types::Registry.new
  end
end
Screw Rails
I CBA spending any more time on this
Tim Riley
@timriley
Apr 14 2016 01:16
If only its boot wasn’t so slow, then we could feasibly just use shotgun+config.ru
Andy Holland
@AMHOL
Apr 14 2016 01:21
It's strange because Types::Address.schema[:country].schema[:code] gets updated correctly
Simon Schmid
@sled
Apr 14 2016 01:22
mhhh what's in the container
Andy Holland
@AMHOL
Apr 14 2016 01:23
Also Types::Address.schema[:country].constructor[name: 'Switzerland', code: 'CH'] errors correctly
Ahh it's just the Address constructor it seems
Simon Schmid
@sled
Apr 14 2016 01:24
2.2.1 :006 > Dry::Types.container["types.address"]
 => #<Dry::Types::Constructor type=#<Dry::Types::Definition primitive=Types::Address options={}>>
2.2.1 :009 > Dry::Types.container["types.address"].type.primitive.schema
 => {:zip=>#<Dry::Types::Definition primitive=Integer options={}>, :city=>#<Dry::Types::Definition primitive=String options={}>, :country=>Types::Country}
2.2.1 :012 > Dry::Types.container["types.address"].type.primitive.schema[:country].schema
 => {:name=>#<Dry::Types::Definition primitive=String options={}>, :code=>#<Dry::Types::Definition primitive=String options={}>}
Simon Schmid
@sled
Apr 14 2016 01:31
hmmm
this is really insane!
2.2.1 :011 > Types::Address.new(country: { name: 'Switzerland', code: 'CH' }, zip: '1234', city: 'Zurich').class.schema[:country].schema
 => {:name=>#<Dry::Types::Definition primitive=String options={}>, :code=>#<Dry::Types::Constrained:0x007fc24488ceb8 @type=#<Dry::Types::Definition primitive=Integer options={}>, @options={:rule=>#<Dry::Logic::Rule::Value predicate=#<Dry::Logic::Predicate id=:type? args=[Integer]> options={}>}, @rule=#<Dry::Logic::Rule::Value predicate=#<Dry::Logic::Predicate id=:type? args=[Integer]> options={}>>}
it basically allows me to create an invalid type (code should be Strict::Int), and when I'm asking the invalid instance about the schema it tells me it is using Strict::Int
wtf
Andy Holland
@AMHOL
Apr 14 2016 01:39
:/
Simon Schmid
@sled
Apr 14 2016 01:40
although....
this is creating an anonymous module
Andy Holland
@AMHOL
Apr 14 2016 01:47
Dry::Types.container['types.address'][country: { name: 'Switzerland', code: 'CH' }, zip: '1234', city: 'Zurich'] doesn't error either tho
I wonder whether it's because address is loaded first
Although multiple reloads should work then
Simon Schmid
@sled
Apr 14 2016 01:56
yep the Country itself applies the change from Types::String to Types::Strict::Int but the Address doesn't seem to care....
also if I add a new method on the struct e.g
module Types
  class Country < Dry::Types::Struct
    attribute :name, Types::String
    attribute :code, Types::Strict::Int

    def hello
      puts "Hello"
    end
  end
end
2.2.1 :025 > Types::Country.new({name: 'Bla', code: 123}).hello
Hello
 => nil

2.2.1 :024 > Types::Address.new(country: { name: 'Switzerland', code: 123 }, zip: '1234', city: 'Zurich').country.hello
NoMethodError: undefined method `hello' for #<Types::Country name="Switzerland" code=123>
Simon Schmid
@sled
Apr 14 2016 02:12
it seems like Dry::Types lives in its own world :/
Andy Holland
@AMHOL
Apr 14 2016 02:14
Dry::Types['types.country'][name: 'Bla', code: 123] reproduces the error
Simon Schmid
@sled
Apr 14 2016 02:19
yes it seems like in Dry::Types the constants are not getting replaced
because Types::Address will instantiate the country from Dry::Types's registry
Andy Holland
@AMHOL
Apr 14 2016 02:31
Sooooooo
Lazy evaluation works
module Dry
  module Types
    REGISTERED_STRUCTS = Concurrent::Array.new
    STRUCT_INHERITED = Struct.method(:inherited)

    def self.register_class(klass, meth = :new)
      type = Definition.new(klass).constructor do |*args, &block|
        Object.const_get(klass.to_s).public_send(meth, *args, &block)
      end
      container.register(identifier(klass), type)
    end

    class Struct
      def self.inherited(klass)
        REGISTERED_STRUCTS << klass
        STRUCT_INHERITED.call(klass)
      end

      def self.unregister
        Dry::Types.container._container.delete(Dry::Types.identifier(self))
      end
    end

    module Rails
      class Railtie < ::Rails::Railtie
        config.to_prepare do
          REGISTERED_STRUCTS.uniq.each(&:unregister)
        end
      end
    end
  end
end
Simon Schmid
@sled
Apr 14 2016 02:32
confirm
it works
Andy Holland
@AMHOL
Apr 14 2016 02:33
I wonder why re-registering still gets the .new method from the old version of the class
What a pile of crap that code is tho
Simon Schmid
@sled
Apr 14 2016 02:44
@AMHOL I think I found the thing which breaks it:
This message was deleted
module Types
  class Test
    def say
      "Hello"
    end
  end
end
Andy Holland
@AMHOL
Apr 14 2016 02:44
?
Simon Schmid
@sled
Apr 14 2016 02:45
2.2.1 :001 > a = Types::Test.method(:new)
 => #<Method: Class#new>
2.2.1 :002 > a.call.say
 => "Hello"
now you change it to "Bye" in the method
2.2.1 :003 > reload!
Reloading...
 => true
2.2.1 :004 > a.call.say
 => "Hello"
it still says hello
Andy Holland
@AMHOL
Apr 14 2016 02:45
Yeah
That's why I wrapped it in a block
But even then I needed to call Object.const_get(klass.to_s) to get the class at call-time
So I'm pretty sure it's a reloading order problem
The thing with inherited being called before the constant is removed
Which I don't get at all
Simon Schmid
@sled
Apr 14 2016 03:02
mh unbinding and binding the method again works
e.g meth.unbind.bind(Types::Test).call
Simon Schmid
@sled
Apr 14 2016 03:14
thanks a ton @AMHOL
Andy Holland
@AMHOL
Apr 14 2016 03:14
No problem
Wish I had a nicer solution
Piotr Solnica
@solnic
Apr 14 2016 07:34
Dear Lord what happened here
Tim Riley
@timriley
Apr 14 2016 07:35
It’s like stumbling upon a ransacked house
or the aftermath of a bloody battle
Piotr Solnica
@solnic
Apr 14 2016 07:35
How about not reloading types at all
Tim Riley
@timriley
Apr 14 2016 07:35
Good morning :)
Piotr Solnica
@solnic
Apr 14 2016 07:35
Morning
Benjamin Klotz
@tak1n
Apr 14 2016 07:35
morning :D
Tim Cooper
@coop
Apr 14 2016 08:27
@solnic would it be worth breaking dry-rb/dry-validation#99 down? I could see someone implementing Dry::Validation.Model separatly to key -> required.
Piotr Solnica
@solnic
Apr 14 2016 08:35
@coop yes
@coop I don’t plan to work on Model part
Tim Cooper
@coop
Apr 14 2016 08:38
Ok, alternative suggestion. Could I delete the attr code so someone can take on #99 and just focus on the rename and we reintroduce Dry::Validation.Model later? Or is it something you want to still include with dry-v?
Piotr Solnica
@solnic
Apr 14 2016 08:38
I want it to be moved to a separate gem
Tim Cooper
@coop
Apr 14 2016 08:39
Sure - if I was to submit a PR that removed it could it be megered before it was introduced as a separate gem?
I guess I’m asking if we’d be ok to lose that functionality until it was implemented as a separate gem.
Piotr Solnica
@solnic
Apr 14 2016 08:40
yes, just create a attr-dsl branch before you remove it
so that we have a reference
Tim Cooper
@coop
Apr 14 2016 08:40
At the moment there isn’t any documentation about it on the website.
Piotr Solnica
@solnic
Apr 14 2016 08:40
then somebody can just use it to see what’s needed to create a model extension gem
Tim Cooper
@coop
Apr 14 2016 08:40
Awesome. I can do that :)
Piotr Solnica
@solnic
Apr 14 2016 08:41
I know, it’s because I was annoyed with this and didn’t want to spend even more time on this gem
Tim Cooper
@coop
Apr 14 2016 08:41
I personally don’t see myself using it.
Piotr Solnica
@solnic
Apr 14 2016 08:41
me too
Tim Cooper
@coop
Apr 14 2016 08:41
I think you’ve said that same thing previously.
That makes sense.
Piotr Solnica
@solnic
Apr 14 2016 08:41
are you saying you’re up to do the rename and value addition?
Tim Cooper
@coop
Apr 14 2016 08:42
I would do one at a time but I’d like to have a go at the rename afterwards if no one else takes it on.
Piotr Solnica
@solnic
Apr 14 2016 08:44
@coop fantastic, you could do it in three steps I believe
Tim Cooper
@coop
Apr 14 2016 08:45
What do you see as the steps?
Piotr Solnica
@solnic
Apr 14 2016 08:45
1) key gets deprecated in favor of required
2) value is added
3) required gets deprecated in favor of filled which sits on top of value(:filled?)
Tim Cooper
@coop
Apr 14 2016 08:46
Right that makes sense.
I will have some time this week - I will see how I go.
Piotr Solnica
@solnic
Apr 14 2016 08:48
I’m going on holidays next week so I won’t be online, once I’m back I want to wrap up dry-v 0.8 and rom 2.0 + rom-sql/repository update and then disappear for a few weeks
shoot me please
this is like…an 8th clone of virtus :(
Tim Cooper
@coop
Apr 14 2016 09:02
Virtus is the first step that most people take when trying to separate ActiveRecord and their domain. Admittedly the code is complicated (because it is solving a complicated problem) so IG et why someone is trying to re-implement it.
Piotr Solnica
@solnic
Apr 14 2016 09:03
I was hoping more people would look into dry-* gems rather than making the same mistakes
James Hamilton
@wjdhamilton
Apr 14 2016 09:04
You probably just need to give it more time and let some high profile people start using them.
Piotr Solnica
@solnic
Apr 14 2016 09:05
once these gems hit 1.0 I’m gonna spend more time on promotion and education
Tim Cooper
@coop
Apr 14 2016 09:05
I agree. It’s kind of shitty.
Do what trailblazer did and promote the shit out of it :P
Piotr Solnica
@solnic
Apr 14 2016 09:05
I don’t feel confident enough to go nuts with promotion
I made that mistake with rom
Tim Cooper
@coop
Apr 14 2016 09:06
What do you mean?
BTW Hash#dig - saw this in our codebase today:
class SomeClass
  def self.accessible_field(field, path)
    define_method(field) do
      @data.dig(*path)
    end
  end

  accessible_field :acquirer_id, %w(donation gift acquirer_id)
end
Piotr Solnica
@solnic
Apr 14 2016 09:06
some time ago I thought it would help the project because more people would get involved, but it’s not how it works
James Hamilton
@wjdhamilton
Apr 14 2016 09:06
As in you need to promote it to the right people?
Piotr Solnica
@solnic
Apr 14 2016 09:07
yes, pretty much, find a small group of people who understand what’s going on and are able to help
James Hamilton
@wjdhamilton
Apr 14 2016 09:07
Yeah, and then if they are properly connected they can promote it too.
Piotr Solnica
@solnic
Apr 14 2016 09:08
yes, that’s what I’ve done eventually. we have a really nice small communities in rom/dry world
James Hamilton
@wjdhamilton
Apr 14 2016 09:08
Yes, you do all seem very friendly
Piotr Solnica
@solnic
Apr 14 2016 09:09
:)
Tim Cooper
@coop
Apr 14 2016 09:10
@solnic dry-rb/dry-validation#107 - update dry-types dep dry-v to support JSON coercions.
Piotr Solnica
@solnic
Apr 14 2016 09:10
oh yeah man just push the green button
Tim Cooper
@coop
Apr 14 2016 09:11
Cool.
I’m still going to submit PRs until I feel comfortable :D
Piotr Solnica
@solnic
Apr 14 2016 09:13
@coop sure :)
Simon Schmid
@sled
Apr 14 2016 11:29
by the way the wiki seems to be dead: https://github.com/dry-rb/dry-validation/wiki
"Please refer to the wiki for full usage documentation."
Piotr Solnica
@solnic
Apr 14 2016 11:32
this should be removed, we have docs on the website now
@AMHOL ^ :laughing:
Andy Holland
@AMHOL
Apr 14 2016 12:35
@solnic the tweet about clojure?
Piotr Solnica
@solnic
Apr 14 2016 12:35
Screen Shot 2016-04-14 at 14.35.34.png
got a new cover photo :P
Tim Cooper
@coop
Apr 14 2016 12:38
Very … interesting :P
Andy Holland
@AMHOL
Apr 14 2016 12:39
:joy: how did I not notice that
Tim Cooper
@coop
Apr 14 2016 12:42

@solnic removing attr was easy dry-rb/dry-validation#108

I will take a look at renaming key -> required tomorrow.

https://github.com/evanphx/benchmark.fyi - should replace with dry-web :P
And ROM.
It would make a good example "production ready” app.
Piotr Solnica
@solnic
Apr 14 2016 12:48
@coop merged
Tim Cooper
@coop
Apr 14 2016 12:48
Cool - breaking it down into 3 or 4 steps made it a lot clearer for me. Should be able to get most of it done this weekend.
Piotr Solnica
@solnic
Apr 14 2016 15:25
yeah, although it’s really unfortunately that ruby doesn’t have a single concept of a first-class function object, and methods would just be functions bound to an object
Simon Schmid
@sled
Apr 14 2016 15:26
mh I think one can have UnboundMethod's
Piotr Solnica
@solnic
Apr 14 2016 15:26
yeah I use them too
I’m pretty sure I used all kinds of features when it comes to procs, lambdas and method objects :laughing:
why not using a SimpleDelegator ?
Piotr Solnica
@solnic
Apr 14 2016 15:46
it doesn’t have the same behavior
lots of inspiration for dry-component right there ^
Simon Schmid
@sled
Apr 14 2016 17:01
https://github.com/ulfurinn/wongi-engine
^ cool algorithm they're using :)
Michał Pietrus
@blelump
Apr 14 2016 17:09
@solnic , is it possible to combine Types::Hash.symbolized behavior with strict? I mean where list of keys always matches given schema
Piotr Solnica
@solnic
Apr 14 2016 17:21
no, we’re evaluating what kind of behaviors we want to have, this is related to struct discussion in dry-rb/dry-types#72
Michał Pietrus
@blelump
Apr 14 2016 17:31
Thanks !
Fran Worley
@fran-worley
Apr 14 2016 18:26
@solnic Any objections to adding a not_eql? predicate to dry-logic? there seem to be opposites of most of the others...
Fran Worley
@fran-worley
Apr 14 2016 18:33
dry-rb/dry-logic#7
Piotr Solnica
@solnic
Apr 14 2016 19:05
@fran-worley :+1: please merge when green, just gave you commit access
you may also consider just doing ! left.eql?(right) instead of negating eql? predicate, tiny perf improvement
Fran Worley
@fran-worley
Apr 14 2016 19:07
Good to know, I'll change it and merge thanks
Piotr Solnica
@solnic
Apr 14 2016 19:09
@fran-worley could you also add support for this to dry-v?
as in, the error message
Fran Worley
@fran-worley
Apr 14 2016 19:10
Yup will do :)
Russell Edens
@rx
Apr 14 2016 20:06
I'm trying to figure out how to use a dry-type in a validation.
The following would be nice, but throws a TypeError:
UUID = Types::Strict::String.constrained(
        format: /\A[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}\z/i)

schema = Validation.Schema do
  key(:uuid) { type?(UUID) }
end

schema.(uuid: SecureRandom.uuid)
Piotr Solnica
@solnic
Apr 14 2016 20:10
@rx key(:uuid).required(UUID)
type? Is for checking the actual primitive type of a value. Which is String in your case
Russell Edens
@rx
Apr 14 2016 20:12
@solnic that was easy. thanks.
Piotr Solnica
@solnic
Apr 14 2016 20:38
meh
Tim Cooper
@coop
Apr 14 2016 20:40
There is a real interest in validating data, it looks like most people want to stick it in a model.
Piotr Solnica
@solnic
Apr 14 2016 20:41
of course, because it’s OO
that was re “stick it in a model"
Benjamin Klotz
@tak1n
Apr 14 2016 20:43

@solnic @timriley @AMHOL is there something I can help with for some dry-* gems?
if nothing specific I will just look at rubinius compatibility (as I'm a rare rbx contributor :D) for dry-v, dry-types etc, this way I can help multiple projects I want to contribute to :P

I think I will start with https://travis-ci.org/dry-rb/dry-validation/jobs/123168059

Piotr Solnica
@solnic
Apr 14 2016 20:44
@tak1n thanks but please don’t spend time on rbx in dry-v
I’ll be refactoring this code by removing heaps of LOC and this problem will probably go away
Benjamin Klotz
@tak1n
Apr 14 2016 20:44
@solnic kk :D
Piotr Solnica
@solnic
Apr 14 2016 20:44
if not, I’ll let you know :P
Benjamin Klotz
@tak1n
Apr 14 2016 20:45
okay :+1: :D
so I think I will do some experimenting, learn everything better and then start to look through the gh issues to see if I can do smth or not :D
Piotr Solnica
@solnic
Apr 14 2016 20:46
this is a good plan
Tim Cooper
@coop
Apr 14 2016 20:48
Have you guys written a blog post on why validation as a separate object is a good idea and how'd youd string validation + dry-types struct struct together in a real world app?
Piotr Solnica
@solnic
Apr 14 2016 20:48
kind-of
Simon Schmid
@sled
Apr 14 2016 20:48
this would be an interesting read
Tim Cooper
@coop
Apr 14 2016 20:49
There are lots of examples how you can use AM::V. I havent really seen anything like that for dry-* libs.
Piotr Solnica
@solnic
Apr 14 2016 20:49
yeah these are fresh ideas in the ruby world
Tim Cooper
@coop
Apr 14 2016 20:50
I think they are good and worth promoting.
Piotr Solnica
@solnic
Apr 14 2016 20:51
yeah but I gotta tell you it’s hard work, most rubyists are deeply influenced by rails and explaining things like rom or dry is difficult and even frustrating sometimes
Simon Schmid
@sled
Apr 14 2016 20:52
there's one sad use-case for the invalid state, and that is re-rendering data in case of a validation failure. Best example is an email, I enter "me2example.com", since this won't pass the type check it's nil at best and I'd have to re-enter the complete email address
Piotr Solnica
@solnic
Apr 14 2016 20:52
that’s so not true, you’ve got the params hash, just use it to re-render the form
there’s a difference between a plain hash which represents (potentially broken) data and a core domain object that can also carry invalid data (when I think it should not be allowed)
Christopher Dennl-Ortega Arrieta
@cdennl
Apr 14 2016 20:53
@solnic I can see your point about difficulties of spreading
Simon Schmid
@sled
Apr 14 2016 20:53
@solnic yes, as soon as form builder comes into play, you got a big PITA ;)
Christopher Dennl-Ortega Arrieta
@cdennl
Apr 14 2016 20:53
however from a software architects and developer view
Piotr Solnica
@solnic
Apr 14 2016 20:53
well, we’re just missing a form builder
Simon Schmid
@sled
Apr 14 2016 20:53
problem is it doesn't quacks like actionview wants it to
Christopher Dennl-Ortega Arrieta
@cdennl
Apr 14 2016 20:53
I support these gems
Tim Cooper
@coop
Apr 14 2016 20:53
I havent done much writing in the past but I'd be willing to give it a go. It's gross seeing "new" self validating libraries being released, it isn't going to stop unless we can produce end to end examples. That post is interesting but doesnt show taking data from params and persisting it.
Christopher Dennl-Ortega Arrieta
@cdennl
Apr 14 2016 20:53
as well as trailblazer
rails definitily has these lacks
I see it in my own apps
Tim Cooper
@coop
Apr 14 2016 20:54
Integrating with forms is a good example.
Simon Schmid
@sled
Apr 14 2016 20:54
also translation come into play here for attributes
Piotr Solnica
@solnic
Apr 14 2016 20:54
https://www.reddit.com/r/ruby/comments/4eqk4h/my_past_and_future_ruby/ <== look at comments, Tim wrote a really nice post about how refreshing it was to dive into rom/dry world, provided links to projects etc. and people reaction is “but rails is ok, I can even write sql manually"
Christopher Dennl-Ortega Arrieta
@cdennl
Apr 14 2016 20:55
haha
Well these might be statements from hobby users
Piotr Solnica
@solnic
Apr 14 2016 20:56
no, really, this is sad. it’s like there’s no more enthusiasm in this community about new stuff
it’s just rails rails blah blah blah active record blah blah blah actionsomething blah blah oh look new cool monkey-patch blah blah blah
Christopher Dennl-Ortega Arrieta
@cdennl
Apr 14 2016 20:57
rails gives an easy intro
well look
so every monkeey can learn to rails
and must will stuck with this atittude
Christopher Dennl-Ortega Arrieta
@cdennl
Apr 14 2016 20:57
it gets things done
Piotr Solnica
@solnic
Apr 14 2016 20:58
I know, I get that :)
Simon Schmid
@sled
Apr 14 2016 20:58
problem is, rails set new standards for development time, everything can be built quick and shitty ;) so it's hard to justify why you'd need to spend extra time adapting this to that - it's like we dug our own hole :(
Piotr Solnica
@solnic
Apr 14 2016 20:58
true
Christopher Dennl-Ortega Arrieta
@cdennl
Apr 14 2016 20:58
well for prototyping ahead it is fine
but if you are for maintanability
Piotr Solnica
@solnic
Apr 14 2016 20:58
you can be as efficient with other tools, seriously
Christopher Dennl-Ortega Arrieta
@cdennl
Apr 14 2016 20:58
in the long run
Piotr Solnica
@solnic
Apr 14 2016 20:59
just apply Rails-like-DRY and conventions and you’re done, because this is the only secret
Christopher Dennl-Ortega Arrieta
@cdennl
Apr 14 2016 20:59
hehe yeah
Piotr Solnica
@solnic
Apr 14 2016 20:59
the problem is how it’s done and what’s under the hood
Christopher Dennl-Ortega Arrieta
@cdennl
Apr 14 2016 20:59
well I think it is a common problem in web dev in general
it looks easy
you get easy results
thats it
Tim Cooper
@coop
Apr 14 2016 21:01
I was wondering how long before someone had a go at ROM in the comments of @timriley post haha.
Christopher Dennl-Ortega Arrieta
@cdennl
Apr 14 2016 21:01
and then there are ppl like apotonick who have there income to clean up the mess after some time
*their
as every solid contractor has their income :D
Piotr Solnica
@solnic
Apr 14 2016 21:01
mess will always be there, it’s more about what kind of a mess it is :P
Christopher Dennl-Ortega Arrieta
@cdennl
Apr 14 2016 21:02
At my job currently I'm migrating a 20year old grown ClearCade environtment into a TFS envirionment including requirement management and test management
the mess is big :D
*ClearCase
Simon Schmid
@sled
Apr 14 2016 21:04
yes... I definitely have to de-rail on a small project ;)
Christopher Dennl-Ortega Arrieta
@cdennl
Apr 14 2016 21:04
When I first started rails (entered rails at 3.1) I was hyped too
after some private apps hitting the 2 or 3 years amrk
I was like uff
Fran Worley
@fran-worley
Apr 14 2016 21:04
@cdennl Exactly where I am at!
Christopher Dennl-Ortega Arrieta
@cdennl
Apr 14 2016 21:05
Then I discovered Trailblazer and the dry gems
which gave me new hope :D
thats my point from view who is private web dev enthusiast, I don't earn my money yet with Web Dev
*as
but from a general software devs and architects view
rails is a mess
too less abstraction / separation of concers
Piotr Solnica
@solnic
Apr 14 2016 21:07

It is quite possible that he was stuck in some bad patterns and needed to see something else to break that

this made me chuckle a bit. I’ve been using rails since 1.0.something and I honestly never ever managed to figure out how to approach things in rails to make the arch better. Now, TIM has been using rails even longer :laughing:

Christopher Dennl-Ortega Arrieta
@cdennl
Apr 14 2016 21:08
I'm sure there are professional rails companies out there who have all home-baked solutions
for all the same problems
you are also solving with dry
as soon as you can spread the word how time / costs are saved when using dry* ppl will use it
at least ppl who want to earn money
Christopher Dennl-Ortega Arrieta
@cdennl
Apr 14 2016 21:15
So in this spirit, good night folks, keep your good work up :) and don't waste time with ignorant ppl :D
Andy Holland
@AMHOL
Apr 14 2016 21:16
and people reaction is “but rails is ok, I can even write sql manually" :joy:
Piotr Solnica
@solnic
Apr 14 2016 21:21
@cdennl night :) and thanks for your support
@AMHOL I was not joking. Just read the comments
Andy Holland
@AMHOL
Apr 14 2016 21:22
Reading them now :/
Piotr Solnica
@solnic
Apr 14 2016 21:22
M
Simon Schmid
@sled
Apr 14 2016 21:26
anyhow maybe this is the wrong place to ask but what would be your approach to building a large model (~50 fields, nested objects - it's an application form) with the requirement of "auto-saving" incomplete snapshots, and enforcing completeness at a certain point in time :D
Piotr Solnica
@solnic
Apr 14 2016 21:28
In steps?
Simon Schmid
@sled
Apr 14 2016 21:28
nope I always get the complete hash with a lot of nils / empty strings in the beginning;)
and it gets built incrementally then
just at the end I enforce a certain completeness / correctness
if it was a small form I wouldn't bother writing two different validation schemes / types whatever but with ~50 fields it's going to be ugly...
Piotr Solnica
@solnic
Apr 14 2016 21:30
Wait. When does validation happen?
Simon Schmid
@sled
Apr 14 2016 21:30
type correctness (i.e date is a date is always enforced)
but not the completeness
when you're finished an "submit" your application it should have a certain completeness
until now I used virtus with AM::Validation and validation contexts
i.e document.valid?(:draft)
and document.valid?(:submission)
Piotr Solnica
@solnic
Apr 14 2016 21:34
So all fields are always being sent?
Simon Schmid
@sled
Apr 14 2016 21:34
yes
Piotr Solnica
@solnic
Apr 14 2016 21:34
So you want to validate values only if they are not nil
And eventually check if all fields have been filled
Simon Schmid
@sled
Apr 14 2016 21:35
yep everything is optional in the "draft" state, and in the "submit" state I want "certain" fields to be filled (you don't have to fill everything)
Andy Holland
@AMHOL
Apr 14 2016 21:35
You could create a validator for each step, then remove the irrelevant values before passing to each validator
Then for the final step compose a validator from all of the others
Piotr Solnica
@solnic
Apr 14 2016 21:36
We don't have the concept of validation contexts. It feels too high level to be part of schema api. I need to think about that
@AMHOL there are no steps. It auto-saves
Andy Holland
@AMHOL
Apr 14 2016 21:37
Ahh OK
Simon Schmid
@sled
Apr 14 2016 21:37
yep... and steps are kinda ugly.... did that once - binds your UI directly to your data model :/
Andy Holland
@AMHOL
Apr 14 2016 21:38
Would be cool to see the the AM::V implementation
Piotr Solnica
@solnic
Apr 14 2016 21:38
AM::V has context feature
Andy Holland
@AMHOL
Apr 14 2016 21:39
Yeah, but it would be easier to create an equivalent dry-validation validator having seen the AM version
Simon Schmid
@sled
Apr 14 2016 21:39
@AMHOL it's super simple, validates :email, presence: true, on: :submit validates :email, format: /regex/, on: [:submit, :draft]
Piotr Solnica
@solnic
Apr 14 2016 21:41
I need to think about this. I plan to add this feature prior 1.0. Just not sure yet how to approach it
Simon Schmid
@sled
Apr 14 2016 21:42
the bad news is the Type is still full of optionals, but there's almost no way around this :/
except copying the schema vom the "draft" type and kicking out the optional where needed to get a "submitted" type :D
Piotr Solnica
@solnic
Apr 14 2016 21:44
We can do all sorts of things to make it simple but I won't get to it anytime soon
Simon Schmid
@sled
Apr 14 2016 21:48
totally get that, will be using our homebrew solution for now: https://github.com/at-point/porro/blob/master/spec/support/models.rb
but there's a simpler project in the pipeline without rails, might be a case for dry-* :)
Piotr Solnica
@solnic
Apr 14 2016 21:50
Cool :)
Tim Riley
@timriley
Apr 14 2016 22:40
Wow. Those Reddit comments kind of miss the point. Anyway, I didn't want to spend too long or go into specifics trashing Rails, that was just some necessary prelude in my article.
Piotr Solnica
@solnic
Apr 14 2016 22:42
Yep. Everything was said already. Plenty of times
Tim Cooper
@coop
Apr 14 2016 22:43
I enjoyed https://medium.com/@bradurani/improving-your-web-app-with-functional-object-oriented-design-5218f9732b74#.99173gai5 from your post @timriley - I hadn’t read it before. I disagree about the not requiring a domain model but overall I think the concept is solid.
Tim Riley
@timriley
Apr 14 2016 22:50
Cool, glad you found it interesting. I think "domain model" in his case there might reasonably be shorthand for "overburdened activerecord model"
We still use proper entity types with dry-types structs. I suppose you'd call them "domain models" too, but it's very different from rails. Immutable value objects are much nicer :)
Tim Cooper
@coop
Apr 14 2016 22:51
I agree.
https://d262ilb51hltx0.cloudfront.net/max/1600/1*AqGmkew-gO_Odt3iC1h92A.png - I like the idea of a command but I strongly dislike the implementation.
In my mind most of that code should be pushed into Account (or the domain equivilent) because the assertions on state are the account’s concern.
I also think it is weird that he is using a DB transaction to enforce a domain / business rule.
Piotr Solnica
@solnic
Apr 14 2016 22:54
Yeah this is messy
Tim Cooper
@coop
Apr 14 2016 22:59

Here is an example of something similar from the project that I work on:
command:

module Flash
  class ChangeOrderExtraData
    include Cobra::Command

    attribute :order_id, String
    attribute :extra, Hash

    def call
      order = repository.load(Order, order_id)
      order.change_manifest_extra_data(extra)
      repository.save(order)
    rescue Order::CannotChangeIntendedGiftAmount
      context.fail!
    end
  end
end

domain:

module Flash
  class Order
    include Cobra::AggregateRoot

    def change_manifest_extra_data(changes)
      @manifest.assert_singular_product

      if changes.key?("intended_gift_amount")
        raise CannotChangeIntendedGiftAmount
      end

      manifest = @manifest.transform(0) do |component|
        component.update_extra(changes)
      end

      raise_event(:changed_manifest_extra_data, 0, manifest)
    end
  end
end

In my example state validation happens inside the domain.

You can probably tell where I want to introduce dry-types :P
Piotr Solnica
@solnic
Apr 14 2016 23:01
Cobra?
Tim Riley
@timriley
Apr 14 2016 23:01
In our case we still have standalone functional objects for things like that change-manifest-extra-data method
The "operations" are first class things for us, then don't need to reside as methods on domain models
Tim Cooper
@coop
Apr 14 2016 23:02
It is our CQRS Event Sourcing framework - we’ve named all the components after shitty 80s action movies.
Tim Riley
@timriley
Apr 14 2016 23:02
Hehe
Tim Cooper
@coop
Apr 14 2016 23:02
Flash -> Flash Gordon
@timriley do you have a code example of what you’re talking about?
I’d be interested in seeing what you mean.
Tim Riley
@timriley
Apr 14 2016 23:05
Not a huge amount as OSS yet, but https://github.com/icelab/alpinist/blob/master/apps/main/lib/main/operations/update_user.rb may serve as a reasonable example
This is a typical full crud-style update
But even if we wanted to make a more specific change about a user, with its own validation logic etc., I'd make a similar operation class dedicated to that change
Simon Schmid
@sled
Apr 14 2016 23:06
@coop interesting example, how do you handle the notification back to client what went wrong?
Tim Cooper
@coop
Apr 14 2016 23:10
@sled at the moment writes to the event store happen in the request response cycle so we can fairly easly turn an exception into a validation message. That being said we’re looking to implement a command queue to delay processing commands which will increase the number of transactions we can receive per second (we’re building a payments system).
When we delay executing commands I want to enforce command “correctness” with dry-v and return a URL for the end user to poll to find out the result of executing a command.
Simon Schmid
@sled
Apr 14 2016 23:12
I really like the command approach from a code point of view, however I really find it hard to "bubble" up the result to the user's screen ;)
Tim Cooper
@coop
Apr 14 2016 23:12

return a URL for the end user to poll to find out the result of executing a command.

This might sound shitty but I think that enforcing command correctness will catch 99% of issues and mostly we will piss off people who are trying to fuck with our system.

@sled

however I really find it hard to "bubble" up the result to the user's screen

What do you mean?

@timriley I like this for CRUD style apps but I honeslty haven’t written something like that for quite some time but I can see how you’d transition it to something a little more complex.
@timriley given the code that you linked, how would you implement the feature “you’re only allowed to update a user’s details on Tuesdays”?
You’d push it into the command?
Simon Schmid
@sled
Apr 14 2016 23:18
@coop do you also handle privileges / access rights in the command like trail blazer?
Tim Cooper
@coop
Apr 14 2016 23:18
@sled I haven’t seen what trailblazer does (I should have a look) but we want to :)
Simon Schmid
@sled
Apr 14 2016 23:19
also resolving dependent entitites would be an interesting question
Tim Cooper
@coop
Apr 14 2016 23:19
What do you mean?
Simon Schmid
@sled
Apr 14 2016 23:19
like CreateInviteForEventCommand
POST /events/123/invites
Tim Cooper
@coop
Apr 14 2016 23:20
What would be the issue?
Simon Schmid
@sled
Apr 14 2016 23:20
do you lookup the event also inside of the command
or use some sort of injection i.e CreateInviteForEvent.call(FindInvite(params[:event_id]), params[:invite])
Tim Cooper
@coop
Apr 14 2016 23:21
Right, yes we do. On the write side we have a repository that you access, it looks like this:
instance = repository.load(Klass, id)
Tim Riley
@timriley
Apr 14 2016 23:22
@coop in that case I'd put it into the validation schema that we use for that command.
Tim Cooper
@coop
Apr 14 2016 23:22
@timriley right.
Tim Riley
@timriley
Apr 14 2016 23:23
I acknowledge that we're also approaching this from a particular angle/experience - this may certainly not be the best way for everyone.
(or even ourselves, once things reach a certain level of complexity - but at least kings are structured such that it's easy to build something bigger out of what we start with)
Tim Cooper
@coop
Apr 14 2016 23:24

@sled our command pattern is:

  1. load the aggregate by ID
  2. emit the events for your change (which happens via a call to the aggregate)
  3. persist the events back to the event store

From there the events are put on a bus and shipped off to read models.

Tim Riley
@timriley
Apr 14 2016 23:24
S/kings/things
Tim Cooper
@coop
Apr 14 2016 23:27
@timriley that sounds fair. Where do you put validation that relies on the state of the object? e.g. you cannot change the password of users who have closed their account?
update_user.by_id(user_id) I’m guessing you’d change this to not return deactived users?
Tim Riley
@timriley
Apr 14 2016 23:29
That's a good question, @koop! I haven't had to do that yet, but off the top of my mind, I'd either (a) just guard against that explicitly at the top of the operation's call method, or (b) pass some state from the existing user to the validation schema as a dependency or something like that.
Or yes, you sidestep the thing entirely by restricting your dataset. A lot would depend on whether this is something people can legitimately stumble onto via your app's UI or not.
Tim Cooper
@coop
Apr 14 2016 23:33

you sidestep the thing entirely by restricting your dataset

The problem with this is that there is no longer a codified rule that says “you cannot change the password of deactived users”, it just sort of happens because you don’t return that kind of user in your scopes.

A lot would depend on whether this is something people can legitimately stumble onto via your app's UI or not

This is true but it still becomes an implicit rule. It looks like adding it to the operations call would work.

Tim Riley
@timriley
Apr 14 2016 23:34
Right. Good points.
And if I wanted to use that logic in multiple places, I'd extract it into a standalone object and inject it wherever needed :)
Tim Cooper
@coop
Apr 14 2016 23:36
I think what you’re doing is good and I think it’s a lighter approach than what I’ve built and it might be a good approach for some of the other apps that we build - just need to get a good understanding of how you’d approach some of these problems.
Don Morrison
@elskwid
Apr 14 2016 23:37
@timriley we do something similar with our objects. They emerged out of “service” objects and are really now just method objects that provide a way to have a specialized function for an operation.
I envision them being registered in a container so we can combine instances of these things rather then relying on Ruby’s constant lookup.
Andy Holland
@AMHOL
Apr 14 2016 23:39
dry-v hit 300 stars BTW
Is there a release planned for dry-web BTW @timriley ?
Simon Schmid
@sled
Apr 14 2016 23:41
module MyConcept
  class DoSomethingCommand
    incldue Fancy::Command

    arguments do
      key(:user_id).required
      key(:name).required
      key(:email).required(format?: EMAIL_REGEX)
    end

    dependencies do
      resolve(:user).from(Repository).by(:id => arguments[:user_id]).required
    end

    policy do 
      user.can_execute_xy?
    end

    def call
      # do something fancy
      context.success!(result)
    end
  end
end
Andy Holland
@AMHOL
Apr 14 2016 23:42
Would be nice to have a bit more of a readme for dry-web-skeleton too, I can't figure out what's going on with it at the moment
Tim Riley
@timriley
Apr 14 2016 23:42
@AMHOL we could probably release a version as-is, since rodakase was previously released too.
Andy Holland
@AMHOL
Apr 14 2016 23:42
That would be cool
Tim Riley
@timriley
Apr 14 2016 23:42
@AMHOL yes! I'm currently updating it to our latest practices, then I'll document it :)
Andy Holland
@AMHOL
Apr 14 2016 23:42
Nice :)
Looking forward to poking around with it :D
Tim Riley
@timriley
Apr 14 2016 23:44
@solnic you ok for me to push a dry-web gem release sometime?
Andy Holland
@AMHOL
Apr 14 2016 23:46
He'll be asleep now @timriley
00:46 here, so 01:46 there
Tim Riley
@timriley
Apr 14 2016 23:46
Yeah, he can answer whenever.
Andy Holland
@AMHOL
Apr 14 2016 23:47
:p
Simon Schmid
@sled
Apr 14 2016 23:52
@coop this is what I'm working towards with dry-*: https://gist.github.com/sled/bb63022fac9b78ed96e5b86e667acfd7
Tim Cooper
@coop
Apr 14 2016 23:53
That’s kind of ool.
Simon Schmid
@sled
Apr 14 2016 23:58
yep dry-* covers a lot, dependency resolver I'm still working on ;)
Andy Holland
@AMHOL
Apr 14 2016 23:59
@sled you should take a look at @timriley's dry-transaction