spec/attributes_spec.rb in cistern-2.4.1 vs spec/attributes_spec.rb in cistern-2.5.0
- old
+ new
@@ -1,166 +1,260 @@
require 'spec_helper'
-describe Cistern::Attributes, 'requires' do
- class RequireSpec < Sample::Model
- identity :id
- attribute :name, type: :string
- attribute :type
+describe Cistern::Attributes, '#request_attributes' do
+ subject { Class.new(Sample::Model) }
+
+ it 'returns a reverse-aliased attributes hash' do
+ subject.class_eval do
+ identity :id
+ attribute :name, alias: 'sample_name'
+ end
+
+ model = subject.new(name: 'steve', id: 1)
+
+ expect(model.request_attributes).to eq(
+ 'sample_name' => 'steve',
+ 'id' => 1
+ )
end
+ it 'drops duplicates values for multiple aliases' do
+ subject.class_eval do
+ identity :id
+ attribute :name, aliases: ['sample_name', 'other_name']
+ end
+
+ model = subject.new(name: 'steve', id: 1)
+
+ expect(model.request_attributes).to eq(
+ 'sample_name' => 'steve',
+ 'other_name' => 'steve',
+ 'id' => 1
+ )
+ end
+end
+
+describe Cistern::Attributes, '#dirty_request_attributes' do
+ subject { Class.new(Sample::Model) }
+
+ it 'returns a reverse-aliased attributes hash of dirty attributes only' do
+ subject.class_eval do
+ identity :id
+ attribute :name, alias: 'sample_name'
+ end
+
+ model = subject.new
+ model.merge_attributes(name: 'steve', id: 1)
+
+ model.name = 'bob'
+
+ expect(model.dirty_request_attributes).to eq(
+ 'sample_name' => 'bob',
+ )
+ end
+
+ it 'drops duplicates values for multiple aliases' do
+ subject.class_eval do
+ identity :id
+ attribute :name, aliases: ['sample_name', 'other_name']
+ end
+
+ model = subject.new(name: 'steve', id: 1)
+
+ expect(model.request_attributes).to eq(
+ 'sample_name' => 'steve',
+ 'other_name' => 'steve',
+ 'id' => 1
+ )
+ end
+end
+
+describe Cistern::Attributes, 'requires' do
+ subject {
+ Class.new(Sample::Model) do
+ identity :id
+ attribute :name, type: :string
+ attribute :type
+ end
+ }
+
it 'raises if required attributes are not present' do
expect {
- Sample.new.require_spec.requires :name
+ subject.new.requires :name
}.to raise_exception(ArgumentError, /name is required/)
data = { name: '1' }
- return_value = Sample.new.require_spec(data).requires :name
+ return_value = subject.new(data).requires :name
expect(return_value).to eq(data)
expect {
- Sample.new.require_spec.requires :name, :type
+ subject.new.requires :name, :type
}.to raise_exception(ArgumentError, /name and type are required/)
data = { name: '1', type: 'sample' }
- return_values = Sample.new.require_spec(data).requires :name, :type
+ return_values = subject.new(data).requires :name, :type
expect(return_values).to eq(data)
end
it 'raises if a required attribute attribute is not present' do
expect {
- Sample.new.require_spec.requires_one :name, :type
+ subject.new.requires_one :name, :type
}.to raise_exception(ArgumentError, /name or type are required/)
data = { name: '1' }
- return_value = Sample.new.require_spec(data).requires_one :name, :type
+ return_value = subject.new(data).requires_one :name, :type
expect(return_value).to eq(data)
data = { name: '1', type: 'sample' }
- return_values = Sample.new.require_spec(data).requires_one :name, :type
+ return_values = subject.new(data).requires_one :name, :type
expect(return_values).to eq(data)
end
end
describe Cistern::Attributes, 'parsing' do
- class TypeSpec < Sample::Model
- identity :id
- attribute :name, type: :string
- attribute :created_at, type: :time
- attribute :flag, type: :boolean
- attribute :list, type: :array
- attribute :number, type: :integer
- attribute :floater, type: :float
- attribute :butternut_id, squash: %w(squash id), type: :integer
- attribute :butternut_type, squash: %w(squash type)
- attribute :squash
- attribute :vegetable, aliases: 'squash'
- attribute :custom, parser: lambda { |v, _| "X!#{v}" }
- attribute :default, default: 'im a squash'
- attribute :string_allow_nil, type: :string, allow_nil: true
+ subject {
+ Class.new(Sample::Model)
+ }
- attribute :same_alias_1, aliases: 'nested'
- attribute :same_alias_2, aliases: 'nested'
-
- attribute :same_alias_squashed_1, squash: %w(nested attr_1)
- attribute :same_alias_squashed_2, squash: %w(nested attr_2)
- attribute :same_alias_squashed_3, squash: %w(nested attr_2)
- attribute :adam_attributes, aliases: 'attributes'
-
- def save
- requires :flag
+ it 'should parse string' do
+ subject.class_eval do
+ attribute :name, type: :string
end
- end
- it 'should parse string' do
- expect(TypeSpec.new(name: 1).name).to eq('1')
- expect(TypeSpec.new(name: "b").name).to eq('b')
- expect(TypeSpec.new(name: nil).name).to eq(nil)
+ expect(subject.new(name: 1).name).to eq('1')
+ expect(subject.new(name: "b").name).to eq('b')
+ expect(subject.new(name: nil).name).to eq(nil)
end
- it 'should allow nils in string types' do
- expect(TypeSpec.new(string_allow_nil: nil).string_allow_nil).to eq(nil)
- end
it "should handle a 'attributes' aliased attribute" do
- expect(TypeSpec.new(attributes: 'x').adam_attributes).to eq('x')
+ subject.class_eval do
+ attribute :adam_attributes, aliases: 'attributes'
+ end
+ expect(subject.new(attributes: 'x').adam_attributes).to eq('x')
end
it 'should parse time' do
+ subject.class_eval do
+ attribute :created_at, type: :time
+ end
+
time = Time.now
- created_at = TypeSpec.new(created_at: time.to_s).created_at
+ created_at = subject.new(created_at: time.to_s).created_at
expect(created_at).to be_a(Time)
expect(created_at.to_i).to eq(time.to_i)
end
it 'should parse boolean' do
- expect(TypeSpec.new(flag: 'false').flag).to be_falsey
- expect(TypeSpec.new(flag: 'true').flag).to be_truthy
- expect(TypeSpec.new(flag: false).flag).to be_falsey
- expect(TypeSpec.new(flag: true).flag).to be_truthy
- expect(TypeSpec.new(flag: '0').flag).to be_falsey
- expect(TypeSpec.new(flag: '1').flag).to be_truthy
- expect(TypeSpec.new(flag: 0).flag).to be_falsey
- expect(TypeSpec.new(flag: 1).flag).to be_truthy
- expect(TypeSpec.new(flag: false)).not_to be_flag
- expect(TypeSpec.new(flag: true)).to be_flag
+ subject.class_eval do
+ attribute :flag, type: :boolean
+ end
+
+ ['false', false, '0', 0].each do |falsey|
+ expect(subject.new(flag: falsey).flag).to be_falsey
+ end
+
+ ['true', true, '1', 1].each do |truthy|
+ expect(subject.new(flag: truthy).flag).to be_truthy
+ end
end
it 'should parse an array' do
- expect(TypeSpec.new(list: []).list).to eq([])
- expect(TypeSpec.new(list: 'item').list).to eq(['item'])
+ subject.class_eval do
+ attribute :list, type: :array
+ end
+
+ expect(subject.new(list: []).list).to eq([])
+ expect(subject.new(list: 'item').list).to eq(['item'])
end
it 'should parse a float' do
- expect(TypeSpec.new(floater: '0.01').floater).to eq(0.01)
- expect(TypeSpec.new(floater: 0.01).floater).to eq(0.01)
+ subject.class_eval do
+ attribute :floater, type: :float
+ end
+
+ expect(subject.new(floater: '0.01').floater).to eq(0.01)
+ expect(subject.new(floater: 0.01).floater).to eq(0.01)
end
it 'should use custom parser' do
- expect(TypeSpec.new(custom: '15').custom).to eq('X!15')
+ subject.class_eval do
+ attribute :custom, parser: lambda { |v, _| "X!#{v}" }
+ end
+
+ expect(subject.new(custom: '15').custom).to eq('X!15')
end
- it 'should squash, cast, alias an attribute and keep a vanilla reference' do
+ it 'squashes, casts and aliases an attribute and keeps a vanilla reference' do
+ subject.class_eval do
+ attribute :butternut_id, squash: %w(squash id), type: :integer
+ attribute :butternut_type, squash: %w(squash type)
+ attribute :squash
+ attribute :vegetable, aliases: 'squash'
+ end
+
# vanilla squash
- expect(TypeSpec.new({ 'squash' => { 'id' => '12', 'type' => 'fred' } }).butternut_type).to eq('fred')
- expect(TypeSpec.new({ 'squash' => { 'id' => '12', 'type' => nil } }).butternut_type).to be_nil
- expect(TypeSpec.new({ 'squash' => nil }).butternut_type).to be_nil
+ expect(subject.new({ 'squash' => { 'id' => '12', 'type' => 'fred' } }).butternut_type).to eq('fred')
+ expect(subject.new({ 'squash' => { 'id' => '12', 'type' => nil } }).butternut_type).to be_nil
+ expect(subject.new({ 'squash' => nil }).butternut_type).to be_nil
# composite processors: squash and cast
- expect(TypeSpec.new({ 'squash' => { 'id' => '12', 'type' => 'fred' } }).butternut_id).to eq(12)
- expect(TypeSpec.new({ 'squash' => { 'id' => nil, 'type' => 'fred' } }).butternut_id).to be_nil
- expect(TypeSpec.new({ 'squash' => { 'type' => 'fred' } }).butternut_id).to be_nil
+ expect(subject.new({ 'squash' => { 'id' => '12', 'type' => 'fred' } }).butternut_id).to eq(12)
+ expect(subject.new({ 'squash' => { 'id' => nil, 'type' => 'fred' } }).butternut_id).to be_nil
+ expect(subject.new({ 'squash' => { 'type' => 'fred' } }).butternut_id).to be_nil
# override intermediate processing
- expect(TypeSpec.new({ 'squash' => { 'id' => '12', 'type' => 'fred' } }).squash).to eq({ 'id' => '12', 'type' => 'fred' })
+ expect(subject.new({ 'squash' => { 'id' => '12', 'type' => 'fred' } }).squash).to eq({ 'id' => '12', 'type' => 'fred' })
# alias of override
- expect(TypeSpec.new({ 'squash' => { 'id' => '12', 'type' => 'fred' } }).vegetable).to eq({ 'id' => '12', 'type' => 'fred' })
+ expect(subject.new({ 'squash' => { 'id' => '12', 'type' => 'fred' } }).vegetable).to eq({ 'id' => '12', 'type' => 'fred' })
end
- it 'should set a default value' do
- expect(TypeSpec.new.default).to eq('im a squash')
+ it 'sets a default value' do
+ subject.class_eval do
+ attribute :default, default: 'im a squash'
+ end
+
+ expect(subject.new.default).to eq('im a squash')
end
it 'should override a default value' do
- expect(TypeSpec.new(default: 'now im a different squash').default).to eq('now im a different squash')
+ subject.class_eval do
+ attribute :default, default: 'im a squash'
+ end
+
+ expect(subject.new(default: 'now im a different squash').default).to eq('now im a different squash')
end
context 'allowing the same alias for multiple attributes' do
+ before {
+ subject.class_eval do
+ attribute :same_alias_1, aliases: 'nested'
+ attribute :same_alias_2, aliases: 'nested'
+
+ attribute :same_alias_squashed_1, squash: %w(nested attr_1)
+ attribute :same_alias_squashed_2, squash: %w(nested attr_2)
+ attribute :same_alias_squashed_3, squash: %w(nested attr_2)
+ end
+ }
+
it 'should do so when not squashing' do
- type_spec = TypeSpec.new({ 'nested' => 'bamboo' })
- expect(type_spec.same_alias_1).to eq('bamboo')
- expect(type_spec.same_alias_2).to eq('bamboo')
+ model = subject.new('nested' => 'bamboo')
+
+ expect(model.same_alias_1).to eq('bamboo')
+ expect(model.same_alias_2).to eq('bamboo')
end
it 'should do so when squashing' do
- type_spec = TypeSpec.new({ 'nested' => { 'attr_1' => 'bamboo', 'attr_2' => 'panda' } })
- expect(type_spec.same_alias_squashed_1).to eq('bamboo')
- expect(type_spec.same_alias_squashed_2).to eq('panda')
- expect(type_spec.same_alias_squashed_3).to eq('panda')
+ model = subject.new('nested' => { 'attr_1' => 'bamboo', 'attr_2' => 'panda' })
+
+ expect(model.same_alias_squashed_1).to eq('bamboo')
+ expect(model.same_alias_squashed_2).to eq('panda')
+ expect(model.same_alias_squashed_3).to eq('panda')
end
end
it 'should slice out unaccounted for attributes' do
- expect(TypeSpec.new({ 'something' => { 'id' => '12' } }).attributes.keys).not_to include('something')
+ expect(subject.new({ 'something' => { 'id' => '12' } }).attributes.keys).not_to include('something')
end
end