These are chat archives for dry-rb/chat

13th
Jul 2016
Nikita Shilnikov
@flash-gordon
Jul 13 2016 09:49
@solnic re Form -> Params it's a good idea fwiw, I'm not sure if we should rename it or just add an alias
Piotr Solnica
@solnic
Jul 13 2016 09:52
yeah thinking about an alias now
ie I don’t want to see people thinking that Form is a form object of some sort
becuase it’s a schema
Nikita Shilnikov
@flash-gordon
Jul 13 2016 09:58
I also have an API that uses URL encoded parameters ie passes values via Rack params but this is not a form
Piotr Solnica
@solnic
Jul 13 2016 10:03
exactly
Don Morrison
@elskwid
Jul 13 2016 10:04
What about confusion that this is a params object of some sort? Similar to the confusion with it being a form object...
Although, I prefer params to form fwiw
Piotr Solnica
@solnic
Jul 13 2016 10:11
welllll, technically speaking we could completely ditch subclasses as it’s actually just a configuration thingie
so ie you could define a schema class, call it however you like and configure it for your use case
we have some ideas how to leverage these built-in subclasses though ie custom message namespace for Form and JSON
but even that will be just configuration
Piotr Solnica
@solnic
Jul 13 2016 11:06
I’m running mutant against trickiest parts of dry-v and seeing 100% coverage! :)
update: no longer 100% :laughing:
Eger Andreas
@andreaseger
Jul 13 2016 11:30

not sure this the right place to ask, but I don't know if I'm using it wrong or if I found a bug: https://gist.github.com/andreaseger/3125a44ba68407fc4fd988972dc6609c

For one I'm not totally sure I'm using these custom types via constructor correctly - documentation is a bit non-existing on that part ;) I guess the block param is the input and the output is passed to the Type I made the constructor on, right?

I seem to not be able to use these kind of custom types in dry-validation as demonstrated in the gist. So am I doing it wrong or is something wrong with the gems?

