These are chat archives for dry-rb/chat

13th
Apr 2016
John Backus
@backus
Apr 13 2016 00:08
Also am I missing something here or is this a bug?
require 'dry/validation'
require 'dry/validation/version'

Dry::Validation::VERSION # => "0.7.4"

schema1 = Dry::Validation.Schema do
  key(:thing).required(:unique?)

  configure do
    config.messages_file = 'custom_errors.yml'

    def unique?(array)
      array.uniq.size == array.size
    end
  end
end

schema2 = Dry::Validation.Schema do
  key(:thing).required(:unique?)

  key(:thing).each do
    inclusion?(%w[foo bar])
  end

  configure do
    config.messages_file = 'custom_errors.yml'

    def unique?(array)
      array.uniq.size == array.size
    end
  end
end

bad_value      = %w[foo wtf bar]
repeated_value = %w[foo bar foo]

schema1.call(thing: bad_value).messages[:thing]      # => nil
schema1.call(thing: repeated_value).messages[:thing] # => ["must not repeat values"]

schema2.call(thing: bad_value).messages[:thing]      # => {1=>["must be one of: foo, bar"]}
schema2.call(thing: repeated_value).messages[:thing] # => nil
only difference between schema1 and schema2 is the added inclusion? rule within each
Benjamin Klotz
@tak1n
Apr 13 2016 06:48
@backus I had such similar cases too, but was not sure if I made smth wrong or not
Piotr Solnica
@solnic
Apr 13 2016 07:22
@backus specifying same key twice will override other rules
Each applies rules to each value, so the way you wrote it is not what you want
Oh wait, it is. Sorry (in pre coffee mode)
John Backus
@backus
Apr 13 2016 07:51
What would that look like then @solnic? Like so?
key(:thing).required(:unique?).each do
  inclusion?(%w[foo bar])
end
Piotr Solnica
@solnic
Apr 13 2016 08:36
this will work in next version, atm I don’t think this is supported
required(:unique?) returns Schema::Rule object which doesn’t support each, IIRC
@backus btw key(:thing).each(inclusion: …) is better, use blocks only when you want ORs
ORs or schemas*
Tim Riley
@timriley
Apr 13 2016 10:01
@solnic something I'd like to do in the next little while is change dry-view Layout's "root" config into an array of paths, so we can package up gems with view templates (like our paginator) and have them available to use within dry-web apps. Does that seem good with you, in principle?
I think you'd still configure the paths yourself manually, for each of your layout classes. That way you can be explicit about which gems' view paths you care to include (and also it means we avoid global state).
Piotr Solnica
@solnic
Apr 13 2016 10:23
@timriley yes sounds good
Tim Riley
@timriley
Apr 13 2016 10:23
Great :)
I want to start avoiding copy/paste code between our apps in situations where it's clear some things can be gem-ified.
Tim Cooper
@coop
Apr 13 2016 11:32

@solnic have you thought about supporting unknown keys with dry-v? This is the JSON payload of one of the APIs that we don’t own:

{
  "RpYCgWVGkNyRk3JsPUj9Hzp_CLedN5xM": {
    "name": “Bedroom"
  },
  # … more hashes ...
}

If we were in control of the API I’d move the id into the payload and just have an array but unfortunately it is supplied by a third party.

Tim Riley
@timriley
Apr 13 2016 11:33
Not answering your question directly, but you could make it work by putting a mapper in front to rearrange those into a normalised form?
Also, what an... interesting API structure
:wink:
Tim Cooper
@coop
Apr 13 2016 11:35
Good call @timriley.
Piotr Solnica
@solnic
Apr 13 2016 11:35
yeah I would map it
stigchristian
@stigchristian
Apr 13 2016 14:44
Hi, how do I validate a nested array of hashes (I want to validate the hash keys/values as well). Something like:
key(:people).each(AddressSchema)
Oh, never mind, found out:
    key(:people).each do
      key(:address).required(:string?)
      key(:city).required(:string?)
    end
Piotr Solnica
@solnic
Apr 13 2016 14:52
@stigchristian please wrap those two keys in a schema block
nested keys are going away in 0.8.0
where did you find that example?
stigchristian
@stigchristian
Apr 13 2016 14:52
great, thanks.
Just tested, no example
    key(:people).each do
      schema do
        key(:address).required(:string?)
        key(:city).required(:string?)
      end
    end
