reform: Problem with delegation in forms in 2.6.1 / disposable 0.6.0

Complete Description of Issue

I have this in a contract:

money_property :fixed_charge, currency: ->(form) { form.default_currency }

...

private

delegate :assignment, to: :model
delegate :default_currency, to: :assignment, allow_nil: true

It works fine with Reform 2.6.0, but with 2.6.1 I get the following error:

  1) timesheets submission as a contractor submit a timesheet block with a note
     Failure/Error: delegate :assignment, to: :model

     ArgumentError:
       wrong number of arguments (given 2, expected 1)
     # /Users/samuelstickland/.rubies/ruby-3.0.2/lib/ruby/3.0.0/forwardable.rb:133:in `instance_delegate'
     # ./app/concepts/assignments/contract/rate.rb:56:in `<class:Rate>'
     # ./app/concepts/assignments/contract/rate.rb:2:in `<module:Contract>'
     # ./app/concepts/assignments/contract/rate.rb:1:in `<main>'
     # ./app/concepts/assignments/op/add_rate.rb:7:in `<class:Present>'
     # ./app/concepts/assignments/op/add_rate.rb:3:in `<class:AddRate>'
     # ./app/concepts/assignments/op/add_rate.rb:2:in `<module:Op>'
     # ./app/concepts/assignments/op/add_rate.rb:1:in `<main>'

Line 56 is the delegation line, line 2 is the class definition, which is weird.

If I remove the delegation and do this instead then it works:

def assignment
  model.assignment
end

def default_currency
  assignment&.default_currency
end

However, those methods can no longer be private, which suggests to me that execution scope has changed?

money_property is defined like this - not pasting the whole thing here - but you can see how the proc passed to currency is called.

  module Contract
    module Money
      extend ActiveSupport::Concern

      module ClassMethods
        def money_property(name, currency: nil, **options)
          ...
            define_method :"coerce_#{name}" do |value|
              currency_val = currency.respond_to?(:call) ? currency.call(self) : currency

                ...
            end
        end
      end
    end
  end

Has the self that is passed to call changed?

Steps to reproduce

Expected behavior

Should function as on 2.6.0

Actual behavior

Getting wrong number of arguments (given 2, expected 1) on use of delegate

System configuration

Reform version: 2.6.1 Ruby version: 3.0.2