Piotr Solnica
@solnic
Jul 13 2016 11:34
@andreaseger try with a strict type otherwise type-check won’t be applied so ie PrefixedString = Strict::String.constructor { |val| “foo_#{val}”}
this feature is still experimental so we haven’t documented it yet
Eger Andreas
@andreaseger
Jul 13 2016 11:39
ok that works, that kind of leads me to a followup question. Is there a way to add a custom type for something that hasn't a strict type in dry-types. i.e. I want to coerce a string into a regex
Piotr Solnica
@solnic
Jul 13 2016 11:40
hmm lemme see
Piotr Solnica
@solnic
Jul 13 2016 11:53
@andreaseger doesn’t seem to possible at the moment, because we have a string on the input, and regexp on the output, I don’t recall if I made that possible…I’ll dig into it and get back to you
Eger Andreas
@andreaseger
Jul 13 2016 11:56
From what I can see it's mostly because there is no regex type in dry-types. I already have a working custom type where the input is an Integer and the output a Time object
Piotr Solnica
@solnic
Jul 13 2016 11:57
wait, I’m silly
it works:
require 'dry-validation'

module Types
  include Dry::Types.module

  Regexp = Dry::Types::Definition
    .new(::Regexp)
    .constrained(type: ::Regexp)
    .constructor(::Regexp.method(:new))
end

schema = Dry::Validation.Form do
  required(:regex_pattern).filled(Types::Regexp)
end

puts schema.(regex_pattern: nil).inspect
#<Dry::Validation::Result output={:regex_pattern=>nil} messages={:regex_pattern=>["must be filled", "must be Regexp"]}>

puts schema.(regex_pattern: 1).inspect
#<Dry::Validation::Result output={:regex_pattern=>1} messages={:regex_pattern=>["must be Regexp"]}>

puts schema.(regex_pattern: ‘\d').inspect
#<Dry::Validation::Result output={:regex_pattern=>/\d/} messages={}>
@andreaseger ^^
Eger Andreas
@andreaseger
Jul 13 2016 11:59
Ah so I define the Regex type myself
thanks
Piotr Solnica
@solnic
Jul 13 2016 12:00
yeah, coercion is safe so TypeErrors are rescued and then a type check is performed
so ie when your Regexp type failed to coerce, dry-v will apply type?(Regexp) and see it’s not valid and you’ll get an error message in dry-v result
Piotr Solnica
@solnic
Jul 13 2016 12:07
if you want you can register Regexp coercible type with a single line: Dry::Types.register_class(Regexp) and it’ll show up as Types::Regexp but it’s not a constrained type so you’d still have to do Types::Regexp.constrained(type: Regexp) if you want to reuse it in schemas and have predicates auto-applied
Eger Andreas
@andreaseger
Jul 13 2016 12:10
For now I probably just add your full regexp definition to my types module, that way I can reuse it everywhere I want and be safe with it
Brad Robertson
@bradrobertson
Jul 13 2016 13:40
I have a question about the design decision to make Dry::Types::Struct an abstract class rather than a mixin. My business domain seems like the exact case where I'd want to control the taxonomy of the system, but forcing me to inherit from Dry::Types::Struct means I no longer can. Any reason this isn't a module I can include instead?
Kiril Dokh
@dsounded
Jul 13 2016 13:43
@solnic Hello, mate, will 0.8.1 be introduced on Friday ?
Piotr Solnica
@solnic
Jul 13 2016 13:44
@dsounded which gem?
Kiril Dokh
@dsounded
Jul 13 2016 13:45
@solnic dry-validation
Piotr Solnica
@solnic
Jul 13 2016 13:46
@dsounded oh it’s already 0.9.2
:laughing:
Kiril Dokh
@dsounded
Jul 13 2016 13:53

Oh, cause you’ve just said about this Friday :)

Could you please tell if it can work with dynamic stuff and has all options in message ?

@solnic ^
Piotr Solnica
@solnic
Jul 13 2016 13:54
Kiril Dokh
@dsounded
Jul 13 2016 13:55
@solnic Hmm, nice, will try it out, thanks
Piotr Solnica
@solnic
Jul 13 2016 13:55
cool, lemme know if it works for you
Eger Andreas
@andreaseger
Jul 13 2016 14:40

maybe I'm trying to do to much at once but given this input {"name" => "male,female,bla"}
I'd like to do 2 things within a dry-validation form validation/coersion:

  1. coerce the string into an array of string by splitting on ,
    -> easy something like this ArrayFromString = Types::Strict::Array.contructor{|val| val.is_a?(::Array) ? val.dup : val.to_s.split(",")}
  2. validate each array member against an enum -> Gender = Types::Strict::String.enum("male","female")

is this something that should be doable? (Specific usecase is jsonapi filter parameter)

I basically can't find a way to combine these 2 steps
Piotr Solnica
@solnic
Jul 13 2016 15:13
@andreaseger it is possible with type_specs enabled
require 'dry-validation'

module Types
  include Dry::Types.module

  Gender = Strict::String.enum('male', 'female')

  ArrayFromString = Strict::Array.constructor { |v|
    v.is_a?(::String) ? v.split(',') : v
  }
end

schema = Dry::Validation.Form do
  configure do
    config.type_specs = true
  end

  required(:name, Types::ArrayFromString).each(Types::Gender)
end

puts schema.(name: ‘’).inspect
#<Dry::Validation::Result output={:name=>[]} messages={}>

puts schema.(name: 1).inspect
# #<Dry::Validation::Result output={:name=>1} messages={:name=>[“must be an array”]}>

puts schema.(name: ‘male,oops,female’).inspect
# #<Dry::Validation::Result output={:name=>[“male”, "oops", "female"]} messages={:name=>{1=>["must be one of: male, female"]}}>

puts schema.(name: ‘male,female’).inspect
# #<Dry::Validation::Result output={:name=>[“male", "female"]} messages={}>
dammit I fail at editing this
there we go
Piotr Solnica
@solnic
Jul 13 2016 15:19
@andreaseger more info about type-specs here: http://dry-rb.org/gems/dry-validation/type-specs/
this is gonna be the way in 1.0.0
Eger Andreas
@andreaseger
Jul 13 2016 15:21
cool, any downside of using this already?
Piotr Solnica
@solnic
Jul 13 2016 15:23
DSL will look a bit more verbose
ie required(:age, [:nil, :int]).maybe(:int?)
or required(:age, MaybeAge).maybe(:int?)
it’s because things have been separated so the outcome is that you gotta a) provide a type spec b) provide validation rules
because types may fail to coerce
and it’s not that trivial to just infer validations from types in many cases, so we gotta think more how to approach it
of course common stuff is trivial
Eger Andreas
@andreaseger
Jul 13 2016 15:27
the docu you linked says this is way faster but only for defining the schema right? so if I define a schema once and keep it around in a constant this won't matter
Piotr Solnica
@solnic
Jul 13 2016 15:28
yes it won’t matter
Eger Andreas
@andreaseger
Jul 13 2016 15:28
also think splitting or making it more explicit that validation and coercion are separate things is a good idea
Piotr Solnica
@solnic
Jul 13 2016 15:29
but we had folks with a 300+ keys defined and it was taking ~5 seconds
it is a good idea yeah :)
Stefan
@StefanWiechula
Jul 13 2016 17:56
http://dry-rb.org/gems/dry-types/hash-schemas/ looks like you can't express/allow a variable (unknown at type definition time) number of items. How would I go about defining a hash type where the keys are all one type and the values another but there could be any number of key/value pairs?
...and hi, all :-)
Piotr Solnica
@solnic
Jul 13 2016 17:59
@StefanWiechula hey there :) that’s not supported, hash schemas are meant to be used as…schemas :)
so you gotta define all valid keys
what’s your use-case?
Stefan
@StefanWiechula
Jul 13 2016 18:01
I've got a mapping from id numbers to hashes that describe the object-with-that-id. The object description hashes all conform to a strict schema. The id numbers are all integers. But in the mapping there could be any number of objects.
(This mapping gets serialized to JSON and passed around between services, stored in a database and recalled later, etc.)
Piotr Solnica
@solnic
Jul 13 2016 18:02
pls report an issue describing this, some example code of what you’d like to achieve will be useful
and we can take it from there
Stefan
@StefanWiechula
Jul 13 2016 18:02
Sure, will do.
Aside, here's a schema definition tool that supports this use case (albeit in another language) https://pypi.python.org/pypi/dataschema
Stefan
@StefanWiechula
Jul 13 2016 18:25
dry-rb/dry-types#113
Piotr Solnica
@solnic
Jul 13 2016 18:31
thanks
Stefan
@StefanWiechula
Jul 13 2016 18:33
Is there a "don't care" schema in dry-types? Then for the short term could I put a "don't care" type in place of that entire hash and validate it with a bit of manual code after dry-types is done with it.
Piotr Solnica
@solnic
Jul 13 2016 18:37
use dry-validation if you want to validate
dry-types is for things where you actually do care :D
Stefan
@StefanWiechula
Jul 13 2016 18:51
Well fair enough, and I suppose in our case we only need validation, not coercion etc. so could switch to dry-validation. I can't see how it supports the use case above and in #113 though. I'd need something like http://dry-rb.org/gems/dry-validation/basics/macros/#each but for hashes not arrays.

