smooth_operator 1.2.9 → 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
|