Piotr Solnica
@solnic
Apr 13 2016 14:54
just schema do .. end
stigchristian
@stigchristian
Apr 13 2016 14:56
?
Piotr Solnica
@solnic
Apr 13 2016 14:56
you wrote schema.do but now I can see you fixed it :)
stigchristian
@stigchristian
Apr 13 2016 14:57
:thumbsup:
that’s where rom/dry ecosystems are going…
Benjamin Klotz
@tak1n
Apr 13 2016 16:33
@solinc :+1: * 1000
Simon Schmid
@sled
Apr 13 2016 19:04
a philiosophical question, shouldn't coercion be separate from a type? :)
Nikita Shilnikov
@flash-gordon
Apr 13 2016 19:09
@sled you have strict types so any coercions are just additions to them. And coercions are proudly named "type constructors" :)
Simon Schmid
@sled
Apr 13 2016 19:09
yeah but I always get the feeling that coercion is related to a specific "format"
it's like the coercer should know how to convert from "input" to requested "type" and the type should just check whether you're assigning a valid type and throw a TypeError if not
there's like two possibilities, passing a coercer in as dependency and in the type constructor do something like coercer.to_date(....) or do it "in-front" of everything e.g XYCoercer(schema, input, ... other options) => Schema Instance
Simon Schmid
@sled
Apr 13 2016 19:15
(just thinking out loud here)
Simon Schmid
@sled
Apr 13 2016 19:20
or like MySchema.new(XYCorcer.coerce(input, MySchema))
Nikita Shilnikov
@flash-gordon
Apr 13 2016 19:42
actually you can build it on top of dry-t
it's really easy
Simon Schmid
@sled
Apr 13 2016 19:44
so basically my coercer would inspect the schema and try to parse the input to the schema
Nikita Shilnikov
@flash-gordon
Apr 13 2016 19:45
yep, something like that
Andy Holland
@AMHOL
Apr 13 2016 20:00
@sled IMO it should be a separate concern, I don't think I'll use structs myself
Simon Schmid
@sled
Apr 13 2016 20:01
@AMHOL what do you use then, just out of interest?
Andy Holland
@AMHOL
Apr 13 2016 20:02
Well, we use Rails at work lol
Simon Schmid
@sled
Apr 13 2016 20:02
me too ;)
Andy Holland
@AMHOL
Apr 13 2016 20:03
But I'd probably go for dry-web with transactions containing something like coercion >> validation >> mapping >> business logic/persistence etc.
Coercion using Dry::Types::Hash
Simon Schmid
@sled
Apr 13 2016 20:04
what I try to do is to use dry-types as enforcer
once the data is dryed-up, I just use representers to get it to the format I need
Andy Holland
@AMHOL
Apr 13 2016 20:05
So you just use strict types?
Simon Schmid
@sled
Apr 13 2016 20:05
(form builder, ...)
yep...
Andy Holland
@AMHOL
Apr 13 2016 20:05
Ahh, OK, so you're using trailblazer
Simon Schmid
@sled
Apr 13 2016 20:05
nope :D
my co-workers would kill me :P
but I really like trailblazer's approach
Andy Holland
@AMHOL
Apr 13 2016 20:06
haha how come?
Simon Schmid
@sled
Apr 13 2016 20:06
it's completely different way of thinking, replacing a lot of stuff ;)
also not a huge fan of adding too much dependencies
gotta do it step by step, making our service objects cleaner with dry-rb
also dry-rb has (almost) no external dependencies which is nice
Andy Holland
@AMHOL
Apr 13 2016 20:11
Yeah, that was a conscious effort from the start
Everything in Ruby introduces too much pollution
Simon Schmid
@sled
Apr 13 2016 20:16
with dry-rb I can have a look at one file and I know "ok this will always be a date object"
reduces a lot of "no method errors" in view helpers etc.
dry-types are basically the "working" object for our custom business logic
writing adapters / representers to make it rails compatible is unfortunately a necessary evil
Piotr Solnica
@solnic
Apr 13 2016 20:21
coercion is so strictly dependant on schema definition that keeping it separate would basically make people implement what dry-v is doing all over again
coercion is a separate step, that happens before applying rules
nobody is forcing you to use it like that, just define a plain schema without any input processor
but then you’re gonna have a separate coercion step, using your own code, that will do exactly the same thing
Simon Schmid
@sled
Apr 13 2016 20:24
yep what I started to do is customizing the input processor, mainly because the date format varies quite a lot
Piotr Solnica
@solnic
Apr 13 2016 20:25
also, re types vs coercion, it just works very well with ruby, just like you have Integer() constructor or Hash() etc. I think the way it works is actually quite idiomatic
uhm, just define a custom type that supports other formats
but I’d be interested to hear what you need, I suspect we should extend current behavior with more formats
Simon Schmid
@sled
Apr 13 2016 20:26
I just think there should be an easy way to customize the input processor
because the type is still a <#Date ...
input format is often dependent on locale, or even what country the user selected for example
or even bastardized stuff like rails date_select_tag with params['date_(1i)']
Piotr Solnica
@solnic
Apr 13 2016 20:29
I god, I forgot about this horror
Simon Schmid
@sled
Apr 13 2016 20:30
it's not the job of dry-types to handle this, but providing an easy way to extend/customize a "naive" input processor ;)
Piotr Solnica
@solnic
Apr 13 2016 20:30
actually, I think it is
dry-types are meant to be used for coercion too
they encapsulate things in a really nice way
are small and reusable, I don’t see any reason for a different abstraction
couldn't really trace it how it does the date conversion
Piotr Solnica
@solnic
Apr 13 2016 20:33
yeah inspection should be nicer
Simon Schmid
@sled
Apr 13 2016 20:33
ah never mind, didn't see it extends the basic coercions
Piotr Solnica
@solnic
Apr 13 2016 20:33
irb(main):002:0> Types::Form::Date.fn
=> #Method: Module(Dry::Types::Coercions)#to_date>
constructor types simply has a constructor fn, a proc/method obj/block/whatever responding to #call
so the logic is external to the type…a constructor type is definition + constructor fn
Simon Schmid
@sled
Apr 13 2016 20:38
would be cool if all types were like this :)
and outsource the coercion completely to the input processor
Piotr Solnica
@solnic
Apr 13 2016 20:38
you can create it just like that CreepyRailsDate = Types::Date.constructor { |value| .. }
aren't those all more or less the same types?
just depending on the input
Piotr Solnica
@solnic
Apr 13 2016 20:44
no, coercion logic is different
Simon Schmid
@sled
Apr 13 2016 20:45
but after coercion they're equivalent
Piotr Solnica
@solnic
Apr 13 2016 20:46
Yes. Their primitive definition is the same
Simon Schmid
@sled
Apr 13 2016 20:50
@solnic ah maybe it's a missunderstanding, if I configure the input_processor as json will it then use Types::JSON::Date automatically?
Simon Schmid
@sled
Apr 13 2016 21:02
I think I mixed up dry-validation with dry-types ;)
Piotr Solnica
@solnic
Apr 13 2016 21:14
@sled yes it will
And if you pass a type with a custom constructor to key then it will use that
Simon Schmid
@sled
Apr 13 2016 21:15
@solnic so the schema actually is some sort of input processor too for the types
Piotr Solnica
@solnic
Apr 13 2016 21:15
It uses an input processor object, which is using dry types hash type with member types
But it can be anything that responds to call
Simon Schmid
@sled
Apr 13 2016 21:16
and the output can be fed into a type
which then doesn't have to care about whether it stems from JSON/Form/....
@solnic something like ^ this would be cool
Piotr Solnica
@solnic
Apr 13 2016 21:25
That is sth we can easily have
Would you mind reporting an issue about this?
Simon Schmid
@sled
Apr 13 2016 21:25
I can create one
with this we don't have to touch the schema or type
and can import from any format by providing a suitable input processor
Piotr Solnica
@solnic
Apr 13 2016 21:29
BTW i'm planning to build a pure data mapper on top of dry types and dry-logic. I don't like rom-mapper after all
Don Morrison
@elskwid
Apr 13 2016 21:29
That’s what we’ve been waiting to hear :wink:
Piotr Solnica
@solnic
Apr 13 2016 21:30
:smile:
Don Morrison
@elskwid
Apr 13 2016 21:32
You’ve got some very different building blocks now. Should be fun!
Simon Schmid
@sled
Apr 13 2016 21:32
yes, dry-rb is a lot of fun for doing business logic :)
but still fighting to make it quack like rails wants it to
Don Morrison
@elskwid
Apr 13 2016 21:36
Just rub some ActiveSupport on it
Simon Schmid
@sled
Apr 13 2016 21:36
did you get it working with form builder out of the box? :)
Piotr Solnica
@solnic
Apr 13 2016 21:39
nothing works with rails OOTB
Nikita Shilnikov
@flash-gordon
Apr 13 2016 21:45
too true
Piotr Solnica
@solnic
Apr 13 2016 21:46
yeah that wasn’t a snark comment, this is the truth :worried:
Simon Schmid
@sled
Apr 13 2016 21:48
it's like ActionPack/View <-> dry-rb <-> ActiveRecord at the moment
Nikita Shilnikov
@flash-gordon
Apr 13 2016 21:49
I believe hot code reloading is the main problem
Simon Schmid
@sled
Apr 13 2016 21:50
@flash-gordon I had partial success by just removing my custom types from the container
Nikita Shilnikov
@flash-gordon
Apr 13 2016 21:54
Preloading all constants before rails started will save you a lot of time. The more you avoid AR::Dependencies, the less troubles you get
It kills hot reloading but I personally prefer to run specs
Simon Schmid
@sled
Apr 13 2016 21:56
hot reloading is a nice plus though ;)
Nikita Shilnikov
@flash-gordon
Apr 13 2016 21:56
I partially agree. But the it is implemented in Rails is just a mess
you cannot just create and remove constants in Ruby pretending it is OK
it is not OK. at all
Simon Schmid
@sled
Apr 13 2016 21:59
not sure whether one can reload within the ruby VM itself
Piotr Solnica
@solnic
Apr 13 2016 22:03
Ruby doesn't support code reloading reliably
AS::Deps is a hack with bugs and awkward limitations
I repeat this like a broken record oh wel
Nikita Shilnikov
@flash-gordon
Apr 13 2016 22:06
There is shotgun gem but I didn't try it
using fork is much safer though
Piotr Solnica
@solnic
Apr 13 2016 22:07
I use it
Simon Schmid
@sled
Apr 13 2016 22:08
AS deps just doesn't like dry-container :/
or to put it another way it tries to register multiple times
I got it working by allowing to replace and existing registration
Nikita Shilnikov
@flash-gordon
Apr 13 2016 22:08
@solnic does it work nice?
Piotr Solnica
@solnic
Apr 13 2016 22:09
Yes
Nikita Shilnikov
@flash-gordon
Apr 13 2016 22:11
cool. Will try it someday :)
@sled so it's weird. You also can teach AS::D to "unregister" components. Though it would be weird as well
Simon Schmid
@sled
Apr 13 2016 22:17
@flash-gordon it's the dry-container which causes the trouble, because when a struct/type gets reloaded it tries to register it again in the dry-container
which causes an error
Nikita Shilnikov
@flash-gordon
Apr 13 2016 22:23
sure. I understand. As I said I stopped to fight against AR::D so I don't have a solution atm. Probably the only way is to hack the container
And any "solution" that is supposed to seamlessly integrate dry-stuff with rails (dry-rails lol) will be a bunch of weird unreliable hacks
Piotr Solnica
@solnic
Apr 13 2016 22:28
True
There is a separate trick to get Spring to work as well.
Simon Schmid
@sled
Apr 13 2016 22:29
@coop this won't work reliable since types get registered on "class definition"
so if you kill the registry you would have to "reload / redefine" all types
Tim Cooper
@coop
Apr 13 2016 22:30
That makes sense. If you put all your types in app/types we could reload that directory too.
Simon Schmid
@sled
Apr 13 2016 22:30
because only the classes you changed will get reloaded/registered by rails
Tim Cooper
@coop
Apr 13 2016 22:31
I believe I’ve done something like that in the past.
Simon Schmid
@sled
Apr 13 2016 22:32
yep if you can get rails to force-reload all your types - the problem is solved
Andy Holland
@AMHOL
Apr 13 2016 22:32
Anyone managed to get an isolated reproduction of this?
Tim Cooper
@coop
Apr 13 2016 22:32
  class Railtie < ::Rails::Railtie
    config.to_prepare { Railtie.setup! }

    def setup!
      return if Rails.configuration.eager_load

      Cobra.config.reset

      paths = Cobra.paths.flat_map do |path|
        config.cobra.directories.flat_map do |directory|
          Dir[path.join(directory, *config.cobra.pattern)]
        end
      end

      paths.each { |path| require_dependency(path) }
    end
  end