By the way (new issue) is this a bug?

[2] pry(main)> Payload = Types::Strict::Hash.strict( [2] pry(main)* location: Types::String, [2] pry(main)* parameters: Types::Array.member( [2] pry(main)* Types::Hash.strict( [2] pry(main)* foo: Types::String, [2] pry(main)* bar: Types::Bool [2] pry(main)* ) [2] pry(main)* ) [2] pry(main)* ) ... [11] pry(main)> Payload[{location: 'here', parameters: [{foo: 'Foo', bar: false}]}] => {:location=>"here", :parameters=>[{:foo=>"Foo", :bar=>false}]} ... [13] pry(main)> Payload[{location: 'here', parameters: [{bar: false}]}] Dry::Types::SchemaKeyError: :parameters is missing in Hash input

(ew, not quit Github flavoured markdown)
Anyhow, the error message on the last line is misleading. It's not that :parameters is missing. It's there but has the wrong type.
John Backus
@backus
Jul 13 2016 18:56

@backus pls report an issue. This needs some discussion

Done dry-rb/dry-validation#213

@solnic ^
Piotr Solnica
@solnic
Jul 13 2016 19:42
@StefanWiechula yeah struct error handling is not-so-great atm
@StefanWiechula re validation, maybe this will be helpful dry-rb/dry-validation#206
Rafael George
@cored
Jul 13 2016 20:31
I want to start a new project with dry-web
should I just install the gem and go from there?
Stefan
@StefanWiechula
Jul 13 2016 20:50
I've got a problem with sum types now and it looks like a bug. The example here works for me http://dry-rb.org/gems/dry-types/sum/ but if I try to sum two hash types it seems to only validate against the first one.

E.g.

h1 = Types::Hash.strict(foo: Types::Strict::String)
h2 = Types::Hash.strict(bar: Types::Strict::String)

h1[{foo: 'asdf'}] # OK
h2[{bar: 'asdf'}] # OK
h1[{bar: 'asdf'}] # Fails, good!

h1_or_h2 = h1 | h2
h1_or_h2[{foo: 'asdf'}] # OK
h1_or_h2[{bar: 'asdf'}] # Fails, bad!

