Skip to content

Commit

Permalink
[D-326] Store accessor support for assignable_values
Browse files Browse the repository at this point in the history
  • Loading branch information
begerdom committed Nov 21, 2024
1 parent 6f848bf commit aae6356
Show file tree
Hide file tree
Showing 5 changed files with 239 additions and 7 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:
runs-on: ubuntu-20.04
services:
mysql:
image: mysql:5.6
image: mysql:5.7
env:
MYSQL_ROOT_PASSWORD: password
ports:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,15 +119,34 @@ def previously_saved_value(record)
end

def value_was(record)
if record.respond_to?(:attribute_in_database)
record.attribute_in_database(:"#{property}") # Rails >= 5.1
else
record.send(value_was_method) # Rails <= 5.0
if record.respond_to?(:attribute_in_database) # Rails >= 5.1
if store_accessor_attribute?
accessor = record.attribute_in_database(:"#{store_identifier}").with_indifferent_access
accessor[property]
else
record.attribute_in_database(:"#{property}")
end
else # Rails <= 5.0
result = record.send(value_was_method).with_indifferent_access
result = result[property] if store_accessor_attribute?
result
end
end

def value_was_method
:"#{property}_was"
if store_accessor_attribute?
:"#{store_identifier}_was"
else
:"#{property}_was"
end
end

def store_accessor_attribute?
store_identifier.present?
end

def store_identifier
@model.stored_attributes.find { |_, attrs| attrs.include?(property.to_sym) }&.first
end

end
Expand Down
207 changes: 206 additions & 1 deletion spec/assignable_values/active_record_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,7 @@ def save_without_validation(record)

end

context 'if the :allow_blank option is set to a lambda ' do
context 'if the :allow_blank option is set to a lambda' do

before :each do
@klass = Song.disposable_copy do
Expand Down Expand Up @@ -370,6 +370,211 @@ def save_without_validation(record)

end

context 'when validating scalar attributes from a store_accessor' do

context 'without options' do

before :each do
@klass = Song.disposable_copy do
store :metadata, accessors: [:format], coder: JSON

assignable_values_for :format do
%w[mp3 wav]
end
end
end

it 'should validate that the attribute is allowed' do
@klass.new(:format => 'mp3').should be_valid
@klass.new(:format => 'disallowed value').should_not be_valid
end

it 'should use the same error message as validates_inclusion_of' do
record = @klass.new(:format => 'disallowed value')
record.valid?
errors = record.errors[:format]
error = errors.respond_to?(:first) ? errors.first : errors # the return value sometimes was a string, sometimes an Array in Rails
error.should == I18n.t('errors.messages.inclusion')
error.should == 'is not included in the list'
end

it 'should not allow nil for the attribute value' do
@klass.new(:format => nil).should_not be_valid
end

it 'should allow a previously saved value even if that value is no longer allowed' do
record = @klass.create!(:format => 'mp3')

record.update_column(:metadata, { 'format' => 'pretend previously valid value' }) # update without validations for the sake of this test
record.reload.should be_valid
end

it 'should allow a previously saved, blank format value even if that value is no longer allowed' do
record = @klass.create!(:format => 'mp3')

record.update_column(:metadata, { 'format' => nil }) # update without validations for the sake of this test
record.reload.should be_valid
end

it 'should not allow nil (the "previous value") if the record was never saved' do
record = @klass.new(:format => nil)
# Show that nil is not assignable, even though `record.genre_was` is nil.
record.should_not be_valid
end

it 'should generate a method returning the humanized value' do
song = @klass.new(:format => 'mp3')
song.humanized_format.should == 'MP3-Codec'
end

it 'should generate a method returning the humanized value, which is nil when the value is blank' do
song = @klass.new
song.format = nil
song.humanized_format.should be_nil
song.format = ''
song.humanized_format.should be_nil
end

it 'should generate an instance method to retrieve the humanization of any given value' do
song = @klass.new(:format => 'mp3')
song.humanized_format('wav').should == 'WAV-Codec'
end

