smooth_operator 0.4.4 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +9 -9
- data/.gitignore +2 -1
- data/.rspec +4 -0
- data/Gemfile +13 -0
- data/README.md +258 -10
- data/console.rb +44 -0
- data/lib/smooth_operator/array_with_meta_data.rb +31 -0
- data/lib/smooth_operator/attribute_assignment.rb +102 -0
- data/lib/smooth_operator/attribute_methods.rb +87 -0
- data/lib/smooth_operator/attributes/base.rb +107 -0
- data/lib/smooth_operator/attributes/dirty.rb +29 -0
- data/lib/smooth_operator/attributes/normal.rb +15 -0
- data/lib/smooth_operator/delegation.rb +60 -0
- data/lib/smooth_operator/finder_methods.rb +43 -0
- data/lib/smooth_operator/helpers.rb +79 -0
- data/lib/smooth_operator/model_schema.rb +81 -0
- data/lib/smooth_operator/open_struct.rb +37 -0
- data/lib/smooth_operator/operator.rb +145 -0
- data/lib/smooth_operator/operators/faraday.rb +75 -0
- data/lib/smooth_operator/operators/typhoeus.rb +77 -0
- data/lib/smooth_operator/persistence.rb +144 -0
- data/lib/smooth_operator/relation/array_relation.rb +13 -0
- data/lib/smooth_operator/relation/association_reflection.rb +75 -0
- data/lib/smooth_operator/relation/associations.rb +75 -0
- data/lib/smooth_operator/relation/reflection.rb +41 -0
- data/lib/smooth_operator/relation/single_relation.rb +14 -0
- data/lib/smooth_operator/remote_call/base.rb +80 -0
- data/lib/smooth_operator/remote_call/errors/connection_failed.rb +20 -0
- data/lib/smooth_operator/remote_call/errors/timeout.rb +20 -0
- data/lib/smooth_operator/remote_call/faraday.rb +19 -0
- data/lib/smooth_operator/remote_call/typhoeus.rb +19 -0
- data/lib/smooth_operator/serialization.rb +79 -0
- data/lib/smooth_operator/translation.rb +27 -0
- data/lib/smooth_operator/validations.rb +15 -0
- data/lib/smooth_operator/version.rb +1 -1
- data/lib/smooth_operator.rb +26 -5
- data/smooth_operator.gemspec +12 -3
- data/spec/factories/user_factory.rb +34 -0
- data/spec/require_helper.rb +11 -0
- data/spec/smooth_operator/attribute_assignment_spec.rb +351 -0
- data/spec/smooth_operator/attributes_dirty_spec.rb +53 -0
- data/spec/smooth_operator/delegation_spec.rb +139 -0
- data/spec/smooth_operator/finder_methods_spec.rb +105 -0
- data/spec/smooth_operator/model_schema_spec.rb +31 -0
- data/spec/smooth_operator/operator_spec.rb +46 -0
- data/spec/smooth_operator/persistence_spec.rb +424 -0
- data/spec/smooth_operator/remote_call_spec.rb +320 -0
- data/spec/smooth_operator/serialization_spec.rb +80 -0
- data/spec/smooth_operator/validations_spec.rb +42 -0
- data/spec/spec_helper.rb +25 -0
- data/spec/support/helpers/persistence_helper.rb +38 -0
- data/spec/support/localhost_server.rb +97 -0
- data/spec/support/models/address.rb +14 -0
- data/spec/support/models/comment.rb +3 -0
- data/spec/support/models/post.rb +13 -0
- data/spec/support/models/user.rb +41 -0
- data/spec/support/models/user_with_address_and_posts.rb +89 -0
- data/spec/support/test_server.rb +165 -0
- metadata +108 -18
- data/lib/smooth_operator/base.rb +0 -30
- data/lib/smooth_operator/core.rb +0 -218
- data/lib/smooth_operator/http_handlers/typhoeus/base.rb +0 -58
- data/lib/smooth_operator/http_handlers/typhoeus/orm.rb +0 -34
- data/lib/smooth_operator/http_handlers/typhoeus/remote_call.rb +0 -28
- data/lib/smooth_operator/operator/base.rb +0 -43
- data/lib/smooth_operator/operator/exceptions.rb +0 -64
- data/lib/smooth_operator/operator/orm.rb +0 -118
- data/lib/smooth_operator/operator/remote_call.rb +0 -84
@@ -0,0 +1,27 @@
|
|
1
|
+
module SmoothOperator
|
2
|
+
|
3
|
+
module Translation
|
4
|
+
|
5
|
+
def human_attribute_name(attribute_key_name, options = {})
|
6
|
+
_translate("attributes.#{model_name.i18n_key}.#{attribute_key_name}", options = {})
|
7
|
+
end
|
8
|
+
|
9
|
+
|
10
|
+
private ###################### PRIVATE #########################
|
11
|
+
|
12
|
+
def _translate(namespace = '', options = {})
|
13
|
+
no_translation = "-- no translation --"
|
14
|
+
|
15
|
+
defaults = ["smooth_operator.#{namespace}".to_sym]
|
16
|
+
defaults << "activerecord.#{namespace}".to_sym
|
17
|
+
defaults << options[:default] if options[:default]
|
18
|
+
defaults.flatten!
|
19
|
+
defaults << no_translation
|
20
|
+
|
21
|
+
options = { count: 1, default: defaults }.merge!(options.except(:default))
|
22
|
+
I18n.translate(defaults.shift, options)
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
data/lib/smooth_operator.rb
CHANGED
@@ -1,7 +1,28 @@
|
|
1
|
+
require "smooth_operator/version"
|
2
|
+
require "smooth_operator/helpers"
|
3
|
+
require "smooth_operator/operator"
|
4
|
+
require "smooth_operator/persistence"
|
5
|
+
require "smooth_operator/translation"
|
6
|
+
require "smooth_operator/open_struct"
|
7
|
+
require "smooth_operator/finder_methods"
|
8
|
+
require "smooth_operator/relation/associations"
|
9
|
+
|
1
10
|
module SmoothOperator
|
2
|
-
|
3
|
-
end
|
11
|
+
class Base < OpenStruct::Base
|
4
12
|
|
5
|
-
|
6
|
-
|
7
|
-
|
13
|
+
extend FinderMethods
|
14
|
+
extend Translation if defined? I18n
|
15
|
+
|
16
|
+
include Operator
|
17
|
+
include Persistence
|
18
|
+
include FinderMethods
|
19
|
+
include Relation::Associations
|
20
|
+
|
21
|
+
self.strict_behaviour = true
|
22
|
+
|
23
|
+
def self.smooth_operator?
|
24
|
+
true
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
data/smooth_operator.gemspec
CHANGED
@@ -1,6 +1,9 @@
|
|
1
1
|
# coding: utf-8
|
2
|
+
|
2
3
|
lib = File.expand_path('../lib', __FILE__)
|
4
|
+
|
3
5
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
6
|
+
|
4
7
|
require 'smooth_operator/version'
|
5
8
|
|
6
9
|
Gem::Specification.new do |spec|
|
@@ -9,7 +12,7 @@ Gem::Specification.new do |spec|
|
|
9
12
|
spec.authors = ["João Gonçalves"]
|
10
13
|
spec.email = ["goncalves.joao@gmail.com"]
|
11
14
|
spec.description = %q{ActiveResource alternative}
|
12
|
-
spec.summary = %q{Simple and fully customizable alternative to ActiveResource,
|
15
|
+
spec.summary = %q{Simple and fully customizable alternative to ActiveResource, using faraday gem to stablish remote calls"}
|
13
16
|
spec.homepage = "https://github.com/goncalvesjoao/smooth_operator"
|
14
17
|
spec.license = "MIT"
|
15
18
|
|
@@ -17,7 +20,13 @@ Gem::Specification.new do |spec|
|
|
17
20
|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
21
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
22
|
spec.require_paths = ["lib"]
|
20
|
-
|
23
|
+
|
21
24
|
spec.add_development_dependency "bundler", "~> 1.3"
|
22
|
-
|
25
|
+
|
26
|
+
spec.add_dependency "json"
|
27
|
+
spec.add_dependency "faraday", "~> 0.8.9"
|
28
|
+
spec.add_dependency "typhoeus", "~> 0.6.8"
|
29
|
+
|
30
|
+
# this is necessary if you want to typhoeus to correctly encode arrays
|
31
|
+
# spec.add_dependency "ethon", :git => 'https://github.com/goncalvesjoao/ethon'
|
23
32
|
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
FactoryGirl.define do
|
2
|
+
|
3
|
+
factory :user, class: User::Base do
|
4
|
+
|
5
|
+
id 1
|
6
|
+
admin true
|
7
|
+
last_name 'Doe'
|
8
|
+
first_name 'John'
|
9
|
+
|
10
|
+
trait :with_address_and_posts do
|
11
|
+
address { { street: 'my_street' } }
|
12
|
+
# posts [{ id: 1, body: 'post1' }, { id: 2, body: 'post2' }]
|
13
|
+
posts [{ id: 1 }, { id: 2 }]
|
14
|
+
end
|
15
|
+
factory :user_with_address_and_posts, traits: [:with_address_and_posts]
|
16
|
+
|
17
|
+
trait :has_my_method do
|
18
|
+
my_method 'my_method'
|
19
|
+
end
|
20
|
+
factory :user_with_my_method, traits: [:with_address_and_posts, :has_my_method]
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
factory :white_list, class: User::Base do
|
25
|
+
id 1
|
26
|
+
first_name 'John'
|
27
|
+
end
|
28
|
+
|
29
|
+
factory :black_list, class: User::Base do
|
30
|
+
admin true
|
31
|
+
last_name 'Doe'
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
2
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), "..", "lib"))
|
3
|
+
|
4
|
+
require 'bundler/setup'
|
5
|
+
require 'smooth_operator'
|
6
|
+
|
7
|
+
Bundler.require :test, :default
|
8
|
+
|
9
|
+
Dir.chdir("spec/") do
|
10
|
+
Dir["support/**/*.rb"].each { |file| require file }
|
11
|
+
end
|
@@ -0,0 +1,351 @@
|
|
1
|
+
require 'date'
|
2
|
+
require "spec_helper"
|
3
|
+
|
4
|
+
describe SmoothOperator::AttributeAssignment do
|
5
|
+
|
6
|
+
describe "#assign_attributes" do
|
7
|
+
|
8
|
+
describe "receiving data from server" do
|
9
|
+
subject { User::Base.new }
|
10
|
+
|
11
|
+
context "when receiving the option 'from_server = true'" do
|
12
|
+
before { subject.assign_attributes({}, from_server: true) }
|
13
|
+
|
14
|
+
it "#has_data_from_server and #from_server should return true" do
|
15
|
+
expect(subject.has_data_from_server).to be true
|
16
|
+
expect(subject.from_server).to be true
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
context "when receiving a Hash with meta_data on it" do
|
21
|
+
before { subject.assign_attributes({ user: attributes_for(:user), status: 1 }) }
|
22
|
+
|
23
|
+
it "#meta_data should reflect the receiving meta_data" do
|
24
|
+
expect(subject._meta_data).to eq({ "status" => 1 })
|
25
|
+
end
|
26
|
+
|
27
|
+
it "subject should NOT contain meta_data" do
|
28
|
+
expect{ subject.status }.to raise_error NoMethodError
|
29
|
+
end
|
30
|
+
|
31
|
+
it "subject should contain all other data" do
|
32
|
+
expect(subject.attributes).to eq(attributes_for(:user))
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
describe "white and black list" do
|
39
|
+
subject { UserWithAddressAndPosts::Son.new(attributes_for(:user_with_address_and_posts)) }
|
40
|
+
|
41
|
+
context "when there are no changes to attributes's white and black list" do
|
42
|
+
it 'it should return all attributes' do
|
43
|
+
expect(subject.to_hash).to eq(attributes_for(:user_with_address_and_posts))
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
context "when there are changes to attributes's white list" do
|
48
|
+
subject(:user_white_listed) { UserWithAddressAndPosts::UserWhiteListed::Son.new(attributes_for(:user_with_address_and_posts)) }
|
49
|
+
|
50
|
+
it 'it should return only the white listed' do
|
51
|
+
expect(user_white_listed.to_hash).to eq(attributes_for(:white_list))
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
context "when there are changes to attributes's black list" do
|
56
|
+
subject(:user_black_listed) { UserWithAddressAndPosts::UserBlackListed::Son.new(attributes_for(:user_with_address_and_posts)) }
|
57
|
+
|
58
|
+
it 'it should not return the black listed' do
|
59
|
+
expect(user_black_listed.to_hash).not_to include(attributes_for(:black_list))
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
context "when something other than a hash is introduced" do
|
65
|
+
it "should do nothing" do
|
66
|
+
[nil, '', [1, 2], 'test', 1, 2].each do |something_other_than_a_hash|
|
67
|
+
expect(User::Base.new(something_other_than_a_hash).internal_data).to eq({})
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
context "when one of the attribute's value, is an hash and is unknown to the schema" do
|
73
|
+
context "when the .unknown_hash_class is unused", current: true do
|
74
|
+
subject { User::Base.new(address: { street: 'something', postal_code: { code: '123' } }) }
|
75
|
+
|
76
|
+
it "a new instance of OpenStruct will be initialized with that hash" do
|
77
|
+
address = subject.address
|
78
|
+
|
79
|
+
expect(address).to be_instance_of(OpenStruct)
|
80
|
+
expect(address.street).to eq('something')
|
81
|
+
|
82
|
+
expect(address.postal_code).to be_instance_of(OpenStruct)
|
83
|
+
expect(address.postal_code.code).to eq('123')
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
context "when the .unknown_hash_class is set to SmoothOperator::OpenStruct::Base" do
|
88
|
+
subject { User::UnknownHashClass::OpenStructBase.new(address: { street: 'something', postal_code: { code: '123' } }) }
|
89
|
+
|
90
|
+
it "a new instance of SmoothOperator::OpenStruct::Base will be initialized with that hash" do
|
91
|
+
address = subject.address
|
92
|
+
|
93
|
+
expect(address).to be_instance_of(SmoothOperator::OpenStruct::Base)
|
94
|
+
expect(address.street).to eq('something')
|
95
|
+
|
96
|
+
expect(address.postal_code).to be_instance_of(SmoothOperator::OpenStruct::Base)
|
97
|
+
expect(address.postal_code.code).to eq('123')
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
context "when the .unknown_hash_class is set to :none" do
|
102
|
+
subject { User::UnknownHashClass::None.new(creator: { first_name: 'admin', address: { street: 'something' } }) }
|
103
|
+
|
104
|
+
it "the hash will be copied as it is" do
|
105
|
+
creator = subject.creator
|
106
|
+
|
107
|
+
expect(creator).to be_instance_of(Hash)
|
108
|
+
expect(creator[:first_name]).to eq('admin')
|
109
|
+
|
110
|
+
expect(creator[:address]).to be_instance_of(Hash)
|
111
|
+
expect(creator[:address][:street]).to eq('something')
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
context "when there is no declared schema" do
|
117
|
+
subject { User::Base.new(attributes_for(:user)) }
|
118
|
+
let(:expected_internal_data) { SmoothOperator::Helpers.stringify_keys(attributes_for(:user)) }
|
119
|
+
|
120
|
+
it "it should populate 'internal_data' with unaltered duplicate data from the received hash" do
|
121
|
+
expect(subject.to_hash).to eq(attributes_for(:user))
|
122
|
+
end
|
123
|
+
|
124
|
+
it "it should populate 'known_attributes' with the keys of the received hash" do
|
125
|
+
expect(subject.known_attributes.to_a).to match_array(expected_internal_data.keys)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
context "when there is a known schema and the received hash has an attribute" do
|
130
|
+
subject { UserWithAddressAndPosts::Son }
|
131
|
+
|
132
|
+
context "that is declared (in schema) as an nil" do
|
133
|
+
|
134
|
+
it "when the attributes's value is '1', should return '1'" do
|
135
|
+
expect(subject.new(complex_field: '1').complex_field).to eq('1')
|
136
|
+
end
|
137
|
+
|
138
|
+
it "when the attributes's value is ['1', '2'], should return ['1', '2']" do
|
139
|
+
expect(subject.new(complex_field: ['1', '2']).complex_field).to eq(['1', '2'])
|
140
|
+
end
|
141
|
+
|
142
|
+
it "when the attributes's value is 1, should be converted to 1" do
|
143
|
+
expect(subject.new(complex_field: 1).complex_field).to eq(1)
|
144
|
+
end
|
145
|
+
|
146
|
+
it "when the attributes's value is { first_name: ['1', '2'] }, should be converted to { first_name: ['1', '2'] }" do
|
147
|
+
expect(subject.new(complex_field: { first_name: ['1', '2'] }).complex_field).to eq({ first_name: ['1', '2'] })
|
148
|
+
end
|
149
|
+
|
150
|
+
it "when the attributes's value is -1, should be converted to -1" do
|
151
|
+
expect(subject.new(complex_field: -1).complex_field).to eq(-1)
|
152
|
+
end
|
153
|
+
|
154
|
+
it "when the attributes's value is 0.35, should be converted to 0.35" do
|
155
|
+
expect(subject.new(complex_field: 0.35).complex_field).to eq(0.35)
|
156
|
+
end
|
157
|
+
|
158
|
+
end
|
159
|
+
|
160
|
+
context "that is declared (in schema) as an integer" do
|
161
|
+
|
162
|
+
it "when the attributes's value is '1', should be converted to 1" do
|
163
|
+
expect(subject.new(age: '1').age).to be(1)
|
164
|
+
end
|
165
|
+
|
166
|
+
it "when the attributes's value is '-1', should be converted to -1" do
|
167
|
+
expect(subject.new(age: '-1').age).to be(-1)
|
168
|
+
end
|
169
|
+
|
170
|
+
it "when the attributes's value is 's-10s', should be converted to -10" do
|
171
|
+
expect(subject.new(age: 's-10s').age).to be(-10)
|
172
|
+
end
|
173
|
+
|
174
|
+
it "when the attributes's value is ' 10s', should be converted to 10" do
|
175
|
+
expect(subject.new(age: ' 10s').age).to be(10)
|
176
|
+
end
|
177
|
+
|
178
|
+
it "when the attributes's value is 123, should be converted to 123" do
|
179
|
+
expect(subject.new(age: 123).age).to be(123)
|
180
|
+
end
|
181
|
+
|
182
|
+
it "when the attributes's value is -5, should be converted to -5" do
|
183
|
+
expect(subject.new(age: -5).age).to be(-5)
|
184
|
+
end
|
185
|
+
|
186
|
+
end
|
187
|
+
|
188
|
+
context "that is declared (in schema) as an float" do
|
189
|
+
|
190
|
+
it "when the attributes's value is '1', should be converted to 1" do
|
191
|
+
expect(subject.new(price: '1').price).to eq(1.0)
|
192
|
+
end
|
193
|
+
|
194
|
+
it "when the attributes's value is '-1', should be converted to -1" do
|
195
|
+
expect(subject.new(price: '-1').price).to eq(-1.0)
|
196
|
+
end
|
197
|
+
|
198
|
+
it "when the attributes's value is 's-10s', should be converted to -10" do
|
199
|
+
expect(subject.new(price: 's-10s').price).to eq(-10.0)
|
200
|
+
end
|
201
|
+
|
202
|
+
it "when the attributes's value is ' 10s', should be converted to 10" do
|
203
|
+
expect(subject.new(price: ' 10s').price).to eq(10.0)
|
204
|
+
end
|
205
|
+
|
206
|
+
it "when the attributes's value is 123, should be converted to 123" do
|
207
|
+
expect(subject.new(price: 123).price).to eq(123.0)
|
208
|
+
end
|
209
|
+
|
210
|
+
it "when the attributes's value is -5, should be converted to -5" do
|
211
|
+
expect(subject.new(price: -5).price).to eq(-5.0)
|
212
|
+
end
|
213
|
+
|
214
|
+
it "when the attributes's value is '12.3', should be converted to 12.3" do
|
215
|
+
expect(subject.new(price: '12.3').price).to eq(12.3)
|
216
|
+
end
|
217
|
+
|
218
|
+
it "when the attributes's value is 's12.3s', should be converted to 12.3" do
|
219
|
+
expect(subject.new(price: 's12.3s').price).to eq(12.3)
|
220
|
+
end
|
221
|
+
|
222
|
+
it "when the attributes's value is 's12,3s', should be converted to 12.3" do
|
223
|
+
expect(subject.new(price: 's12,3s').price).to eq(12.3)
|
224
|
+
end
|
225
|
+
|
226
|
+
it "when the attributes's value is 1.2, should be converted to 1.2" do
|
227
|
+
expect(subject.new(price: 1.2).price).to eq(1.2)
|
228
|
+
end
|
229
|
+
|
230
|
+
end
|
231
|
+
|
232
|
+
context "that is declared (in schema) as an boolean" do
|
233
|
+
|
234
|
+
it "when the attributes's value is true, should be converted to true" do
|
235
|
+
expect(subject.new(manager: true).manager).to be(true)
|
236
|
+
end
|
237
|
+
|
238
|
+
it "when the attributes's value is false, should be converted to false" do
|
239
|
+
expect(subject.new(manager: false).manager).to be(false)
|
240
|
+
end
|
241
|
+
|
242
|
+
it "when the attributes's value is 'true', should be converted to true" do
|
243
|
+
expect(subject.new(manager: 'true').manager).to be(true)
|
244
|
+
end
|
245
|
+
|
246
|
+
it "when the attributes's value is 'false', should be converted to false" do
|
247
|
+
expect(subject.new(manager: 'false').manager).to be(false)
|
248
|
+
end
|
249
|
+
|
250
|
+
it "when the attributes's value is '1', should be converted to true" do
|
251
|
+
expect(subject.new(manager: '1').manager).to be(true)
|
252
|
+
end
|
253
|
+
|
254
|
+
it "when the attributes's value is '0', should be converted to false" do
|
255
|
+
expect(subject.new(manager: '0').manager).to be(false)
|
256
|
+
end
|
257
|
+
|
258
|
+
it "when the attributes's value is '', should be converted to nil" do
|
259
|
+
expect(subject.new(manager: '').manager).to be_nil
|
260
|
+
end
|
261
|
+
|
262
|
+
it "when the attributes's value is 'something', should be converted to nil" do
|
263
|
+
expect(subject.new(manager: 'something').manager).to be_nil
|
264
|
+
end
|
265
|
+
|
266
|
+
end
|
267
|
+
|
268
|
+
context "that is declared (in schema) as an existing class" do
|
269
|
+
|
270
|
+
it "if the attribute's value is an hash a new instance of that class will be initialized with that hash" do
|
271
|
+
address = subject.new(address: { street: 'something' }).address
|
272
|
+
|
273
|
+
expect(address).to be_instance_of(Address)
|
274
|
+
expect(address.street).to eq('something')
|
275
|
+
end
|
276
|
+
|
277
|
+
it "if the attribute's value is not an hash, then that value will be simply cloned" do
|
278
|
+
expect(subject.new(address: 'something').address).to eq('something')
|
279
|
+
end
|
280
|
+
|
281
|
+
it "if the attribute's value is an array, a new instance of that class will be initialized for each array entry" do
|
282
|
+
posts = subject.new(posts: [{ body: 'post1' }, { body: 'post2' }]).posts
|
283
|
+
|
284
|
+
expect(posts.length).to be(2)
|
285
|
+
|
286
|
+
expect(posts[0]).to be_instance_of(Post)
|
287
|
+
expect(posts[0].body).to eq('post1')
|
288
|
+
|
289
|
+
expect(posts[1]).to be_instance_of(Post)
|
290
|
+
expect(posts[1].body).to eq('post2')
|
291
|
+
end
|
292
|
+
|
293
|
+
end
|
294
|
+
|
295
|
+
context "that is declared (in schema) as a date" do
|
296
|
+
|
297
|
+
it "if the attribute's value is a valid date string" do
|
298
|
+
dob = subject.new(dob: '2-2-2222').dob
|
299
|
+
|
300
|
+
expect(dob).to be_instance_of(Date)
|
301
|
+
expect(dob.day).to be(2)
|
302
|
+
expect(dob.month).to be(2)
|
303
|
+
expect(dob.year).to be(2222)
|
304
|
+
end
|
305
|
+
|
306
|
+
it "if the attribute's value is a valid date" do
|
307
|
+
date_now = DateTime.now
|
308
|
+
dob = subject.new(dob: date_now).dob
|
309
|
+
|
310
|
+
expect(dob).to be_instance_of(DateTime)
|
311
|
+
expect(dob).to eq(date_now)
|
312
|
+
end
|
313
|
+
|
314
|
+
it "if the attribute's value is an invalid date string, the returning value should be nil" do
|
315
|
+
expect(subject.new(dob: '2s-2-2222').dob).to be_nil
|
316
|
+
end
|
317
|
+
|
318
|
+
end
|
319
|
+
|
320
|
+
context "that is declared (in schema) as a datetime" do
|
321
|
+
|
322
|
+
it "if the attribute's value is a valid datetime string" do
|
323
|
+
date = subject.new(date: '2-2-2222 12:30').date
|
324
|
+
|
325
|
+
expect(date).to be_instance_of(DateTime)
|
326
|
+
expect(date.day).to be(2)
|
327
|
+
expect(date.month).to be(2)
|
328
|
+
expect(date.year).to be(2222)
|
329
|
+
expect(date.hour).to be(12)
|
330
|
+
expect(date.min).to be(30)
|
331
|
+
end
|
332
|
+
|
333
|
+
it "if the attribute's value is a valid datetime" do
|
334
|
+
date_now = DateTime.now
|
335
|
+
date = subject.new(date: date_now).date
|
336
|
+
|
337
|
+
expect(date).to be_instance_of(DateTime)
|
338
|
+
expect(date).to eq(date_now)
|
339
|
+
end
|
340
|
+
|
341
|
+
it "if the attribute's value is an invalid datetime string, the returning value should be nil" do
|
342
|
+
expect(subject.new(date: '2s-2-2222').date).to be_nil
|
343
|
+
end
|
344
|
+
|
345
|
+
end
|
346
|
+
|
347
|
+
end
|
348
|
+
|
349
|
+
end
|
350
|
+
|
351
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe SmoothOperator::Attributes::Dirty do
|
4
|
+
|
5
|
+
subject { UserWithAddressAndPosts::DirtyAttributes.new(attributes_for(:user_with_address_and_posts)) }
|
6
|
+
|
7
|
+
context "when no changes are made to an attribute" do
|
8
|
+
it "checking if that attribute is changed, should return false" do
|
9
|
+
expect(subject.first_name_changed?).to be false
|
10
|
+
end
|
11
|
+
|
12
|
+
it "checking that attribute past value, should its original value" do
|
13
|
+
expect(subject.first_name_was).to eq('John')
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
context "when there are changes made to an attribute" do
|
18
|
+
before { subject.first_name = 'nhoJ' }
|
19
|
+
|
20
|
+
it "checking if that attribute is changed, should return true" do
|
21
|
+
expect(subject.first_name_changed?).to be true
|
22
|
+
end
|
23
|
+
|
24
|
+
it "checking that attribute past value, should its original value" do
|
25
|
+
expect(subject.first_name_was).to eq('John')
|
26
|
+
end
|
27
|
+
|
28
|
+
context "when there are changes to the changes made to an attribute" do
|
29
|
+
before { subject.first_name = 'no_name' }
|
30
|
+
|
31
|
+
it "checking if that attribute is changed, should return true" do
|
32
|
+
expect(subject.first_name_changed?).to be true
|
33
|
+
end
|
34
|
+
|
35
|
+
it "checking that attribute past value, should its first original value" do
|
36
|
+
expect(subject.first_name_was).to eq('John')
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
context "when there are changes made to a nested object" do
|
42
|
+
before { subject.address.street = 'my street' }
|
43
|
+
|
44
|
+
it "checking if the nested object as changed, should return false" do
|
45
|
+
expect(subject.address_changed?).to be false
|
46
|
+
end
|
47
|
+
|
48
|
+
it "checking if the nested object's attribute as changed, should return true" do
|
49
|
+
expect(subject.address.street_changed?).to be true
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|