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
@@ -1,58 +0,0 @@
|
|
1
|
-
require 'typhoeus'
|
2
|
-
require "smooth_operator/http_handlers/typhoeus/remote_call"
|
3
|
-
|
4
|
-
module SmoothOperator
|
5
|
-
module HttpHandlers
|
6
|
-
module Typhoeus
|
7
|
-
|
8
|
-
class Base
|
9
|
-
|
10
|
-
def self.make_the_call(http_verb, url, options, basic_auth_credentials)
|
11
|
-
hydra = get_hydra_and_remove_it_from options
|
12
|
-
options = { (http_verb == :get ? :params : :body) => options, :method => http_verb, :headers => { "Content-type" => "application/x-www-form-urlencoded" } }.merge auth_credentials(basic_auth_credentials)
|
13
|
-
|
14
|
-
if hydra.present?
|
15
|
-
make_asynchronous_request(url, options, hydra)
|
16
|
-
else
|
17
|
-
make_synchronous_request(url, options)
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
private ###################### PRIVATE ##################
|
22
|
-
|
23
|
-
def self.auth_credentials(basic_auth_credentials)
|
24
|
-
basic_auth_credentials.blank? ? {} : { userpwd: "#{basic_auth_credentials[:username]}:#{basic_auth_credentials[:password]}" }
|
25
|
-
end
|
26
|
-
|
27
|
-
def self.get_hydra_and_remove_it_from(options)
|
28
|
-
options.delete(:hydra)
|
29
|
-
end
|
30
|
-
|
31
|
-
def self.make_synchronous_request(url, options)
|
32
|
-
hydra = ::Typhoeus::Hydra::hydra
|
33
|
-
remote_call = make_asynchronous_request(url, options, hydra)
|
34
|
-
|
35
|
-
remote_call.request.on_complete do |response|
|
36
|
-
remote_call.raw_response = response
|
37
|
-
remote_call.response = remote_call.parsed_response
|
38
|
-
end
|
39
|
-
|
40
|
-
hydra.run
|
41
|
-
remote_call
|
42
|
-
end
|
43
|
-
|
44
|
-
def self.make_asynchronous_request(url, options, hydra)
|
45
|
-
request = ::Typhoeus::Request.new(url, options)
|
46
|
-
|
47
|
-
remote_call = SmoothOperator::HttpHandlers::Typhoeus::RemoteCall.new(request)
|
48
|
-
|
49
|
-
hydra.queue(request)
|
50
|
-
|
51
|
-
remote_call
|
52
|
-
end
|
53
|
-
|
54
|
-
end
|
55
|
-
|
56
|
-
end
|
57
|
-
end
|
58
|
-
end
|
@@ -1,34 +0,0 @@
|
|
1
|
-
module SmoothOperator
|
2
|
-
module HttpHandlers
|
3
|
-
module Typhoeus
|
4
|
-
|
5
|
-
class ORM
|
6
|
-
|
7
|
-
attr_reader :object_class
|
8
|
-
|
9
|
-
def initialize(object_class)
|
10
|
-
@object_class = object_class
|
11
|
-
end
|
12
|
-
|
13
|
-
def make_the_call(http_verb, options, relative_path, &block)
|
14
|
-
injected_hydra = options[:hydra]
|
15
|
-
hydra = injected_hydra || ::Typhoeus::Hydra::hydra
|
16
|
-
options[:hydra] = hydra
|
17
|
-
|
18
|
-
remote_call = @object_class.make_the_call(http_verb, relative_path, options)
|
19
|
-
|
20
|
-
remote_call.request.on_complete do |typhoeus_response|
|
21
|
-
remote_call.raw_response = typhoeus_response
|
22
|
-
yield(remote_call)
|
23
|
-
end
|
24
|
-
|
25
|
-
hydra.run if injected_hydra.blank?
|
26
|
-
|
27
|
-
remote_call
|
28
|
-
end
|
29
|
-
|
30
|
-
end
|
31
|
-
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
@@ -1,28 +0,0 @@
|
|
1
|
-
require "smooth_operator/operator/remote_call"
|
2
|
-
|
3
|
-
module SmoothOperator
|
4
|
-
module HttpHandlers
|
5
|
-
module Typhoeus
|
6
|
-
|
7
|
-
class RemoteCall < SmoothOperator::Operator::RemoteCall
|
8
|
-
|
9
|
-
attr_reader :request
|
10
|
-
|
11
|
-
def initialize(request)
|
12
|
-
@request = request
|
13
|
-
end
|
14
|
-
|
15
|
-
def parse_response
|
16
|
-
begin
|
17
|
-
@parse_response ||= ::HTTParty::Parser.call(@raw_response.body, :json)
|
18
|
-
rescue
|
19
|
-
nil
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
end
|
24
|
-
|
25
|
-
end
|
26
|
-
|
27
|
-
end
|
28
|
-
end
|
@@ -1,43 +0,0 @@
|
|
1
|
-
module SmoothOperator
|
2
|
-
module Operator
|
3
|
-
|
4
|
-
module Base
|
5
|
-
|
6
|
-
def self.included(base)
|
7
|
-
base.extend(ClassMethods)
|
8
|
-
end
|
9
|
-
|
10
|
-
module ClassMethods
|
11
|
-
|
12
|
-
def get(relative_path, options = {})
|
13
|
-
make_the_call(:get, relative_path, options)
|
14
|
-
end
|
15
|
-
|
16
|
-
def post(relative_path, options = {})
|
17
|
-
make_the_call(:post, relative_path, options)
|
18
|
-
end
|
19
|
-
|
20
|
-
def put(relative_path, options = {})
|
21
|
-
make_the_call(:put, relative_path, options)
|
22
|
-
end
|
23
|
-
|
24
|
-
def delete(relative_path, options = {})
|
25
|
-
make_the_call(:delete, relative_path, options)
|
26
|
-
end
|
27
|
-
|
28
|
-
def make_the_call(http_verb, relative_path, options = {})
|
29
|
-
url = build_url(relative_path)
|
30
|
-
options = query_string(options || {})
|
31
|
-
http_handler_base.make_the_call(http_verb, url, options, get_basic_auth_credentials)
|
32
|
-
end
|
33
|
-
|
34
|
-
def query_string(options)
|
35
|
-
options
|
36
|
-
end
|
37
|
-
|
38
|
-
end
|
39
|
-
|
40
|
-
end
|
41
|
-
|
42
|
-
end
|
43
|
-
end
|
@@ -1,64 +0,0 @@
|
|
1
|
-
module SmoothOperator
|
2
|
-
module Operator
|
3
|
-
|
4
|
-
module Exceptions
|
5
|
-
|
6
|
-
extend self
|
7
|
-
|
8
|
-
def raise_proper_exception(response, code)
|
9
|
-
return nil if CODES_TO_IGNORE.include?(code)
|
10
|
-
exception_to_raise = (CODE_VS_EXCEPTIONS[code] || SmoothOperator::Operator::Exceptions::Unknown).new(response)
|
11
|
-
#raise exception_to_raise, exception_to_raise.message
|
12
|
-
end
|
13
|
-
|
14
|
-
class Base < StandardError
|
15
|
-
attr_reader :response
|
16
|
-
|
17
|
-
def initialize(response)
|
18
|
-
@response = response
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
class Unknown < SmoothOperator::Operator::Exceptions::Base
|
23
|
-
def message
|
24
|
-
'Unknown Error'
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
class NotFound < SmoothOperator::Operator::Exceptions::Base
|
29
|
-
def message
|
30
|
-
'NotFound'
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
class EntityNotProcessed < SmoothOperator::Operator::Exceptions::Base
|
35
|
-
def message
|
36
|
-
'EntityNotProcessed'
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
class ServerError < SmoothOperator::Operator::Exceptions::Base
|
41
|
-
def message
|
42
|
-
'ServerError'
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
class AuthorizationRequired < SmoothOperator::Operator::Exceptions::Base
|
47
|
-
def message
|
48
|
-
'AuthorizationRequired'
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
CODES_TO_IGNORE = [200]
|
53
|
-
|
54
|
-
CODE_VS_EXCEPTIONS = {
|
55
|
-
401 => AuthorizationRequired,
|
56
|
-
422 => EntityNotProcessed,
|
57
|
-
404 => NotFound,
|
58
|
-
500 => ServerError
|
59
|
-
}
|
60
|
-
|
61
|
-
end
|
62
|
-
|
63
|
-
end
|
64
|
-
end
|
@@ -1,118 +0,0 @@
|
|
1
|
-
module SmoothOperator
|
2
|
-
module Operator
|
3
|
-
|
4
|
-
module ORM
|
5
|
-
|
6
|
-
def self.included(base)
|
7
|
-
base.extend(ClassMethods)
|
8
|
-
base.send(:attr_reader, :last_response)
|
9
|
-
base.send(:attr_reader, :exception)
|
10
|
-
end
|
11
|
-
|
12
|
-
module ClassMethods
|
13
|
-
|
14
|
-
def find(id, options = {})
|
15
|
-
if id == :all
|
16
|
-
find_each('', options)
|
17
|
-
else
|
18
|
-
find_one(id, options)
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
def safe_find(id, options = {})
|
23
|
-
begin
|
24
|
-
find(id, options)
|
25
|
-
rescue Exception => exception #exception.response contains the server response
|
26
|
-
id == :all ? [] : nil
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
def create(options = {})
|
31
|
-
new_object = new(options)
|
32
|
-
|
33
|
-
http_handler_orm.make_the_call(:post, { model_name_downcase => new_object.safe_table_hash }, '') do |remote_call|
|
34
|
-
new_object.send('after_create_update_or_destroy', remote_call)
|
35
|
-
end
|
36
|
-
|
37
|
-
new_object
|
38
|
-
end
|
39
|
-
|
40
|
-
def find_each(relative_path, options)
|
41
|
-
http_handler_orm.make_the_call(:get, options, relative_path) do |remote_call|
|
42
|
-
objects_list = remote_call.get_attributes(table_name)
|
43
|
-
|
44
|
-
if objects_list.kind_of?(Array)
|
45
|
-
remote_call.response = objects_list.map { |attributes| new remote_call.get_attributes(model_name_downcase, attributes) }
|
46
|
-
else
|
47
|
-
remote_call.response = objects_list
|
48
|
-
end
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
def find_one(relative_path, options)
|
53
|
-
http_handler_orm.make_the_call(:get, options, relative_path) do |remote_call|
|
54
|
-
remote_call.response = new remote_call.get_attributes(model_name_downcase)
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
end
|
59
|
-
|
60
|
-
def save(options = {})
|
61
|
-
begin
|
62
|
-
save!(options)
|
63
|
-
rescue Exception => exception
|
64
|
-
send("exception=", exception)
|
65
|
-
false
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
def save!(options = {})
|
70
|
-
options = build_options_for_save(options)
|
71
|
-
|
72
|
-
if new_record?
|
73
|
-
http_handler_orm.make_the_call(:post, options, '') { |remote_call| after_create_update_or_destroy(remote_call) }
|
74
|
-
else
|
75
|
-
http_handler_orm.make_the_call(:put, options, id) { |remote_call| after_create_update_or_destroy(remote_call) }
|
76
|
-
end
|
77
|
-
end
|
78
|
-
|
79
|
-
def destroy(options = {})
|
80
|
-
return true if new_record?
|
81
|
-
|
82
|
-
http_handler_orm.make_the_call(:delete, options, id) do |remote_call|
|
83
|
-
after_create_update_or_destroy(remote_call)
|
84
|
-
end
|
85
|
-
end
|
86
|
-
|
87
|
-
private ####################### private #######################
|
88
|
-
|
89
|
-
def build_options_for_save(options = {})
|
90
|
-
options ||= {}
|
91
|
-
options[self.class.model_name_downcase] ||= safe_table_hash
|
92
|
-
options
|
93
|
-
end
|
94
|
-
|
95
|
-
def after_create_update_or_destroy(remote_call)
|
96
|
-
new_attributes = remote_call.get_attributes(self.class.model_name_downcase)
|
97
|
-
assign_attributes(new_attributes)
|
98
|
-
set_raw_response_exception_and_build_proper_response(remote_call)
|
99
|
-
end
|
100
|
-
|
101
|
-
def set_raw_response_exception_and_build_proper_response(remote_call)
|
102
|
-
send("last_response=", remote_call.raw_response)
|
103
|
-
send("exception=", remote_call.exception)
|
104
|
-
remote_call.response = remote_call.successful_response?
|
105
|
-
end
|
106
|
-
|
107
|
-
def last_response=(response)
|
108
|
-
@last_response = response
|
109
|
-
end
|
110
|
-
|
111
|
-
def exception=(exception)
|
112
|
-
@exception = exception
|
113
|
-
end
|
114
|
-
|
115
|
-
end
|
116
|
-
|
117
|
-
end
|
118
|
-
end
|
@@ -1,84 +0,0 @@
|
|
1
|
-
require "smooth_operator/operator/exceptions"
|
2
|
-
|
3
|
-
module SmoothOperator
|
4
|
-
module Operator
|
5
|
-
|
6
|
-
class RemoteCall
|
7
|
-
|
8
|
-
attr_reader :protocol_handler, :raw_response, :parsed_response, :exception
|
9
|
-
attr_writer :response
|
10
|
-
|
11
|
-
HTTP_SUCCESS_CODES = [200, 201, 202, 203, 204]
|
12
|
-
|
13
|
-
def parse_response # TO BE OVERWRITTEN IF NECESSARY
|
14
|
-
@raw_response
|
15
|
-
end
|
16
|
-
|
17
|
-
def successful_response? # TO BE OVERWRITTEN IF NECESSARY
|
18
|
-
@raw_response.blank? || HTTP_SUCCESS_CODES.include?(code)
|
19
|
-
end
|
20
|
-
|
21
|
-
def code # TO BE OVERWRITTEN IF NECESSARY
|
22
|
-
@raw_response.code
|
23
|
-
end
|
24
|
-
|
25
|
-
def ok?; successful_response?; end
|
26
|
-
|
27
|
-
def error?; !ok? && exception.present?; end
|
28
|
-
|
29
|
-
def raw_response=(response)
|
30
|
-
@raw_response = response
|
31
|
-
@parsed_response = parse_response_and_set_exception_if_necessary
|
32
|
-
end
|
33
|
-
|
34
|
-
def response
|
35
|
-
exception.present? ? false : @response
|
36
|
-
end
|
37
|
-
|
38
|
-
def ==(object_to_compare)
|
39
|
-
response == object_to_compare
|
40
|
-
end
|
41
|
-
|
42
|
-
def <<(object)
|
43
|
-
if response.is_a? Array
|
44
|
-
response << object
|
45
|
-
else
|
46
|
-
response += object
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
def respond_to?(symbol, include_priv = false)
|
51
|
-
response.respond_to?(symbol, include_priv)
|
52
|
-
end
|
53
|
-
|
54
|
-
def to_s
|
55
|
-
response
|
56
|
-
end
|
57
|
-
|
58
|
-
def get_attributes(key, attributes = nil)
|
59
|
-
attributes ||= @parsed_response
|
60
|
-
|
61
|
-
if attributes.kind_of?(Hash)
|
62
|
-
attributes.include?(key) ? attributes[key] : attributes
|
63
|
-
else
|
64
|
-
attributes
|
65
|
-
end
|
66
|
-
end
|
67
|
-
|
68
|
-
protected ####################### protected ##################
|
69
|
-
|
70
|
-
def parse_response_and_set_exception_if_necessary
|
71
|
-
@exception = successful_response? ? nil : SmoothOperator::Operator::Exceptions.raise_proper_exception(@raw_response, code)
|
72
|
-
parse_response
|
73
|
-
end
|
74
|
-
|
75
|
-
private ####################### private ######################
|
76
|
-
|
77
|
-
def method_missing(method, *args, &block)
|
78
|
-
response.send(method, *args, &block)
|
79
|
-
end
|
80
|
-
|
81
|
-
end
|
82
|
-
|
83
|
-
end
|
84
|
-
end
|