it 'should generate a class method to retrieve the humanization of any given value' do
@klass.humanized_format('wav').should == 'WAV-Codec'
end

context 'for multiple: true' do
before :each do
@klass = Song.disposable_copy do
store :metadata, accessors: [:instruments], coder: JSON

assignable_values_for :instruments, multiple: true do
%w[piano drums guitar]
end
end
end

it 'should raise when trying to humanize a value without an argument' do
song = @klass.new
proc { song.humanized_instrument }.should raise_error(ArgumentError)
end

it 'should generate an instance method to retrieve the humanization of any given value' do
song = @klass.new(:instruments => 'drums')
song.humanized_instrument('piano').should == 'Piano'
end

it 'should generate a class method to retrieve the humanization of any given value' do
@klass.humanized_instrument('piano').should == 'Piano'
end

it 'should generate an instance method to retrieve the humanizations of all current values' do
song = @klass.new
song.instruments = nil
song.humanized_instruments.should == nil
song.instruments = []
song.humanized_instruments.should == []
song.instruments = ['piano', 'drums']
song.humanized_instruments.should == ['Piano', 'Drums']
end
end
end

context 'if the :allow_blank option is set to true' do

before :each do
@klass = Song.disposable_copy do

store :metadata, accessors: [:format], coder: JSON

assignable_values_for :format, :allow_blank => true do
%w[mp3 wav]
end
end
end

it 'should allow nil for the attribute value' do
@klass.new(:format => nil).should be_valid
end

it 'should allow an empty string as value' do
@klass.new(:format => '').should be_valid
end

end

context 'if the :allow_blank option is set to a symbol that refers to an instance method' do

before :each do
@klass = Song.disposable_copy do

store :metadata, accessors: [:format], coder: JSON

attr_accessor :format_may_be_blank

assignable_values_for :format, :allow_blank => :format_may_be_blank do
%w[mp3 wav]
end

end
end

it 'should call that method to determine if a blank value is allowed' do
@klass.new(:format => '', :format_may_be_blank => true).should be_valid
@klass.new(:format => '', :format_may_be_blank => false).should_not be_valid
end

end

context 'if the :allow_blank option is set to a lambda' do

before :each do
@klass = Song.disposable_copy do

store :metadata, accessors: [:format], coder: JSON

attr_accessor :format_may_be_blank

assignable_values_for :format, :allow_blank => lambda { format_may_be_blank } do
%w[mp3 wav]
end

end
end

it 'should evaluate that lambda in the record context to determine if a blank value is allowed' do
@klass.new(:format => '', :format_may_be_blank => true).should be_valid
@klass.new(:format => '', :format_may_be_blank => false).should_not be_valid
end

end

context 'if the :message option is set to a string' do

before :each do
@klass = Song.disposable_copy do

store :metadata, accessors: [:format], coder: JSON

assignable_values_for :format, :message => 'should be something different' do
%w[mp3 wav]
end
end
end

it 'should use this string as a custom error message' do
record = @klass.new(:format => 'disallowed value')
record.valid?
errors = record.errors[:format]
error = errors.respond_to?(:first) ? errors.first : errors # the return value sometimes was a string, sometimes an Array in Rails
error.should == 'should be something different'
end

end

end

context 'when validating belongs_to associations' do

it 'should validate that the association is allowed' do
Expand Down
1 change: 1 addition & 0 deletions spec/support/database.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
t.integer :year
t.integer :duration
t.string :multi_genres, :array => true
t.json :metadata
end

create_table :vinyl_recordings do |t|
Expand Down
7 changes: 7 additions & 0 deletions spec/support/i18n.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,16 @@ en:

assignable_values:
song:
format:
mp3: 'MP3-Codec'
wav: 'WAV-Codec'
genre:
pop: 'Pop music'
rock: 'Rock music'
instruments:
drums: 'Drums'
guitar: 'Guitar'
piano: 'Piano'
multi_genres:
pop: 'Pop music'
rock: 'Rock music'
Expand Down

0 comments on commit aae6356

Please sign in to comment.