Piotr Solnica
@solnic
Jul 13 2016 20:53
@StefanWiechula pls report
Rafael George
@cored
Jul 13 2016 21:04
@solnic I'm trying to test drive a feature with dry-web; I added a new feature story. And every step is a new operation in trailblazer jargon or an interactor in hanami jargo. I confronted a problem and is the fact that I'm not attached to AR by any means; my first try on testing let's say the CreateCategory interactor is to pass a category_name and then assert against the repository
don't know if there's a better way
Stefan
@StefanWiechula
Jul 13 2016 21:08
@solnic dry-rb/dry-types#114
Stefan
@StefanWiechula
Jul 13 2016 21:28
Path to the error would be nice, for nested types.
I guess that's a feature request :-)
Brent Jubinville
@utilityboy
Jul 13 2016 21:56
Hi all. Is there a way to create a constrained "Maybe"? e.g I have a type that checks for a UUID:
UUID = Types::Strict::String.constrained(format: /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/)
but would like to also allow it to be nil... I thought possibly something like
MaybeUUID = Types::Maybe::Strict::String.constrained(format: /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/)
Tim Riley
@timriley
Jul 13 2016 22:09
@utilityboy I think maybe types are always going to come back to you as Some() or None, but you could use an “optional” type, which allows nil:
t = Types::Strict::String.constrained(format: /foo/).optional

t[“foo”] # => “foo”
t[“bar”] # => Dry::Types::ConstraintError
t[nil] # => nil
@cored is that testing really any different under dry-web than it would be anywhere else?
Brent Jubinville
@utilityboy
Jul 13 2016 22:14
That @timriley I'll give that a run
@timriley that does satisfy the ability to set the value to nil, but at a schema level on the struct I still get something like:
#<Dry::Types::StructError: [TestIt.new] :uuid is missing in Hash input>
I'm trying to Maybe that attribute in the definition of the Struct
Brent Jubinville
@utilityboy
Jul 13 2016 22:19
I phrased my initial question incorrectly I think. Sorry bout that.
Tim Riley
@timriley
Jul 13 2016 22:25
So you’re passing uuid: nil in the hash?
Brent Jubinville
@utilityboy
Jul 13 2016 22:26
I'm trying to avoid have to pass uuid at all to the hash
e.g.
MyStruct.new({}) # valid
MyStruct.new({uuid: SecureRandom.uuid}) # valid
MyStruct.new({uuid: 'kaboom'}) # not so valid
Tim Riley
@timriley
Jul 13 2016 22:28
oh
maybe you want this:
class MyStruct < Dry::Types::Struct
  constructor_type :weak
  attribute :foo, Types::Strict::String
  attribute :bar, Types::Strict::String
end

my = MyStruct.new(foo: "hi")
# => #<Str foo="hi" bar=nil>
Piotr Solnica
@solnic
Jul 13 2016 22:42
Struct constructors still need more work. We're extracting this api to dry-struct where it'll be improved
In general I don't recommend using complex structs where they fill in missing data etc. better to prepare valid hash prior sending it to struct constructor
Structs are meant to be used as simple data objects...not something that generates data
Brent Jubinville
@utilityboy
Jul 13 2016 22:47
I follow. In general this suits my needs very well (I've base-classed domain events and commands with Structs). There are just a couple attributes that are optional in some cases. This UUID is a correlation ID and most of the time, it is not required.
Thanks to you both for the help.
Tim Riley
@timriley
Jul 13 2016 22:48
When I need some special-casing of input handling for a struct, I make a custom .new and prepare the attrs before sending to super
Brent Jubinville
@utilityboy
Jul 13 2016 22:49
@timriley I do that as well actually. In this case however, the struct is valid with or without a UUID
Tim Riley
@timriley
Jul 13 2016 22:49
right, fair enough :)
Brent Jubinville
@utilityboy
Jul 13 2016 22:52
Actually, as to not confuse things, I do something like this:
module Event
  class Base < Dry::Types::Struct
    class << self
      def build(*args)
        # weak schema, which sets defaults
        event_schema = Types::Hash.symbolized(
          id: Types::Maybe::Strict::Int,
          stream_id: Types::UUID,
          event_id: Types::UUID.default(SecureRandom.uuid),
          correlation_id: Types::UUID.default(SecureRandom.uuid),
          timestamp: Types::Timestamp.default { Time.current },
          effective_timestamp: Types::Timestamp.default { Time.current },
          payload: self::PayloadSchema,
          metadata: self::MetadataSchema
        )
        event_hash = event_schema[*args]
        new(event_hash)
      end
    end

    attribute :id, Types::Maybe::Strict::Int
    attribute :stream_id, Types::UUID
    attribute :event_id, Types::UUID
    attribute :correlation_id, Types::UUID
    attribute :timestamp, Types::Timestamp
    attribute :effective_timestamp, Types::Timestamp
  end