Full Backtrace of Exception (if any)

     # /Users/samuelstickland/.rubies/ruby-3.0.2/lib/ruby/3.0.0/forwardable.rb:133:in `instance_delegate'
     # ./app/concepts/assignments/contract/rate.rb:56:in `<class:Rate>'
     # ./app/concepts/assignments/contract/rate.rb:2:in `<module:Contract>'
     # ./app/concepts/assignments/contract/rate.rb:1:in `<main>'
     # /Users/samuelstickland/.gem/ruby/3.0.2/gems/bootsnap-1.9.1/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:23:in `require'
     # /Users/samuelstickland/.gem/ruby/3.0.2/gems/bootsnap-1.9.1/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:23:in `block in require_with_bootsnap_lfi'
     # /Users/samuelstickland/.gem/ruby/3.0.2/gems/bootsnap-1.9.1/lib/bootsnap/load_path_cache/loaded_features_index.rb:92:in `register'
     # /Users/samuelstickland/.gem/ruby/3.0.2/gems/bootsnap-1.9.1/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:22:in `require_with_bootsnap_lfi'
     # /Users/samuelstickland/.gem/ruby/3.0.2/gems/bootsnap-1.9.1/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:31:in `require'
     # /Users/samuelstickland/.gem/ruby/3.0.2/gems/zeitwerk-2.5.1/lib/zeitwerk/kernel.rb:27:in `require'
     # ./app/concepts/assignments/op/add_rate.rb:7:in `<class:Present>'
     # ./app/concepts/assignments/op/add_rate.rb:3:in `<class:AddRate>'
     # ./app/concepts/assignments/op/add_rate.rb:2:in `<module:Op>'
     # ./app/concepts/assignments/op/add_rate.rb:1:in `<main>'
     # /Users/samuelstickland/.gem/ruby/3.0.2/gems/bootsnap-1.9.1/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:23:in `require'
     # /Users/samuelstickland/.gem/ruby/3.0.2/gems/bootsnap-1.9.1/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:23:in `block in require_with_bootsnap_lfi'
     # /Users/samuelstickland/.gem/ruby/3.0.2/gems/bootsnap-1.9.1/lib/bootsnap/load_path_cache/loaded_features_index.rb:92:in `register'
     # /Users/samuelstickland/.gem/ruby/3.0.2/gems/bootsnap-1.9.1/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:22:in `require_with_bootsnap_lfi'
     # /Users/samuelstickland/.gem/ruby/3.0.2/gems/bootsnap-1.9.1/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:31:in `require'
     # /Users/samuelstickland/.gem/ruby/3.0.2/gems/zeitwerk-2.5.1/lib/zeitwerk/kernel.rb:27:in `require'
     # ./spec/factories/rates.rb:41:in `block (3 levels) in <main>'
     # /Users/samuelstickland/.gem/ruby/3.0.2/gems/factory_bot-6.2.0/lib/factory_bot/attribute_assigner.rb:50:in `instance_exec'
     # /Users/samuelstickland/.gem/ruby/3.0.2/gems/factory_bot-6.2.0/lib/factory_bot/attribute_assigner.rb:50:in `build_class_instance'
     # /Users/samuelstickland/.gem/ruby/3.0.2/gems/factory_bot-6.2.0/lib/factory_bot/attribute_assigner.rb:13:in `object'
     # /Users/samuelstickland/.gem/ruby/3.0.2/gems/factory_bot-6.2.0/lib/factory_bot/evaluation.rb:13:in `object'
     # /Users/samuelstickland/.gem/ruby/3.0.2/gems/factory_bot-6.2.0/lib/factory_bot/strategy/create.rb:9:in `result'
     # /Users/samuelstickland/.gem/ruby/3.0.2/gems/factory_bot-6.2.0/lib/factory_bot/factory.rb:43:in `run'
     # /Users/samuelstickland/.gem/ruby/3.0.2/gems/factory_bot-6.2.0/lib/factory_bot/factory_runner.rb:29:in `block in run'
     # /Users/samuelstickland/.gem/ruby/3.0.2/gems/activesupport-6.1.4.1/lib/active_support/notifications.rb:205:in `instrument'
     # /Users/samuelstickland/.gem/ruby/3.0.2/gems/factory_bot-6.2.0/lib/factory_bot/factory_runner.rb:28:in `run'
     # /Users/samuelstickland/.gem/ruby/3.0.2/gems/factory_bot-6.2.0/lib/factory_bot/strategy_syntax_method_registrar.rb:28:in `block in define_singular_strategy_method'
     # ./spec/factories/assignments.rb:169:in `block (4 levels) in <main>'
     # /Users/samuelstickland/.gem/ruby/3.0.2/gems/factory_bot-6.2.0/lib/factory_bot/callback.rb:13:in `instance_exec'
     # /Users/samuelstickland/.gem/ruby/3.0.2/gems/factory_bot-6.2.0/lib/factory_bot/callback.rb:13:in `run'
     # /Users/samuelstickland/.gem/ruby/3.0.2/gems/factory_bot-6.2.0/lib/factory_bot/callbacks_observer.rb:11:in `block in update'
     # /Users/samuelstickland/.gem/ruby/3.0.2/gems/factory_bot-6.2.0/lib/factory_bot/callbacks_observer.rb:10:in `each'
     # /Users/samuelstickland/.gem/ruby/3.0.2/gems/factory_bot-6.2.0/lib/factory_bot/callbacks_observer.rb:10:in `update'
     # /Users/samuelstickland/.rubies/ruby-3.0.2/lib/ruby/3.0.0/observer.rb:222:in `block in notify_observers'
     # /Users/samuelstickland/.rubies/ruby-3.0.2/lib/ruby/3.0.0/observer.rb:221:in `each'
     # /Users/samuelstickland/.rubies/ruby-3.0.2/lib/ruby/3.0.0/observer.rb:221:in `notify_observers'
     # /Users/samuelstickland/.gem/ruby/3.0.2/gems/factory_bot-6.2.0/lib/factory_bot/evaluation.rb:24:in `notify'
     # /Users/samuelstickland/.gem/ruby/3.0.2/gems/factory_bot-6.2.0/lib/factory_bot/strategy/create.rb:13:in `block in result'
     # <internal:kernel>:90:in `tap'
     # /Users/samuelstickland/.gem/ruby/3.0.2/gems/factory_bot-6.2.0/lib/factory_bot/strategy/create.rb:9:in `result'
     # /Users/samuelstickland/.gem/ruby/3.0.2/gems/factory_bot-6.2.0/lib/factory_bot/factory.rb:43:in `run'
     # /Users/samuelstickland/.gem/ruby/3.0.2/gems/factory_bot-6.2.0/lib/factory_bot/factory_runner.rb:29:in `block in run'
     # /Users/samuelstickland/.gem/ruby/3.0.2/gems/activesupport-6.1.4.1/lib/active_support/notifications.rb:205:in `instrument'
     # /Users/samuelstickland/.gem/ruby/3.0.2/gems/factory_bot-6.2.0/lib/factory_bot/factory_runner.rb:28:in `run'
     # /Users/samuelstickland/.gem/ruby/3.0.2/gems/factory_bot-6.2.0/lib/factory_bot/strategy_syntax_method_registrar.rb:28:in `block in define_singular_strategy_method'
     # ./spec/concepts/timesheets/submissions/features/timesheets_spec.rb:20:in `block (3 levels) in <top (required)>'
     # /Users/samuelstickland/.gem/ruby/3.0.2/gems/rspec-core-3.10.1/lib/rspec/core/memoized_helpers.rb:317:in `block (2 levels) in let'
     # /Users/samuelstickland/.gem/ruby/3.0.2/gems/rspec-core-3.10.1/lib/rspec/core/memoized_helpers.rb:157:in `block (3 levels) in fetch_or_store'
     # /Users/samuelstickland/.gem/ruby/3.0.2/gems/rspec-core-3.10.1/lib/rspec/core/memoized_helpers.rb:157:in `fetch'
     # /Users/samuelstickland/.gem/ruby/3.0.2/gems/rspec-core-3.10.1/lib/rspec/core/memoized_helpers.rb:157:in `block (2 levels) in fetch_or_store'
     # /Users/samuelstickland/.gem/ruby/3.0.2/gems/rspec-support-3.10.3/lib/rspec/support/reentrant_mutex.rb:23:in `synchronize'
     # /Users/samuelstickland/.gem/ruby/3.0.2/gems/rspec-core-3.10.1/lib/rspec/core/memoized_helpers.rb:156:in `block in fetch_or_store'
     # /Users/samuelstickland/.gem/ruby/3.0.2/gems/rspec-core-3.10.1/lib/rspec/core/memoized_helpers.rb:155:in `fetch'
     # /Users/samuelstickland/.gem/ruby/3.0.2/gems/rspec-core-3.10.1/lib/rspec/core/memoized_helpers.rb:155:in `fetch_or_store'
     # /Users/samuelstickland/.gem/ruby/3.0.2/gems/rspec-core-3.10.1/lib/rspec/core/memoized_helpers.rb:317:in `block in let'
     # /Users/samuelstickland/.gem/ruby/3.0.2/gems/rspec-core-3.10.1/lib/rspec/core/memoized_helpers.rb:376:in `block in let!'
     # /Users/samuelstickland/.gem/ruby/3.0.2/gems/rspec-core-3.10.1/lib/rspec/core/example.rb:455:in `instance_exec'
     # /Users/samuelstickland/.gem/ruby/3.0.2/gems/rspec-core-3.10.1/lib/rspec/core/example.rb:455:in `instance_exec'
     # /Users/samuelstickland/.gem/ruby/3.0.2/gems/rspec-core-3.10.1/lib/rspec/core/hooks.rb:365:in `run'
     # /Users/samuelstickland/.gem/ruby/3.0.2/gems/rspec-core-3.10.1/lib/rspec/core/hooks.rb:529:in `block in run_owned_hooks_for'
     # /Users/samuelstickland/.gem/ruby/3.0.2/gems/rspec-core-3.10.1/lib/rspec/core/hooks.rb:528:in `each'
     # /Users/samuelstickland/.gem/ruby/3.0.2/gems/rspec-core-3.10.1/lib/rspec/core/hooks.rb:528:in `run_owned_hooks_for'
     # /Users/samuelstickland/.gem/ruby/3.0.2/gems/rspec-core-3.10.1/lib/rspec/core/hooks.rb:615:in `block in run_example_hooks_for'
     # /Users/samuelstickland/.gem/ruby/3.0.2/gems/rspec-core-3.10.1/lib/rspec/core/hooks.rb:614:in `reverse_each'
     # /Users/samuelstickland/.gem/ruby/3.0.2/gems/rspec-core-3.10.1/lib/rspec/core/hooks.rb:614:in `run_example_hooks_for'
     # /Users/samuelstickland/.gem/ruby/3.0.2/gems/rspec-core-3.10.1/lib/rspec/core/hooks.rb:484:in `run'
     # /Users/samuelstickland/.gem/ruby/3.0.2/gems/rspec-core-3.10.1/lib/rspec/core/example.rb:502:in `run_before_example'
     # /Users/samuelstickland/.gem/ruby/3.0.2/gems/rspec-core-3.10.1/lib/rspec/core/example.rb:261:in `block in run'
     # /Users/samuelstickland/.gem/ruby/3.0.2/gems/rspec-core-3.10.1/lib/rspec/core/example.rb:508:in `block in with_around_and_singleton_context_hooks'
     # /Users/samuelstickland/.gem/ruby/3.0.2/gems/rspec-core-3.10.1/lib/rspec/core/example.rb:465:in `block in with_around_example_hooks'
     # /Users/samuelstickland/.gem/ruby/3.0.2/gems/rspec-core-3.10.1/lib/rspec/core/hooks.rb:486:in `block in run'
     # /Users/samuelstickland/.gem/ruby/3.0.2/gems/rspec-core-3.10.1/lib/rspec/core/hooks.rb:626:in `block in run_around_example_hooks_for'
     # /Users/samuelstickland/.gem/ruby/3.0.2/gems/rspec-core-3.10.1/lib/rspec/core/example.rb:350:in `call'
     # /Users/samuelstickland/.gem/ruby/3.0.2/gems/rspec-rails-5.0.2/lib/rspec/rails/adapters.rb:75:in `block (2 levels) in <module:MinitestLifecycleAdapter>'
     # /Users/samuelstickland/.gem/ruby/3.0.2/gems/rspec-core-3.10.1/lib/rspec/core/example.rb:455:in `instance_exec'
     # /Users/samuelstickland/.gem/ruby/3.0.2/gems/rspec-core-3.10.1/lib/rspec/core/example.rb:455:in `instance_exec'
     # /Users/samuelstickland/.gem/ruby/3.0.2/gems/rspec-core-3.10.1/lib/rspec/core/hooks.rb:390:in `execute_with'
     # /Users/samuelstickland/.gem/ruby/3.0.2/gems/rspec-core-3.10.1/lib/rspec/core/hooks.rb:628:in `block (2 levels) in run_around_example_hooks_for'
     # /Users/samuelstickland/.gem/ruby/3.0.2/gems/rspec-core-3.10.1/lib/rspec/core/example.rb:350:in `call'
     # /Users/samuelstickland/.gem/ruby/3.0.2/gems/rspec-core-3.10.1/lib/rspec/core/hooks.rb:629:in `run_around_example_hooks_for'
     # /Users/samuelstickland/.gem/ruby/3.0.2/gems/rspec-core-3.10.1/lib/rspec/core/hooks.rb:486:in `run'
     # /Users/samuelstickland/.gem/ruby/3.0.2/gems/rspec-core-3.10.1/lib/rspec/core/example.rb:465:in `with_around_example_hooks'
     # /Users/samuelstickland/.gem/ruby/3.0.2/gems/rspec-core-3.10.1/lib/rspec/core/example.rb:508:in `with_around_and_singleton_context_hooks'
     # /Users/samuelstickland/.gem/ruby/3.0.2/gems/rspec-core-3.10.1/lib/rspec/core/example.rb:259:in `run'
     # /Users/samuelstickland/.gem/ruby/3.0.2/gems/rspec-core-3.10.1/lib/rspec/core/example_group.rb:644:in `block in run_examples'
     # /Users/samuelstickland/.gem/ruby/3.0.2/gems/rspec-core-3.10.1/lib/rspec/core/example_group.rb:640:in `map'
     # /Users/samuelstickland/.gem/ruby/3.0.2/gems/rspec-core-3.10.1/lib/rspec/core/example_group.rb:640:in `run_examples'
     # /Users/samuelstickland/.gem/ruby/3.0.2/gems/rspec-core-3.10.1/lib/rspec/core/example_group.rb:606:in `run'
     # /Users/samuelstickland/.gem/ruby/3.0.2/gems/rspec-core-3.10.1/lib/rspec/core/example_group.rb:607:in `block in run'
     # /Users/samuelstickland/.gem/ruby/3.0.2/gems/rspec-core-3.10.1/lib/rspec/core/example_group.rb:607:in `map'
     # /Users/samuelstickland/.gem/ruby/3.0.2/gems/rspec-core-3.10.1/lib/rspec/core/example_group.rb:607:in `run'
     # /Users/samuelstickland/.gem/ruby/3.0.2/gems/rspec-core-3.10.1/lib/rspec/core/runner.rb:121:in `block (3 levels) in run_specs'
     # /Users/samuelstickland/.gem/ruby/3.0.2/gems/rspec-core-3.10.1/lib/rspec/core/runner.rb:121:in `map'
     # /Users/samuelstickland/.gem/ruby/3.0.2/gems/rspec-core-3.10.1/lib/rspec/core/runner.rb:121:in `block (2 levels) in run_specs'
     # /Users/samuelstickland/.gem/ruby/3.0.2/gems/rspec-core-3.10.1/lib/rspec/core/configuration.rb:2067:in `with_suite_hooks'
     # /Users/samuelstickland/.gem/ruby/3.0.2/gems/rspec-core-3.10.1/lib/rspec/core/runner.rb:116:in `block in run_specs'
     # /Users/samuelstickland/.gem/ruby/3.0.2/gems/rspec-core-3.10.1/lib/rspec/core/reporter.rb:74:in `report'
     # /Users/samuelstickland/.gem/ruby/3.0.2/gems/rspec-core-3.10.1/lib/rspec/core/runner.rb:115:in `run_specs'
     # /Users/samuelstickland/.gem/ruby/3.0.2/gems/rspec-core-3.10.1/lib/rspec/core/runner.rb:89:in `run'
     # /Users/samuelstickland/.gem/ruby/3.0.2/gems/rspec-core-3.10.1/lib/rspec/core/runner.rb:71:in `run'
     # /Users/samuelstickland/.gem/ruby/3.0.2/gems/rspec-core-3.10.1/lib/rspec/core/runner.rb:45:in `invoke'
     # /Users/samuelstickland/.gem/ruby/3.0.2/gems/rspec-core-3.10.1/exe/rspec:4:in `<top (required)>'
     # /Users/samuelstickland/.gem/ruby/3.0.2/bin/rspec:25:in `load'
     # /Users/samuelstickland/.gem/ruby/3.0.2/bin/rspec:25:in `<top (required)>'
     # /Users/samuelstickland/.rubies/ruby-3.0.2/lib/ruby/site_ruby/3.0.0/bundler/cli/exec.rb:58:in `load'
     # /Users/samuelstickland/.rubies/ruby-3.0.2/lib/ruby/site_ruby/3.0.0/bundler/cli/exec.rb:58:in `kernel_load'
     # /Users/samuelstickland/.rubies/ruby-3.0.2/lib/ruby/site_ruby/3.0.0/bundler/cli/exec.rb:23:in `run'
     # /Users/samuelstickland/.rubies/ruby-3.0.2/lib/ruby/site_ruby/3.0.0/bundler/cli.rb:478:in `exec'
     # /Users/samuelstickland/.rubies/ruby-3.0.2/lib/ruby/site_ruby/3.0.0/bundler/vendor/thor/lib/thor/command.rb:27:in `run'
     # /Users/samuelstickland/.rubies/ruby-3.0.2/lib/ruby/site_ruby/3.0.0/bundler/vendor/thor/lib/thor/invocation.rb:127:in `invoke_command'
     # /Users/samuelstickland/.rubies/ruby-3.0.2/lib/ruby/site_ruby/3.0.0/bundler/vendor/thor/lib/thor.rb:392:in `dispatch'
     # /Users/samuelstickland/.rubies/ruby-3.0.2/lib/ruby/site_ruby/3.0.0/bundler/cli.rb:31:in `dispatch'
     # /Users/samuelstickland/.rubies/ruby-3.0.2/lib/ruby/site_ruby/3.0.0/bundler/vendor/thor/lib/thor/base.rb:485:in `start'
     # /Users/samuelstickland/.rubies/ruby-3.0.2/lib/ruby/site_ruby/3.0.0/bundler/cli.rb:25:in `start'
     # /Users/samuelstickland/.rubies/ruby-3.0.2/lib/ruby/gems/3.0.0/gems/bundler-2.2.30/exe/bundle:49:in `block in <top (required)>'
     # /Users/samuelstickland/.rubies/ruby-3.0.2/lib/ruby/site_ruby/3.0.0/bundler/friendly_errors.rb:103:in `with_friendly_errors'
     # /Users/samuelstickland/.rubies/ruby-3.0.2/lib/ruby/gems/3.0.0/gems/bundler-2.2.30/exe/bundle:37:in `<top (required)>'
     # /Users/samuelstickland/.rubies/ruby-3.0.2/bin/bundle:23:in `load'
     # /Users/samuelstickland/.rubies/ruby-3.0.2/bin/bundle:23:in `<main>'

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Reactions: 2
  • Comments: 26 (17 by maintainers)

Most upvoted comments

Here is the diff from my Gemfile.lock

Screenshot 2021-12-01 at 12 39 34 Screenshot 2021-12-01 at 12 40 31

Thanks for the repo, @majksner! I was able to make it work, but it’s a weird error, I suspect ActiveSupport’s delegate to be the problem source.

Here’s how I fixed it.

  class Factory < Reform::Form

    model :project
    property :state
    
    extend Forwardable
    def_delegators :model, :name

@apotonick

Here you go:

  1. Clone repo https://github.com/majksner/reform-issue-534
  2. bundle
  3. bundle exec rails s
  4. go to http://127.0.0.1:3000/welcome

Code is a bit messy, but this was the quickest thing to do.

Let me know if you need anything else.