Comments (6)
The problem here is that ActiveSupport monkey patches Object
which Dry::Types::Enum
implicitly inherits from.
From: /home/benny/Dev/ruby/dry-rb/bugs/.gem/ruby/2.3.0/gems/dry-types-0.7.1/lib/dry/types/array/member.rb @ line 15 Dry::Types::Array::Member#call:
12: def call(input, meth = :call)
13: input.map { |el|
14: result = member.__send__(meth, el)
=> 15: binding.pry
16: result
17: }
18: end
[1] pry(#<Dry::Types::Array::Member>)> meth
=> :try
[2] pry(#<Dry::Types::Array::Member>)> member
=> #<Dry::Types::Enum:0x0055df70d72fa8
@mapping={0=>"val1", 1=>"val2"},
@options={:values=>["val1", "val2"]},
@type=
#<Dry::Types::Constrained:0x0055df70d73070
@options=
{:rule=>
#<Dry::Logic::Rule::Conjunction left=#<Dry::Logic::Rule::Value predicate=#<Dry::Logic::Predicate id=:type? args=[String]> options={}> right=#<Dry::Logic::Rule::Value predicate=#<Dry::Logic::Predicate id=:inclusion? args=[["val1", "val2"]]> options={}>>},
@rule=
#<Dry::Logic::Rule::Conjunction left=#<Dry::Logic::Rule::Value predicate=#<Dry::Logic::Predicate id=:type? args=[String]> options={}> right=#<Dry::Logic::Rule::Value predicate=#<Dry::Logic::Predicate id=:inclusion? args=[["val1", "val2"]]> options={}>>,
@type=#<Dry::Types::Definition primitive=String options={}>>,
@values=["val1", "val2"]>
[3] pry(#<Dry::Types::Array::Member>)> member.method(:try)
=> #<Method: Dry::Types::Enum(Object)#try>
[4] pry(#<Dry::Types::Array::Member>)> member.method(:try).source_location
=> ["/home/benny/Dev/ruby/dry-rb/bugs/.gem/ruby/2.3.0/gems/activesupport-4.2.6/lib/active_support/core_ext/object/try.rb", 62]
[5] pry(#<Dry::Types::Array::Member>)> member
=> #<Dry::Types::Enum:0x0055df70d72fa8
@mapping={0=>"val1", 1=>"val2"},
@options={:values=>["val1", "val2"]},
@type=
#<Dry::Types::Constrained:0x0055df70d73070
@options=
{:rule=>
#<Dry::Logic::Rule::Conjunction left=#<Dry::Logic::Rule::Value predicate=#<Dry::Logic::Predicate id=:type? args=[String]> options={}> right=#<Dry::Logic::Rule::Value predicate=#<Dry::Logic::Predicate id=:inclusion? args=[["val1", "val2"]]> options={}>>},
@rule=
#<Dry::Logic::Rule::Conjunction left=#<Dry::Logic::Rule::Value predicate=#<Dry::Logic::Predicate id=:type? args=[String]> options={}> right=#<Dry::Logic::Rule::Value predicate=#<Dry::Logic::Predicate id=:inclusion? args=[["val1", "val2"]]> options={}>>,
@type=#<Dry::Types::Definition primitive=String options={}>>,
@values=["val1", "val2"]>
[6] pry(#<Dry::Types::Array::Member>)> member.class.ancestors
=> [Dry::Types::Enum, Dry::Types::Decorator, Object, #<Module:0x0055df707e4cd0>, PP::ObjectMixin, Kernel, BasicObject]
And therefore the method_missing is never hit.
I'm not sure what dry-types should do in that cases where another thirdparty lib monkey patches basically everything.
I know we have to change because rails and therefore activesupport is kinda a monopoly, which makes me really sad..
You mentioned it's working in other scenarios. Can you give 1 example where exactly so I can lookup the differences. I'm not sure how many methods Dry::Types::Enum
needs from Object, maybe 1 possible way is to explicitly inherit from BasicObject
, which leads to less inspectability but it would solve that problem.
from dry-types.
I only tried it with some other types and there it worked. So something like.
CustomStringArray = Strict::Array.member(Coercible::String)
Form my point of view this is caused by how the array type and enum works together. I just wasn't sure if a similar approach to the explicit usage/exploit of method_missing in enum is used somewhere else.
from dry-types.
For you're given example:
CustomStringArray = Strict::Array.member(Coercible::String)
I get following behaviour:
From: /home/benny/Dev/ruby/dry-rb/bugs/.gem/ruby/2.3.0/gems/dry-types-0.7.1/lib/dry/types/array/member.rb @ line 15 Dry::Types::Array::Member#call:
12: def call(input, meth = :call)
13: input.map { |el|
14: result = member.__send__(meth, el)
=> 15: binding.pry
16: result
17: }
18: end
[1] pry(#<Dry::Types::Array::Member>)> meth
=> :try
[2] pry(#<Dry::Types::Array::Member>)> member
=> #<Dry::Types::Constructor type=#<Dry::Types::Definition primitive=String options={}>>
[3] pry(#<Dry::Types::Array::Member>)> member.method(:try)
=> #<Method: Dry::Types::Constructor#try>
[4] pry(#<Dry::Types::Array::Member>)> member.class
=> Dry::Types::Constructor
[5] pry(#<Dry::Types::Array::Member>)> member.class.ancestors
=> [Dry::Types::Constructor,
#<Dry::Equalizer:0x00560ad0b03bb0>,
Dry::Types::Definition,
Dry::Types::Builder,
Dry::Types::Options,
Dry::Equalizer::Methods,
#<Dry::Equalizer:0x00560ad0b35020>,
Object,
#<Module:0x00560ad0b2c448>,
PP::ObjectMixin,
Kernel,
BasicObject]
[6] pry(#<Dry::Types::Array::Member>)> member.method(:try).source_location
=> ["/home/benny/Dev/ruby/dry-rb/bugs/.gem/ruby/2.3.0/gems/dry-types-0.7.1/lib/dry/types/constructor.rb", 32]
The reason why this works is because in
dry-types/lib/dry/types/constructor.rb
Line 32 in 9d72307
So I see 2 possible solutions for now:
- Do not rely on method_missing and somehow implement
Dry::Types::Enum#try
- Rely on method_missing with a clean slate (inherit from BasicObject)
https://github.com/rails/rails/blob/52ce6ece8c8f74064bb64e0a0b1ddd83092718e1/activesupport/lib/active_support/core_ext/object/try.rb#L87
from dry-types.
IMHO relying on method_missing
in a gem without a clean slate is dangerous. There are quite a few gems which add them self to object (i.e. json and bson just to name two from the top of my head)
I haven't dug into the code to deep but I've tried to have Dry::Types::Enum
inherit from BasicObject
. The specs stayed green and it fixes the described issue. (see master...andreaseger:fix_try_method_missing_conflict )
I can make a PR from there if this will be the preferred solution.
Would it be possible to completely remove the reliance on method_missing? Meaning not only for this special case but in general.
from dry-types.
It looks like you're heading in a good direction, @andreaseger. To me it seems like it's the #method_missing
in Dry::Types::Decorator
that's probably the real issue here, because the #method_missing
in Dry::Types::Constructor
has a local #try
method right alongside it, so it shouldn't have the issue in an ActiveSupport-tainted environment that you saw.
Decorator
is mixed into 6 classes, and if your experiments with having one of them inherit from BasicObject is successful, I reckon it'd be worth rolling that out to the other 5 and seeing how it looks then. At least at this point we'd have a complete PR for further discussion :) What do you think?
from dry-types.
Let's just implement try
in Decorator and delegate explicitly.
from dry-types.
Related Issues (20)
- Types::Params::Integer incorrect coercion for numeric values HOT 9
- Types::Params::*.optional not handling empty string HOT 4
- Anonymous Dry::Types based module cannot be extended HOT 4
- Params namespace disapearing HOT 9
- Params coercion for Array works in an unintuitive way HOT 1
- https://dry-rb.org/gems/dry-types/ not at latest version HOT 1
- [Security] Workflow ci.yml is using vulnerable action actions/checkout
- Compound Types with Examples HOT 1
- Dry::Container::Error: There is already an item registered with the key "nominal.string" on jruby-head HOT 4
- Future-proof local override for Types::Instance/Constructor classes that potentially collide with predicates
- Unexpected error raised from Sum type HOT 7
- Types::Coeercible::Integer, should this use Base 10? HOT 2
- Add Zeitwerk autoloader
- Using transform_keys breaks error handling
- Undefined method after upgrading dependencies. HOT 5
- Missing method implementation for `Enum.each_value` HOT 1
- Types.Constructor failing on Ruby 3.1 HOT 8
- A bug with array, struct and sum of structs in `try` method
- Fix BigDemical warning
- default causes try/1 to raise an ConstraintError exception rather than an Failure
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
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.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from dry-types.