smooth_operator 1.2.9 → 1.3.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 +8 -8
- data/README.md +78 -22
- data/console.rb +2 -0
- data/lib/smooth_operator/array_with_meta_data.rb +20 -8
- data/lib/smooth_operator/{relation → associations}/association_reflection.rb +8 -8
- data/lib/smooth_operator/{relation/array_relation.rb → associations/has_many_relation.rb} +3 -13
- data/lib/smooth_operator/{relation → associations}/reflection.rb +2 -2
- data/lib/smooth_operator/associations.rb +110 -0
- data/lib/smooth_operator/attribute_assignment.rb +52 -60
- data/lib/smooth_operator/cookie_jar.rb +21 -0
- data/lib/smooth_operator/delegation.rb +13 -34
- data/lib/smooth_operator/finder_methods.rb +26 -17
- data/lib/smooth_operator/helpers.rb +14 -8
- data/lib/smooth_operator/http_methods.rb +17 -0
- data/lib/smooth_operator/internal_data.rb +45 -0
- data/lib/smooth_operator/open_struct.rb +11 -26
- data/lib/smooth_operator/operator.rb +65 -59
- data/lib/smooth_operator/operators/connection_wrapper.rb +15 -0
- data/lib/smooth_operator/operators/faraday.rb +6 -6
- data/lib/smooth_operator/operators/typhoeus.rb +22 -12
- data/lib/smooth_operator/options.rb +30 -0
- data/lib/smooth_operator/persistence.rb +64 -61
- data/lib/smooth_operator/remote_call/base.rb +7 -6
- data/lib/smooth_operator/resource_name.rb +46 -0
- data/lib/smooth_operator/schema.rb +21 -0
- data/lib/smooth_operator/serialization.rb +80 -36
- data/lib/smooth_operator/translation.rb +21 -12
- data/lib/smooth_operator/type_casting.rb +127 -0
- data/lib/smooth_operator/validations.rb +25 -3
- data/lib/smooth_operator/version.rb +1 -1
- data/lib/smooth_operator.rb +55 -5
- data/smooth_operator.gemspec +5 -5
- data/spec/smooth_operator/attribute_assignment_spec.rb +5 -14
- data/spec/smooth_operator/finder_methods_spec.rb +4 -9
- data/spec/smooth_operator/persistence_spec.rb +27 -19
- data/spec/smooth_operator/remote_call_spec.rb +104 -84
- data/spec/smooth_operator/{model_schema_spec.rb → resource_name_spec.rb} +1 -1
- data/spec/support/models/address.rb +8 -10
- data/spec/support/models/comment.rb +2 -0
- data/spec/support/models/post.rb +7 -7
- data/spec/support/models/user.rb +10 -13
- data/spec/support/models/user_with_address_and_posts.rb +9 -17
- data/spec/support/test_server.rb +7 -7
- metadata +25 -25
- data/lib/smooth_operator/attribute_methods.rb +0 -78
- data/lib/smooth_operator/attributes/base.rb +0 -107
- data/lib/smooth_operator/attributes/dirty.rb +0 -29
- data/lib/smooth_operator/attributes/normal.rb +0 -15
- data/lib/smooth_operator/blank_slate.rb +0 -7
- data/lib/smooth_operator/model_schema.rb +0 -81
- data/lib/smooth_operator/relation/associations.rb +0 -102
- data/spec/smooth_operator/attributes_dirty_spec.rb +0 -53
data/lib/smooth_operator.rb
CHANGED
@@ -1,24 +1,29 @@
|
|
1
|
+
require "smooth_operator/schema"
|
1
2
|
require "smooth_operator/version"
|
2
3
|
require "smooth_operator/helpers"
|
3
4
|
require "smooth_operator/operator"
|
4
5
|
require "smooth_operator/persistence"
|
5
6
|
require "smooth_operator/translation"
|
6
7
|
require "smooth_operator/open_struct"
|
8
|
+
require "smooth_operator/http_methods"
|
9
|
+
require "smooth_operator/associations"
|
7
10
|
require "smooth_operator/finder_methods"
|
8
|
-
require "smooth_operator/relation/associations"
|
9
11
|
|
10
12
|
module SmoothOperator
|
11
|
-
class Base < OpenStruct
|
13
|
+
class Base < OpenStruct
|
12
14
|
|
15
|
+
extend Schema
|
16
|
+
extend HttpMethods
|
17
|
+
extend Associations
|
13
18
|
extend FinderMethods
|
14
|
-
extend Relation::Associations
|
15
19
|
extend Translation if defined? I18n
|
16
20
|
|
17
21
|
include Operator
|
22
|
+
include HttpMethods
|
18
23
|
include Persistence
|
19
24
|
include FinderMethods
|
20
25
|
|
21
|
-
|
26
|
+
options strict_behaviour: true
|
22
27
|
|
23
28
|
def self.smooth_operator?
|
24
29
|
true
|
@@ -33,12 +38,57 @@ module SmoothOperator
|
|
33
38
|
include ActiveModel::Validations::Callbacks
|
34
39
|
include ActiveModel::Conversion
|
35
40
|
|
41
|
+
options unknown_hash_class: SmoothOperator::OpenStruct
|
42
|
+
|
43
|
+
validate :validate_induced_errors, :validate_nested_objects
|
44
|
+
|
36
45
|
def column_for_attribute(attribute_name)
|
37
|
-
type =
|
46
|
+
type = self.class.attribute_type(attribute_name)
|
38
47
|
|
39
48
|
ActiveRecord::ConnectionAdapters::Column.new(attribute_name.to_sym, type, type)
|
40
49
|
end
|
41
50
|
|
51
|
+
def save(relative_path = nil, data = {}, options = {})
|
52
|
+
return false unless before_save
|
53
|
+
|
54
|
+
clear_induced_errors
|
55
|
+
|
56
|
+
save_result = valid? ? super : false
|
57
|
+
|
58
|
+
after_save if valid? && save_result
|
59
|
+
|
60
|
+
save_result
|
61
|
+
end
|
62
|
+
|
63
|
+
def before_save
|
64
|
+
true
|
65
|
+
end
|
66
|
+
|
67
|
+
def after_save; end
|
68
|
+
|
69
|
+
def self.model_name
|
70
|
+
smooth_model_name
|
71
|
+
end
|
72
|
+
|
73
|
+
protected ################# PROTECTED ###################
|
74
|
+
|
75
|
+
def validate_induced_errors
|
76
|
+
induced_errors.each do |key, value|
|
77
|
+
[*value].each do |_value|
|
78
|
+
self.errors.add(key, _value) unless self.errors.added?(key, _value)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
Helpers.blank?(induced_errors)
|
83
|
+
end
|
84
|
+
|
85
|
+
def validate_nested_objects
|
86
|
+
all_nested_objects = self.class.reflections.keys
|
87
|
+
.map { |association| send(association) }.flatten.compact
|
88
|
+
|
89
|
+
all_nested_objects.map { |nested_object| nested_object.valid? }.all?
|
90
|
+
end
|
91
|
+
|
42
92
|
end
|
43
93
|
end
|
44
94
|
end
|
data/smooth_operator.gemspec
CHANGED
@@ -20,12 +20,12 @@ Gem::Specification.new do |spec|
|
|
20
20
|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
21
21
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
22
22
|
spec.require_paths = ["lib"]
|
23
|
-
|
23
|
+
|
24
24
|
spec.add_development_dependency "bundler", "~> 1.3"
|
25
|
-
|
26
|
-
spec.add_dependency "json"
|
27
|
-
spec.add_dependency "faraday", "~> 0.8
|
28
|
-
spec.add_dependency "typhoeus", "~> 0.6
|
25
|
+
|
26
|
+
spec.add_dependency "json", '~> 1.5'
|
27
|
+
spec.add_dependency "faraday", "~> 0.8"
|
28
|
+
spec.add_dependency "typhoeus", "~> 0.6"
|
29
29
|
|
30
30
|
# this is necessary if you want to typhoeus to correctly encode arrays
|
31
31
|
# spec.add_dependency "ethon", :git => 'https://github.com/goncalvesjoao/ethon'
|
@@ -8,15 +8,6 @@ describe SmoothOperator::AttributeAssignment do
|
|
8
8
|
describe "receiving data from server" do
|
9
9
|
subject { User::Base.new }
|
10
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
11
|
context "when receiving a Hash with meta_data on it" do
|
21
12
|
before { subject.assign_attributes({ user: attributes_for(:user), status: 1 }) }
|
22
13
|
|
@@ -84,21 +75,21 @@ describe SmoothOperator::AttributeAssignment do
|
|
84
75
|
end
|
85
76
|
end
|
86
77
|
|
87
|
-
context "when the .unknown_hash_class is set to SmoothOperator::OpenStruct
|
78
|
+
context "when the .unknown_hash_class is set to SmoothOperator::OpenStruct" do
|
88
79
|
subject { User::UnknownHashClass::OpenStructBase.new(address: { street: 'something', postal_code: { code: '123' } }) }
|
89
80
|
|
90
|
-
it "a new instance of SmoothOperator::OpenStruct
|
81
|
+
it "a new instance of SmoothOperator::OpenStruct will be initialized with that hash" do
|
91
82
|
address = subject.address
|
92
83
|
|
93
|
-
expect(address).to be_instance_of(SmoothOperator::OpenStruct
|
84
|
+
expect(address).to be_instance_of(SmoothOperator::OpenStruct)
|
94
85
|
expect(address.street).to eq('something')
|
95
86
|
|
96
|
-
expect(address.postal_code).to be_instance_of(SmoothOperator::OpenStruct
|
87
|
+
expect(address.postal_code).to be_instance_of(SmoothOperator::OpenStruct)
|
97
88
|
expect(address.postal_code.code).to eq('123')
|
98
89
|
end
|
99
90
|
end
|
100
91
|
|
101
|
-
context "when the .unknown_hash_class is set to
|
92
|
+
context "when the .unknown_hash_class is set to nil" do
|
102
93
|
subject { User::UnknownHashClass::None.new(creator: { first_name: 'admin', address: { street: 'something' } }) }
|
103
94
|
|
104
95
|
it "the hash will be copied as it is" do
|
@@ -8,11 +8,6 @@ shared_examples_for "finder method" do
|
|
8
8
|
it "the instance class should be populated with the returned hash" do
|
9
9
|
expect(user.attributes).to eq(attributes_for(:user_with_address_and_posts))
|
10
10
|
end
|
11
|
-
|
12
|
-
it "#has_data_from_server and #from_server should return true" do
|
13
|
-
expect(user.has_data_from_server).to be true
|
14
|
-
expect(user.from_server).to be true
|
15
|
-
end
|
16
11
|
end
|
17
12
|
|
18
13
|
describe SmoothOperator::FinderMethods do
|
@@ -39,13 +34,13 @@ describe SmoothOperator::FinderMethods do
|
|
39
34
|
|
40
35
|
describe ".find" do
|
41
36
|
context "when the server returns a single hash" do
|
42
|
-
let(:user) { subject.find(5).
|
37
|
+
let(:user) { subject.find(5).data }
|
43
38
|
|
44
39
|
it_behaves_like "finder method"
|
45
40
|
end
|
46
41
|
|
47
42
|
context "when the server returns a hash with meta_data" do
|
48
|
-
let(:user) { subject.find("5/with_metadata").
|
43
|
+
let(:user) { subject.find("5/with_metadata").data }
|
49
44
|
|
50
45
|
it_behaves_like "finder method"
|
51
46
|
|
@@ -61,7 +56,7 @@ describe SmoothOperator::FinderMethods do
|
|
61
56
|
context "when the server returns an array" do
|
62
57
|
it "it should return a RemoteCall instance an array that contains a subject's class instance, one for every array's entry" do
|
63
58
|
remote_call = subject.find(:all)
|
64
|
-
users = remote_call.
|
59
|
+
users = remote_call.data
|
65
60
|
|
66
61
|
expect(users).to be_instance_of(Array)
|
67
62
|
expect(users[0]).to be_instance_of(subject)
|
@@ -70,7 +65,7 @@ describe SmoothOperator::FinderMethods do
|
|
70
65
|
|
71
66
|
it "if any of the array entries is not a hash, it shall not be converted or alteread" do
|
72
67
|
remote_call = subject.find('misc_array')
|
73
|
-
users = remote_call.
|
68
|
+
users = remote_call.data
|
74
69
|
|
75
70
|
expect(users).to be_instance_of(Array)
|
76
71
|
expect(users[0]).to be_instance_of(subject)
|
@@ -23,7 +23,12 @@ shared_examples_for "persistent remote call" do
|
|
23
23
|
let(:method_arguments) { ['', { status: 200 }] }
|
24
24
|
|
25
25
|
it "it should return true" do
|
26
|
-
execute_method
|
26
|
+
result = execute_method
|
27
|
+
|
28
|
+
if !(method_to_execute.to_s =~ /create/)
|
29
|
+
expect(result).to be true
|
30
|
+
end
|
31
|
+
|
27
32
|
expect(subject.last_remote_call.ok?).to be true
|
28
33
|
expect(subject.last_remote_call.status).to be true
|
29
34
|
end
|
@@ -40,7 +45,12 @@ shared_examples_for "persistent remote call" do
|
|
40
45
|
let(:method_arguments) { ['', { status: 422 }] }
|
41
46
|
|
42
47
|
it "it should return false" do
|
43
|
-
execute_method
|
48
|
+
result = execute_method
|
49
|
+
|
50
|
+
if !(method_to_execute.to_s =~ /create/)
|
51
|
+
expect(result).to be false
|
52
|
+
end
|
53
|
+
|
44
54
|
expect(subject.last_remote_call.not_processed?).to be true
|
45
55
|
expect(subject.last_remote_call.status).to be false
|
46
56
|
end
|
@@ -57,7 +67,12 @@ shared_examples_for "persistent remote call" do
|
|
57
67
|
let(:method_arguments) { ['', { status: 404 }] }
|
58
68
|
|
59
69
|
it "it should return nil" do
|
60
|
-
execute_method
|
70
|
+
result = execute_method
|
71
|
+
|
72
|
+
if !(method_to_execute.to_s =~ /create/)
|
73
|
+
expect(result).to be nil
|
74
|
+
end
|
75
|
+
|
61
76
|
expect(subject.last_remote_call.client_error?).to be true
|
62
77
|
expect(subject.last_remote_call.error?).to be true
|
63
78
|
expect(subject.last_remote_call.status).to be nil
|
@@ -73,7 +88,12 @@ shared_examples_for "persistent remote call" do
|
|
73
88
|
let(:method_arguments) { ['', { status: 500 }] }
|
74
89
|
|
75
90
|
it "it should return nil" do
|
76
|
-
execute_method
|
91
|
+
result = execute_method
|
92
|
+
|
93
|
+
if !(method_to_execute.to_s =~ /create/)
|
94
|
+
expect(result).to be nil
|
95
|
+
end
|
96
|
+
|
77
97
|
expect(subject.last_remote_call.server_error?).to be true
|
78
98
|
expect(subject.last_remote_call.error?).to be true
|
79
99
|
expect(subject.last_remote_call.status).to be_nil
|
@@ -133,13 +153,6 @@ describe SmoothOperator::Persistence, helpers: :persistence do
|
|
133
153
|
subject { new_user }
|
134
154
|
let(:method_to_execute) { :reload }
|
135
155
|
|
136
|
-
context "before calling #reload" do
|
137
|
-
it "#has_data_from_server and #from_server should return false" do
|
138
|
-
expect(subject.from_server).to be_falsey
|
139
|
-
expect(subject.has_data_from_server).to be_falsey
|
140
|
-
end
|
141
|
-
end
|
142
|
-
|
143
156
|
context "when subject doesn't has an id" do
|
144
157
|
it "it should raise 'UnknownPath'" do
|
145
158
|
expect{ subject.reload }.to raise_error 'UnknownPath'
|
@@ -155,11 +168,6 @@ describe SmoothOperator::Persistence, helpers: :persistence do
|
|
155
168
|
it "it should fetch server data" do
|
156
169
|
expect(subject.attributes).to eq(attributes_for(:user_with_address_and_posts))
|
157
170
|
end
|
158
|
-
|
159
|
-
it "#has_data_from_server and #from_server should return true" do
|
160
|
-
expect(subject.from_server).to be true
|
161
|
-
expect(subject.has_data_from_server).to be true
|
162
|
-
end
|
163
171
|
end
|
164
172
|
|
165
173
|
context "when calling #reload on a nested object" do
|
@@ -194,7 +202,7 @@ describe SmoothOperator::Persistence, helpers: :persistence do
|
|
194
202
|
end
|
195
203
|
|
196
204
|
describe ".create" do
|
197
|
-
|
205
|
+
|
198
206
|
subject { created_subject }
|
199
207
|
let(:method_arguments) { [] }
|
200
208
|
|
@@ -263,7 +271,7 @@ describe SmoothOperator::Persistence, helpers: :persistence do
|
|
263
271
|
expect(subject.destroyed?).to be(true)
|
264
272
|
end
|
265
273
|
end
|
266
|
-
|
274
|
+
|
267
275
|
context "after a failed execution of #destroy" do
|
268
276
|
subject { existing_user }
|
269
277
|
before { subject.destroy('', { status: 422 }) }
|
@@ -387,7 +395,7 @@ describe SmoothOperator::Persistence, helpers: :persistence do
|
|
387
395
|
end
|
388
396
|
|
389
397
|
describe "#destroy" do
|
390
|
-
|
398
|
+
|
391
399
|
let(:method_to_execute) { :destroy }
|
392
400
|
let(:method_arguments) { [] }
|
393
401
|
|