This is what I’ve done previously.
Simon Schmid
@sled
Apr 13 2016 22:32
I'll give it a shot right away ;)
@AMHOL create a new rails project, include the gem, create a type, rails c, play with your type, edit & save it, reload!
Tim Cooper
@coop
Apr 13 2016 22:33
@sled https://gist.github.com/coop/e4e13b850fcf0440f711d7c5f78d2521 - this is the full implementation.
Andy Holland
@AMHOL
Apr 13 2016 22:34
What Rails version? That didn't work for me last time
Simon Schmid
@sled
Apr 13 2016 22:44
4.2.5
Simon Schmid
@sled
Apr 13 2016 22:54
mh nope doesn't work it seems there's a global registry
it survives the reload ;)
So that works for me.
Andy Holland
@AMHOL
Apr 13 2016 22:57
TY
Tim Cooper
@coop
Apr 13 2016 22:57
Setup the module in an initializer and then define types in app/types/types (app/models/types would probably be less weird).
You need /types for rails constant lookup.
Andy Holland
@AMHOL
Apr 13 2016 22:58
So it fails when you use app/models/types?
with this I can reproduce the error in your example
Tim Cooper
@coop
Apr 13 2016 23:02
I haven't tried app/models/types it just looks more "normal" than app/types/types.
Thanks @sled I will have a look later. On my way to work now.
Andy Holland
@AMHOL
Apr 13 2016 23:04
Yeah, looks like it's just inheriting struct
As that automatically registers the type
Simon Schmid
@sled
Apr 13 2016 23:10
yep that's the only problem afaik
Piotr Solnica
@solnic
Apr 13 2016 23:23
just disable code reloading and stupid spring and use shotgun + rackup :)
Tim Cooper
@coop
Apr 13 2016 23:44

use shotgun + rackup

In a rails app? I wonder if that would work.