end
And then a derived Event looks a little like this:
module Event
  class SomethingHappened < Base
    MetadataSchema = Types::Strict::Hash.strict(
      timestamp: Types::Timestamp
    )

    PayloadSchema = Types::Strict::Hash.strict(
      location: Types::ID
    )

    attribute :metadata, MetadataSchema
    attribute :payload, PayloadSchema

  end
end
The event is constructed with Event::SomethingHappened.build({...})
Piotr Solnica
@solnic
Jul 13 2016 22:56
Gotcha. This will work better in dry-struct so that missing keys will be filled in for you.
Brent Jubinville
@utilityboy
Jul 13 2016 22:58
Sounds great! I've used Virtus for years and really like where this is going. I wish I had better Ruby chops so I could contribute.
Piotr Solnica
@solnic
Jul 13 2016 23:00
@utilityboy cool :) I'll show you how to do it better for the time being, but later, as I'm on mobile atm
Brent Jubinville
@utilityboy
Jul 13 2016 23:00
Wicked. Thanks @solnic .
Once all this is baked, I am quite happy to write a blog / wiki post on how I've used it. That I certain am able to contribute to the project(s).
Piotr Solnica
@solnic
Jul 13 2016 23:02
That'd be fantastic. These projects needs testimonials a lot!
Brent Jubinville
@utilityboy
Jul 13 2016 23:03
Happy to.
Rob Jacoby
@robjacoby
Jul 13 2016 23:21
Morning folks! Was just wondering if there was any WIP docs for dry-view
Tim Riley
@timriley
Jul 13 2016 23:24
@robjacoby dry-view is entirely undocumented right now, I’m really sorry to say :(
But I can answer questions about it in the meantime, if you have any!
Rob Jacoby
@robjacoby
Jul 13 2016 23:24
I thought that might be the case :(
I’m kind of blundering my way through it, but my biggest confusion is partials and local variables
Tim Riley
@timriley
Jul 13 2016 23:26
I may in fact be working on something right now that can help with your confusion
Anyway, please do tell: what was getting you confused?
what is “page"
Tim Riley
@timriley
Jul 13 2016 23:31
Ah. page is the one special case for dry-view: it’s the object configured as the view class’ scope. It’s configured here and is an instance of this page class.
This is what enables you to establish some sort of common, baseline “context”/“environment” for your views.
For example, the Page#view_localsis what allows us to explicitly pass a set of common locals that we know every page should have, but without resorting back to a giant module grab bag of helpers.
The scope is handled specially for layout templates, too: https://github.com/dry-rb/dry-view/blob/master/lib/dry/view/layout.rb#L99-L105
that’s what makes it available as page inside the template file
We could perhaps improve the naming there.
Probably makes more sense to actually call it scope in the template too
Rob Jacoby
@robjacoby
Jul 13 2016 23:40
Ahh right, so page is only available within layouts
There also seems like there’s some magic going on within the shared directory
Tim Riley
@timriley
Jul 13 2016 23:42
Well, it’s a template lookup rule.
Rob Jacoby
@robjacoby
Jul 13 2016 23:44
I couldn’t seem to work out how to call something like shared/search/_large.html.slim
Ah, I don’t think it’d work for subdirs under shared
since partials get rendered by using method calls on view part objects
Rob Jacoby
@robjacoby
Jul 13 2016 23:45
yeah I tried search.large and obviously that didn’t work
good to know though, I’ll just rework my structure
Tim Riley
@timriley
Jul 13 2016 23:45
it’d be something we could look at supporting, but right now it’s just flat
but you can put a shared/ dir anywhere along the lookup path for your template
so if you were rendering some/search/show.html.slim, for example
you could have some/search/shared/_large.html.slim
Rob Jacoby
@robjacoby
Jul 13 2016 23:46
yep for sure
Tim Riley
@timriley
Jul 13 2016 23:46
and it’d be visible to other templates under some/search too
Rob Jacoby
@robjacoby
Jul 13 2016 23:47
last question (for now), if I wanted a helper method that’s available in the layout, Berg::Page would be the place for it?
Tim Riley
@timriley
Jul 13 2016 23:50
Yeah, that’s right.
I might see if we can improve the naming of these things so they’re more obvious.
Rob Jacoby
@robjacoby
Jul 13 2016 23:50
awesome thanks Tim :)
Tim Riley
@timriley
Jul 13 2016 23:51
My pleasure! And please keep the feedback coming. It’s how we’ll make dry-view better :)
BTW, I’m working on this today, @robjacoby: dry-rb/dry-view#7
So you can pass args to part methods and have them turn into extra data for a partial’s scope
Rob Jacoby
@robjacoby
Jul 13 2016 23:55
Ooh that could be very helpful