diff --git a/lib/inertia_rails/base_prop.rb b/lib/inertia_rails/base_prop.rb index 41ba85d..6dcde20 100644 --- a/lib/inertia_rails/base_prop.rb +++ b/lib/inertia_rails/base_prop.rb @@ -5,22 +5,12 @@ def initialize(value = nil, &block) @block = block end - def call - to_proc.call + def call(controller) + value.respond_to?(:call) ? controller.instance_exec(&value) : value end - private - - def to_proc - # This is called by controller.instance_exec, which changes self to the - # controller instance. That makes the instance variables unavailable to the - # proc via closure. Copying the instance variables to local variables before - # the proc is returned keeps them in scope for the returned proc. - value = @value - return value if value.respond_to?(:call) - return Proc.new { value } if value - - @block + def value + @value.nil? ? @block : @value end end end diff --git a/lib/inertia_rails/middleware.rb b/lib/inertia_rails/middleware.rb index 0d0d017..0600149 100644 --- a/lib/inertia_rails/middleware.rb +++ b/lib/inertia_rails/middleware.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module InertiaRails class Middleware def initialize(app) diff --git a/lib/inertia_rails/renderer.rb b/lib/inertia_rails/renderer.rb index 5662d53..0029196 100644 --- a/lib/inertia_rails/renderer.rb +++ b/lib/inertia_rails/renderer.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'net/http' require 'json' require_relative "inertia_rails" @@ -84,12 +86,16 @@ def computed_props drop_partial_except_keys(_props) if rendering_partial_component? - deep_transform_values( - _props, - lambda do |prop| - prop.respond_to?(:call) ? controller.instance_exec(&prop) : prop + deep_transform_values _props do |prop| + case prop + when BaseProp + prop.call(controller) + when Proc + controller.instance_exec(&prop) + else + prop end - ) + end end def page @@ -111,10 +117,10 @@ def page default_page end - def deep_transform_values(hash, proc) - return proc.call(hash) unless hash.is_a? Hash + def deep_transform_values(hash, &block) + return block.call(hash) unless hash.is_a? Hash - hash.transform_values {|value| deep_transform_values(value, proc)} + hash.transform_values {|value| deep_transform_values(value, &block)} end def drop_partial_except_keys(hash) diff --git a/spec/dummy/app/controllers/application_controller.rb b/spec/dummy/app/controllers/application_controller.rb index 09705d1..5eb9132 100644 --- a/spec/dummy/app/controllers/application_controller.rb +++ b/spec/dummy/app/controllers/application_controller.rb @@ -1,2 +1,5 @@ class ApplicationController < ActionController::Base + def controller_method + "controller_method value" + end end diff --git a/spec/inertia/always_prop_spec.rb b/spec/inertia/always_prop_spec.rb index 46916b1..7ea0fde 100644 --- a/spec/inertia/always_prop_spec.rb +++ b/spec/inertia/always_prop_spec.rb @@ -1,20 +1,3 @@ RSpec.describe InertiaRails::AlwaysProp do - describe '#call' do - subject(:call) { prop.call } - let(:prop) { described_class.new('value') } - - it { is_expected.to eq('value') } - - context 'with a callable value' do - let(:prop) { described_class.new(-> { 'callable' }) } - - it { is_expected.to eq('callable') } - end - - context 'with a block' do - let(:prop) { described_class.new { 'block' } } - - it { is_expected.to eq('block') } - end - end + it_behaves_like 'callable prop' end diff --git a/spec/inertia/base_prop_spec.rb b/spec/inertia/base_prop_spec.rb new file mode 100644 index 0000000..da5451e --- /dev/null +++ b/spec/inertia/base_prop_spec.rb @@ -0,0 +1,3 @@ +RSpec.describe InertiaRails::BaseProp do + it_behaves_like 'callable prop' +end diff --git a/spec/inertia/defer_prop_spec.rb b/spec/inertia/defer_prop_spec.rb index b7d6fb6..8727998 100644 --- a/spec/inertia/defer_prop_spec.rb +++ b/spec/inertia/defer_prop_spec.rb @@ -1,42 +1,31 @@ RSpec.describe InertiaRails::DeferProp do - describe '#call' do - subject(:call) { prop.call } - let(:prop) { described_class.new('value') } + let(:prop) { described_class.new('value') } - it { is_expected.to eq('value') } + it_behaves_like 'callable prop' + + describe '#group' do + subject(:group) { prop.group } it 'returns the default group' do - expect(prop.group).to eq('default') + expect(group).to eq('default') end - context "with group" do + context "with a custom group" do let(:prop) { described_class.new('value', group: 'custom') } it 'returns the group' do - expect(prop.group).to eq('custom') + expect(group).to eq('custom') end - end - - context 'with a callable value' do - let(:prop) { described_class.new(-> { 'callable' }) } - it { is_expected.to eq('callable') } - - context "with group" do + context 'with a callable value' do let(:prop) { described_class.new(-> { 'callable' }, group: 'custom') } it 'returns the group' do - expect(prop.group).to eq('custom') + expect(group).to eq('custom') end end - end - - context 'with a block' do - let(:prop) { described_class.new { 'block' } } - it { is_expected.to eq('block') } - - context "with group" do + context 'with a block' do let(:prop) { described_class.new(group: 'custom') { 'block' } } it 'returns the group' do @@ -44,8 +33,10 @@ end end end + end - it 'returns the merge flag' do + describe '#merge' do + it 'updates the merge value' do expect(prop.merge?).to be_falsey prop.merge diff --git a/spec/inertia/merge_prop_spec.rb b/spec/inertia/merge_prop_spec.rb index c5b626d..42e2a57 100644 --- a/spec/inertia/merge_prop_spec.rb +++ b/spec/inertia/merge_prop_spec.rb @@ -1,24 +1,11 @@ RSpec.describe InertiaRails::MergeProp do - describe '#call' do - subject(:call) { prop.call } - let(:prop) { described_class.new('value') } + let(:prop) { described_class.new('value') } - it { is_expected.to eq('value') } + describe '#merge?' do + subject { prop.merge? } - context 'with a callable value' do - let(:prop) { described_class.new(-> { 'callable' }) } - - it { is_expected.to eq('callable') } - end - - context 'with a block' do - let(:prop) { described_class.new { 'block' } } - - it { is_expected.to eq('block') } - end - - it 'returns the merge flag' do - expect(prop.merge?).to eq(true) - end + it { is_expected.to be(true) } end + + it_behaves_like 'callable prop' end diff --git a/spec/inertia/optional_prop_spec.rb b/spec/inertia/optional_prop_spec.rb index 7130afe..26f11f4 100644 --- a/spec/inertia/optional_prop_spec.rb +++ b/spec/inertia/optional_prop_spec.rb @@ -1,21 +1,3 @@ RSpec.describe InertiaRails::OptionalProp do - describe '#call' do - context 'with a value' do - it 'returns the value' do - expect(InertiaRails::OptionalProp.new('thing').call).to eq('thing') - end - end - - context 'with a callable value' do - it 'returns the result of the callable value' do - expect(InertiaRails::OptionalProp.new(->{ 'thing' }).call).to eq('thing') - end - end - - context 'with a block' do - it 'returns the result of the block' do - expect(InertiaRails::OptionalProp.new {'thing'}.call).to eq('thing') - end - end - end + it_behaves_like 'callable prop' end diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index f89646e..2843743 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -10,9 +10,8 @@ abort("The Rails environment is running in production mode!") if Rails.env.production? require 'rspec/rails' # Require the spec/support directory and its subdirectories. -Dir[Rails.root.join('spec', 'support', '**', '*.rb')].each { |f| require f } +Dir[Pathname.new(__dir__).join('support', '**', '*.rb')].each { |f| require f } -require_relative './support/helper_module' # Add additional requires below this line. Rails is not loaded until this point! # Requires supporting ruby files with custom matchers and macros, etc, in # spec/support/ and its subdirectories. Files matching `spec/**/*_spec.rb` are diff --git a/spec/support/shared_examples.rb b/spec/support/shared_examples.rb new file mode 100644 index 0000000..90e2248 --- /dev/null +++ b/spec/support/shared_examples.rb @@ -0,0 +1,45 @@ +RSpec.shared_examples 'callable prop' do + describe '#call' do + subject(:call) { prop.call(controller) } + let(:prop) { described_class.new('value') } + let(:controller) { ApplicationController.new } + + it { is_expected.to eq('value') } + + context 'with a callable value' do + let(:prop) { described_class.new(-> { 'callable' }) } + + it { is_expected.to eq('callable') } + + context "with false as value" do + let(:prop) { described_class.new(false) } + + it { is_expected.to eq(false) } + end + + context "with nil as value" do + let(:prop) { described_class.new(nil) } + + it { is_expected.to eq(nil) } + end + + context "with dependency on the context of a controller" do + let(:prop) { described_class.new(-> { controller_method }) } + + it { is_expected.to eq('controller_method value') } + end + end + + context 'with a block' do + let(:prop) { described_class.new { 'block' } } + + it { is_expected.to eq('block') } + + context "with dependency on the context of a controller" do + let(:prop) { described_class.new { controller_method } } + + it { is_expected.to eq('controller_method value') } + end + end + end +end \ No newline at end of file