smooth_operator 0.0.2 → 0.3.2
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.
- data/lib/smooth_operator.rb +2 -0
- data/lib/smooth_operator/base.rb +17 -49
- data/lib/smooth_operator/core.rb +162 -0
- data/lib/smooth_operator/http_handlers/typhoeus/base.rb +58 -0
- data/lib/smooth_operator/http_handlers/typhoeus/orm.rb +33 -0
- data/lib/smooth_operator/http_handlers/typhoeus/remote_call.rb +28 -0
- data/lib/smooth_operator/operator/base.rb +38 -0
- data/lib/smooth_operator/operator/exceptions.rb +64 -0
- data/lib/smooth_operator/operator/orm.rb +120 -0
- data/lib/smooth_operator/operator/remote_call.rb +84 -0
- data/lib/smooth_operator/rollback.rb +16 -0
- data/lib/smooth_operator/version.rb +1 -1
- metadata +11 -7
- data/lib/smooth_operator/exceptions.rb +0 -60
- data/lib/smooth_operator/open_struct.rb +0 -16
- data/lib/smooth_operator/operator.rb +0 -100
- data/lib/smooth_operator/orm.rb +0 -154
- data/lib/smooth_operator/protocol_handler.rb +0 -26
data/lib/smooth_operator.rb
CHANGED
data/lib/smooth_operator/base.rb
CHANGED
@@ -1,62 +1,30 @@
|
|
1
|
-
require "smooth_operator/operator"
|
2
|
-
require "smooth_operator/orm"
|
1
|
+
require "smooth_operator/operator/base"
|
2
|
+
require "smooth_operator/operator/orm"
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
include SmoothOperator::Operator
|
8
|
-
include SmoothOperator::ORM
|
9
|
-
|
10
|
-
def hash_of_full_content
|
11
|
-
@table
|
12
|
-
end
|
13
|
-
|
14
|
-
def as_json(options = nil)
|
15
|
-
@table.as_json(options)
|
16
|
-
end
|
17
|
-
|
18
|
-
def self.nested_objects_classes(hash)
|
19
|
-
hash.each do |nested_object_symbol, nested_object_class|
|
20
|
-
define_method(nested_object_symbol.to_s) do
|
21
|
-
get_nested_object_variable(nested_object_symbol, nested_object_class)
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
4
|
+
require "smooth_operator/http_handlers/typhoeus/base"
|
5
|
+
require "smooth_operator/http_handlers/typhoeus/orm"
|
25
6
|
|
26
|
-
|
27
|
-
|
28
|
-
def get_nested_object_variable(nested_object_symbol, nested_object_class)
|
29
|
-
nested_object_variable = instance_variable_get("@#{nested_object_symbol}")
|
30
|
-
|
31
|
-
return nested_object_variable if nested_object_variable.present?
|
32
|
-
|
33
|
-
nested_object_variable = initialize_nested_object_variable(hash_of_full_content[nested_object_symbol], nested_object_class, nested_object_symbol)
|
7
|
+
module SmoothOperator
|
34
8
|
|
35
|
-
|
9
|
+
class Base < OpenStruct
|
10
|
+
include SmoothOperator::Core
|
11
|
+
include SmoothOperator::Operator::Base
|
12
|
+
include SmoothOperator::Operator::ORM
|
36
13
|
|
37
|
-
|
38
|
-
end
|
14
|
+
protected ################### PROTECTED ###################
|
39
15
|
|
40
|
-
def
|
41
|
-
|
42
|
-
attributes.map { |_attributes| get_nested_object(_attributes, nested_object_class, nested_object_symbol) }
|
43
|
-
else
|
44
|
-
get_nested_object(attributes, nested_object_class, nested_object_symbol)
|
45
|
-
end
|
16
|
+
def self.http_handler_base
|
17
|
+
SmoothOperator::HttpHandlers::Typhoeus::Base
|
46
18
|
end
|
47
19
|
|
48
|
-
def
|
49
|
-
|
50
|
-
plural?(nested_object_symbol) ? [] : nil
|
51
|
-
else
|
52
|
-
nested_object_class.new(nested_objects_attributes)
|
53
|
-
end
|
20
|
+
def self.http_handler_orm
|
21
|
+
@http_handler_orm ||= SmoothOperator::HttpHandlers::Typhoeus::ORM.new(self)
|
54
22
|
end
|
55
23
|
|
56
|
-
def
|
57
|
-
|
58
|
-
string == string.pluralize
|
24
|
+
def http_handler_orm
|
25
|
+
self.class.http_handler_orm
|
59
26
|
end
|
60
27
|
|
61
28
|
end
|
29
|
+
|
62
30
|
end
|
@@ -0,0 +1,162 @@
|
|
1
|
+
module SmoothOperator
|
2
|
+
module Core
|
3
|
+
|
4
|
+
def table_hash
|
5
|
+
@table
|
6
|
+
end
|
7
|
+
|
8
|
+
def as_json(options = nil)
|
9
|
+
@table.as_json(options)
|
10
|
+
end
|
11
|
+
|
12
|
+
def new_record?
|
13
|
+
!persisted?
|
14
|
+
end
|
15
|
+
|
16
|
+
def persisted?
|
17
|
+
try(:id).present?
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_partial_path
|
21
|
+
class_name_plural = self.class.table_name
|
22
|
+
"#{class_name_plural}/#{class_name_plural.singularize}"
|
23
|
+
end
|
24
|
+
|
25
|
+
def valid?
|
26
|
+
errors.blank?
|
27
|
+
end
|
28
|
+
|
29
|
+
def invalid?
|
30
|
+
!valid?
|
31
|
+
end
|
32
|
+
|
33
|
+
def assign_attributes(attributes = {})
|
34
|
+
return if attributes.blank?
|
35
|
+
|
36
|
+
attributes.each do |name, value|
|
37
|
+
send("#{name}=", value)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def safe_table_hash
|
42
|
+
safe_hash = table_hash.dup
|
43
|
+
|
44
|
+
if self.class.save_attr_white_list.present?
|
45
|
+
safe_hash.slice!(*self.class.save_attr_white_list)
|
46
|
+
else
|
47
|
+
self.class.save_attr_black_list.each { |attribute| safe_hash.delete(attribute) }
|
48
|
+
end
|
49
|
+
|
50
|
+
safe_hash
|
51
|
+
end
|
52
|
+
|
53
|
+
|
54
|
+
private ######################### PRIVATE ###############################
|
55
|
+
|
56
|
+
def get_nested_object_variable(nested_object_symbol, nested_object_class)
|
57
|
+
nested_object_variable = instance_variable_get("@#{nested_object_symbol}")
|
58
|
+
|
59
|
+
return nested_object_variable if nested_object_variable.present?
|
60
|
+
|
61
|
+
nested_object_variable = initialize_nested_object_variable(table_hash[nested_object_symbol], nested_object_class, nested_object_symbol)
|
62
|
+
|
63
|
+
instance_variable_set("@#{nested_object_symbol}", nested_object_variable)
|
64
|
+
|
65
|
+
nested_object_variable
|
66
|
+
end
|
67
|
+
|
68
|
+
def initialize_nested_object_variable(attributes, nested_object_class, nested_object_symbol)
|
69
|
+
if attributes.kind_of? Array
|
70
|
+
attributes.map { |_attributes| get_nested_object(_attributes, nested_object_class, nested_object_symbol) }
|
71
|
+
else
|
72
|
+
get_nested_object(attributes, nested_object_class, nested_object_symbol)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def get_nested_object(nested_objects_attributes, nested_object_class, nested_object_symbol)
|
77
|
+
if nested_objects_attributes.blank?
|
78
|
+
plural?(nested_object_symbol) ? [] : nil
|
79
|
+
else
|
80
|
+
nested_object_class.new(nested_objects_attributes)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def plural?(string)
|
85
|
+
string = string.to_s
|
86
|
+
string == string.pluralize
|
87
|
+
end
|
88
|
+
|
89
|
+
|
90
|
+
def self.included(base)
|
91
|
+
base.extend(ClassMethods)
|
92
|
+
end
|
93
|
+
|
94
|
+
module ClassMethods
|
95
|
+
|
96
|
+
def nested_objects_classes(hash)
|
97
|
+
hash.each do |nested_object_symbol, nested_object_class|
|
98
|
+
define_method(nested_object_symbol.to_s) do
|
99
|
+
get_nested_object_variable(nested_object_symbol, nested_object_class)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
attr_writer :endpoint
|
105
|
+
def endpoint
|
106
|
+
@endpoint ||= ENV["API_ENDPOINT"]
|
107
|
+
end
|
108
|
+
|
109
|
+
attr_writer :endpoint_user
|
110
|
+
def endpoint_user
|
111
|
+
@endpoint_user ||= ENV["API_USER"]
|
112
|
+
end
|
113
|
+
|
114
|
+
attr_writer :endpoint_pass
|
115
|
+
def endpoint_pass
|
116
|
+
@endpoint_pass ||= ENV["API_PASS"]
|
117
|
+
end
|
118
|
+
|
119
|
+
attr_writer :save_attr_black_list
|
120
|
+
def save_attr_black_list
|
121
|
+
@save_attr_black_list ||= [:id, :created_at, :updated_at, :errors]
|
122
|
+
end
|
123
|
+
|
124
|
+
attr_writer :save_attr_white_list
|
125
|
+
def save_attr_white_list
|
126
|
+
@save_attr_white_list ||= []
|
127
|
+
end
|
128
|
+
|
129
|
+
attr_writer :model_name
|
130
|
+
def model_name
|
131
|
+
@model_name ||= name.split('::').last.underscore.capitalize
|
132
|
+
end
|
133
|
+
|
134
|
+
def model_name_downcase
|
135
|
+
model_name.downcase
|
136
|
+
end
|
137
|
+
|
138
|
+
attr_writer :table_name
|
139
|
+
def table_name
|
140
|
+
@table_name ||= model_name_downcase.to_s.pluralize
|
141
|
+
end
|
142
|
+
|
143
|
+
private ################################ PRIVATE #########################
|
144
|
+
|
145
|
+
def build_url(relative_path)
|
146
|
+
slash = '/' if relative_path.present?
|
147
|
+
extention = '.json'
|
148
|
+
[endpoint, table_name, slash, relative_path.to_s, extention].join
|
149
|
+
end
|
150
|
+
|
151
|
+
def get_basic_auth_credentials
|
152
|
+
if endpoint_user.present? || endpoint_pass.present?
|
153
|
+
{ username: endpoint_user, password: endpoint_pass }
|
154
|
+
else
|
155
|
+
nil
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
end
|
160
|
+
|
161
|
+
end
|
162
|
+
end
|
@@ -0,0 +1,58 @@
|
|
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 = { params: options, method: http_verb }.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
|
+
request = ::Typhoeus::Request.new(url, options)
|
33
|
+
remote_call = SmoothOperator::HttpHandlers::Typhoeus::RemoteCall.new(request)
|
34
|
+
|
35
|
+
request.on_complete do |response|
|
36
|
+
remote_call.raw_response = response
|
37
|
+
remote_call.response = remote_call.parsed_response
|
38
|
+
end
|
39
|
+
|
40
|
+
request.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
|
@@ -0,0 +1,33 @@
|
|
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, id, &block)
|
14
|
+
injected_hydra = options[:hydra]
|
15
|
+
options[:hydra] = injected_hydra || ::Typhoeus::Hydra.hydra
|
16
|
+
|
17
|
+
remote_call = @object_class.make_the_call(http_verb, id, options)
|
18
|
+
|
19
|
+
remote_call.request.on_complete do |typhoeus_response|
|
20
|
+
remote_call.raw_response = typhoeus_response
|
21
|
+
yield(remote_call)
|
22
|
+
end
|
23
|
+
|
24
|
+
remote_call.request.run if injected_hydra.blank?
|
25
|
+
|
26
|
+
remote_call
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,28 @@
|
|
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
|
@@ -0,0 +1,38 @@
|
|
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
|
+
http_handler_base.make_the_call(http_verb, url, (options || {}), get_basic_auth_credentials)
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,64 @@
|
|
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
|
@@ -0,0 +1,120 @@
|
|
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
|
+
protected ####################### protected #######################
|
41
|
+
|
42
|
+
def find_each(options)
|
43
|
+
http_handler_orm.make_the_call(:get, options, '') do |remote_call|
|
44
|
+
objects_list = remote_call.get_attributes(table_name)
|
45
|
+
|
46
|
+
if objects_list.kind_of?(Array)
|
47
|
+
remote_call.response = objects_list.map { |attributes| new remote_call.get_attributes(model_name_downcase, attributes) }
|
48
|
+
else
|
49
|
+
remote_call.response = objects_list
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def find_one(id, options)
|
55
|
+
http_handler_orm.make_the_call(:get, options, id) do |remote_call|
|
56
|
+
remote_call.response = new remote_call.get_attributes(model_name_downcase)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
def save(options = {})
|
63
|
+
begin
|
64
|
+
save!(options)
|
65
|
+
rescue Exception => exception
|
66
|
+
send("exception=", exception)
|
67
|
+
false
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def save!(options = {})
|
72
|
+
options = build_options_for_save(options)
|
73
|
+
|
74
|
+
if new_record?
|
75
|
+
http_handler_orm.make_the_call(:post, options, '') { |remote_call| after_create_update_or_destroy(remote_call) }
|
76
|
+
else
|
77
|
+
http_handler_orm.make_the_call(:put, options, id) { |remote_call| after_create_update_or_destroy(remote_call) }
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def destroy(options = {})
|
82
|
+
return true if new_record?
|
83
|
+
|
84
|
+
http_handler_orm.make_the_call(:delete, options, id) do |remote_call|
|
85
|
+
after_create_update_or_destroy(remote_call)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
private ####################### private #######################
|
90
|
+
|
91
|
+
def build_options_for_save(options = {})
|
92
|
+
options ||= {}
|
93
|
+
options[self.class.model_name_downcase] ||= safe_table_hash
|
94
|
+
options
|
95
|
+
end
|
96
|
+
|
97
|
+
def after_create_update_or_destroy(remote_call)
|
98
|
+
new_attributes = remote_call.get_attributes(self.class.model_name_downcase)
|
99
|
+
assign_attributes(new_attributes)
|
100
|
+
set_raw_response_exception_and_build_proper_response(remote_call)
|
101
|
+
end
|
102
|
+
|
103
|
+
def set_raw_response_exception_and_build_proper_response(remote_call)
|
104
|
+
send("last_response=", remote_call.raw_response)
|
105
|
+
send("exception=", remote_call.exception)
|
106
|
+
remote_call.response = remote_call.successful_response?
|
107
|
+
end
|
108
|
+
|
109
|
+
def last_response=(response)
|
110
|
+
@last_response = response
|
111
|
+
end
|
112
|
+
|
113
|
+
def exception=(exception)
|
114
|
+
@exception = exception
|
115
|
+
end
|
116
|
+
|
117
|
+
end
|
118
|
+
|
119
|
+
end
|
120
|
+
end
|
@@ -0,0 +1,84 @@
|
|
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
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module SmoothOperator
|
2
|
+
|
3
|
+
class Rollback < SmoothOperator::Base
|
4
|
+
|
5
|
+
def rollback(options = {}, dont_update = false)
|
6
|
+
http_handler_orm.make_the_call(:delete, options, "#{version_id}/rollback") do |remote_call|
|
7
|
+
set_raw_response_exception_and_build_proper_response(remote_call)
|
8
|
+
|
9
|
+
new_attributes = remote_call.get_attributes(self.class.model_name_downcase)
|
10
|
+
assign_attributes(new_attributes.slice("id", "version_id"))
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: smooth_operator
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.2
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-08-
|
12
|
+
date: 2013-08-12 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bundler
|
@@ -73,11 +73,15 @@ files:
|
|
73
73
|
- Rakefile
|
74
74
|
- lib/smooth_operator.rb
|
75
75
|
- lib/smooth_operator/base.rb
|
76
|
-
- lib/smooth_operator/
|
77
|
-
- lib/smooth_operator/
|
78
|
-
- lib/smooth_operator/
|
79
|
-
- lib/smooth_operator/
|
80
|
-
- lib/smooth_operator/
|
76
|
+
- lib/smooth_operator/core.rb
|
77
|
+
- lib/smooth_operator/http_handlers/typhoeus/base.rb
|
78
|
+
- lib/smooth_operator/http_handlers/typhoeus/orm.rb
|
79
|
+
- lib/smooth_operator/http_handlers/typhoeus/remote_call.rb
|
80
|
+
- lib/smooth_operator/operator/base.rb
|
81
|
+
- lib/smooth_operator/operator/exceptions.rb
|
82
|
+
- lib/smooth_operator/operator/orm.rb
|
83
|
+
- lib/smooth_operator/operator/remote_call.rb
|
84
|
+
- lib/smooth_operator/rollback.rb
|
81
85
|
- lib/smooth_operator/version.rb
|
82
86
|
- smooth_operator.gemspec
|
83
87
|
homepage: https://github.com/goncalvesjoao/smooth_operator
|
@@ -1,60 +0,0 @@
|
|
1
|
-
module SmoothOperator
|
2
|
-
module Exceptions
|
3
|
-
|
4
|
-
extend self
|
5
|
-
|
6
|
-
def raise_proper_exception(response)
|
7
|
-
return nil if CODES_TO_IGNORE.include?(response.code)
|
8
|
-
exception_to_raise = (CODE_VS_EXCEPTIONS[response.code] || SmoothOperator::Exceptions::Unknown).new(response)
|
9
|
-
raise exception_to_raise, exception_to_raise.message
|
10
|
-
end
|
11
|
-
|
12
|
-
class Base < StandardError
|
13
|
-
attr_reader :response
|
14
|
-
|
15
|
-
def initialize(response)
|
16
|
-
@response = response
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
class Unknown < SmoothOperator::Exceptions::Base
|
21
|
-
def message
|
22
|
-
'Unknown Error'
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
class NotFound < SmoothOperator::Exceptions::Base
|
27
|
-
def message
|
28
|
-
'NotFound'
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
class EntityNotProcessed < SmoothOperator::Exceptions::Base
|
33
|
-
def message
|
34
|
-
'EntityNotProcessed'
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
class ServerError < SmoothOperator::Exceptions::Base
|
39
|
-
def message
|
40
|
-
'ServerError'
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
class AuthorizationRequired < SmoothOperator::Exceptions::Base
|
45
|
-
def message
|
46
|
-
'AuthorizationRequired'
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
CODES_TO_IGNORE = [200]
|
51
|
-
|
52
|
-
CODE_VS_EXCEPTIONS = {
|
53
|
-
401 => AuthorizationRequired,
|
54
|
-
422 => EntityNotProcessed,
|
55
|
-
404 => NotFound,
|
56
|
-
500 => ServerError
|
57
|
-
}
|
58
|
-
|
59
|
-
end
|
60
|
-
end
|
@@ -1,16 +0,0 @@
|
|
1
|
-
module SmoothOperator
|
2
|
-
module OpenStruct
|
3
|
-
|
4
|
-
extend self # extends the module Class with its instance methods and
|
5
|
-
# makes it possible do 'SmoothOperator::OpenStruct.new'
|
6
|
-
|
7
|
-
def new(attributes = {})
|
8
|
-
::OpenStruct.new(attributes).extend(SmoothOperator::OpenStruct)
|
9
|
-
end
|
10
|
-
|
11
|
-
def to_hash
|
12
|
-
@table
|
13
|
-
end
|
14
|
-
|
15
|
-
end
|
16
|
-
end
|
@@ -1,100 +0,0 @@
|
|
1
|
-
require "smooth_operator/protocol_handler"
|
2
|
-
|
3
|
-
module SmoothOperator
|
4
|
-
module Operator
|
5
|
-
|
6
|
-
HTTP_SUCCESS_CODES = [200, 201, 202, 203, 204]
|
7
|
-
|
8
|
-
def self.included(base)
|
9
|
-
base.extend(ClassMethods)
|
10
|
-
end
|
11
|
-
|
12
|
-
module ClassMethods
|
13
|
-
|
14
|
-
attr_writer :endpoint
|
15
|
-
def endpoint
|
16
|
-
@endpoint ||= ENV["API_ENDPOINT"]
|
17
|
-
end
|
18
|
-
|
19
|
-
attr_writer :endpoint_user
|
20
|
-
def endpoint_user
|
21
|
-
@endpoint_user ||= ENV["API_USER"]
|
22
|
-
end
|
23
|
-
|
24
|
-
attr_writer :endpoint_pass
|
25
|
-
def endpoint_pass
|
26
|
-
@endpoint_pass ||= ENV["API_PASS"]
|
27
|
-
end
|
28
|
-
|
29
|
-
def get(relative_path, options = {})
|
30
|
-
make_the_call(:get, relative_path, SmoothOperator::ProtocolHandler.get_options(options))
|
31
|
-
end
|
32
|
-
|
33
|
-
def post(relative_path, options = {})
|
34
|
-
make_the_call(:post, relative_path, SmoothOperator::ProtocolHandler.post_options(options))
|
35
|
-
end
|
36
|
-
|
37
|
-
def put(relative_path, options = {})
|
38
|
-
make_the_call(:put, relative_path, SmoothOperator::ProtocolHandler.put_options(options))
|
39
|
-
end
|
40
|
-
|
41
|
-
def delete(relative_path, options = {})
|
42
|
-
make_the_call(:delete, relative_path, SmoothOperator::ProtocolHandler.delete_options(options))
|
43
|
-
end
|
44
|
-
|
45
|
-
|
46
|
-
def safe_get(relative_path, options = {})
|
47
|
-
safe_response(get(relative_path, options)) rescue nil
|
48
|
-
end
|
49
|
-
|
50
|
-
def safe_post(relative_path, options = {})
|
51
|
-
safe_response(post(relative_path, options)) rescue nil
|
52
|
-
end
|
53
|
-
|
54
|
-
def safe_put(relative_path, options = {})
|
55
|
-
safe_response(put(relative_path, options)) rescue nil
|
56
|
-
end
|
57
|
-
|
58
|
-
def safe_delete(relative_path, options = {})
|
59
|
-
safe_response(delete(relative_path, options)) rescue nil
|
60
|
-
end
|
61
|
-
|
62
|
-
def safe_response(response)
|
63
|
-
successful_response?(response) ? response.parsed_response : nil
|
64
|
-
end
|
65
|
-
|
66
|
-
|
67
|
-
def make_the_call(http_verb, relative_path, options = {})
|
68
|
-
url = build_url(relative_path)
|
69
|
-
options = build_options(options)
|
70
|
-
response = SmoothOperator::ProtocolHandler.send(http_verb, url, options)
|
71
|
-
response
|
72
|
-
end
|
73
|
-
|
74
|
-
def build_url(relative_path)
|
75
|
-
slash = '/' if relative_path.present?
|
76
|
-
extention = '.json'
|
77
|
-
[endpoint, table_name, slash, relative_path.to_s, extention].join
|
78
|
-
end
|
79
|
-
|
80
|
-
def set_basic_auth_credentials(options)
|
81
|
-
if endpoint_user.present? || endpoint_pass.present? && !options.include?(:basic_auth)
|
82
|
-
options.merge({ basic_auth: { username: endpoint_user, password: endpoint_pass } })
|
83
|
-
else
|
84
|
-
options
|
85
|
-
end
|
86
|
-
end
|
87
|
-
|
88
|
-
def build_options(options)
|
89
|
-
options = {} if options.blank?
|
90
|
-
set_basic_auth_credentials(options)
|
91
|
-
end
|
92
|
-
|
93
|
-
def successful_response?(response)
|
94
|
-
HTTP_SUCCESS_CODES.include?(response.code) || response.blank?
|
95
|
-
end
|
96
|
-
|
97
|
-
end
|
98
|
-
|
99
|
-
end
|
100
|
-
end
|
data/lib/smooth_operator/orm.rb
DELETED
@@ -1,154 +0,0 @@
|
|
1
|
-
require "smooth_operator/exceptions"
|
2
|
-
|
3
|
-
module SmoothOperator
|
4
|
-
module ORM
|
5
|
-
|
6
|
-
def self.included(base)
|
7
|
-
base.extend(ClassMethods)
|
8
|
-
base.send(:attr_reader, :last_response)
|
9
|
-
end
|
10
|
-
|
11
|
-
module ClassMethods
|
12
|
-
|
13
|
-
attr_writer :save_attr_black_list
|
14
|
-
def save_attr_black_list
|
15
|
-
@save_attr_black_list ||= [:id, :created_at, :updated_at]
|
16
|
-
end
|
17
|
-
|
18
|
-
attr_writer :save_attr_white_list
|
19
|
-
def save_attr_white_list
|
20
|
-
@save_attr_white_list ||= []
|
21
|
-
end
|
22
|
-
|
23
|
-
def find(id, options = {})
|
24
|
-
if id == :all
|
25
|
-
find_each(options)
|
26
|
-
else
|
27
|
-
find_one(id, options)
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
def safe_find(id, options = {})
|
32
|
-
begin
|
33
|
-
find(id, options)
|
34
|
-
rescue Exception => exception #exception.response contains the server response
|
35
|
-
id == :all ? [] : nil
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
attr_writer :model_name
|
40
|
-
def model_name
|
41
|
-
@model_name ||= name.split('::').last.underscore.capitalize
|
42
|
-
end
|
43
|
-
|
44
|
-
def model_name_downcase
|
45
|
-
model_name.downcase
|
46
|
-
end
|
47
|
-
|
48
|
-
attr_writer :table_name
|
49
|
-
def table_name
|
50
|
-
@table_name ||= model_name_downcase.to_s.pluralize
|
51
|
-
end
|
52
|
-
|
53
|
-
private #------------------------------------------------ private
|
54
|
-
|
55
|
-
def instantiate_each(objects)
|
56
|
-
objects.map { |object| new(object) }
|
57
|
-
end
|
58
|
-
|
59
|
-
def find_each(options)
|
60
|
-
response = get(nil, options)
|
61
|
-
parsed_response = parse_response_or_raise_proper_exception(response)
|
62
|
-
parsed_response.kind_of?(Array) ? instantiate_each(parsed_response) : parsed_response
|
63
|
-
end
|
64
|
-
|
65
|
-
def find_one(id, options)
|
66
|
-
response = get(id, options)
|
67
|
-
parsed_response = parse_response_or_raise_proper_exception(response)
|
68
|
-
new(parsed_response)
|
69
|
-
end
|
70
|
-
|
71
|
-
def parse_response_or_raise_proper_exception(response)
|
72
|
-
if successful_response?(response)
|
73
|
-
response.parsed_response
|
74
|
-
else
|
75
|
-
SmoothOperator::Exceptions.raise_proper_exception(response)
|
76
|
-
end
|
77
|
-
end
|
78
|
-
|
79
|
-
end
|
80
|
-
|
81
|
-
def save
|
82
|
-
begin
|
83
|
-
save!
|
84
|
-
rescue Exception => exception
|
85
|
-
false
|
86
|
-
end
|
87
|
-
end
|
88
|
-
|
89
|
-
def save!
|
90
|
-
@last_response = create_or_update
|
91
|
-
import_response_errors(@last_response)
|
92
|
-
SmoothOperator::Exceptions.raise_proper_exception(@last_response) unless self.class.successful_response?(@last_response)
|
93
|
-
true
|
94
|
-
end
|
95
|
-
|
96
|
-
def destroy
|
97
|
-
return true if new_record?
|
98
|
-
|
99
|
-
@last_response = self.class.delete(self.id)
|
100
|
-
import_response_errors(@last_response)
|
101
|
-
self.class.successful_response?(@last_response)
|
102
|
-
end
|
103
|
-
|
104
|
-
def new_record?
|
105
|
-
!persisted?
|
106
|
-
end
|
107
|
-
|
108
|
-
def persisted?
|
109
|
-
try(:id).present?
|
110
|
-
end
|
111
|
-
|
112
|
-
def to_partial_path
|
113
|
-
class_name_plural = self.class.table_name
|
114
|
-
"#{class_name_plural}/#{class_name_plural.singularize}"
|
115
|
-
end
|
116
|
-
|
117
|
-
def valid?
|
118
|
-
errors.blank?
|
119
|
-
end
|
120
|
-
|
121
|
-
def invalid?
|
122
|
-
!valid?
|
123
|
-
end
|
124
|
-
|
125
|
-
def hash_of_safe_content
|
126
|
-
safe_hash = hash_of_full_content.dup
|
127
|
-
|
128
|
-
if self.class.save_attr_white_list.present?
|
129
|
-
safe_hash.slice!(*self.class.save_attr_white_list)
|
130
|
-
else
|
131
|
-
self.class.save_attr_black_list.each { |attribute| safe_hash.delete(attribute) }
|
132
|
-
end
|
133
|
-
|
134
|
-
safe_hash
|
135
|
-
end
|
136
|
-
|
137
|
-
private #-------------------------------------- private
|
138
|
-
|
139
|
-
def create_or_update
|
140
|
-
if new_record?
|
141
|
-
self.class.post('', { self.class.model_name_downcase => hash_of_safe_content })
|
142
|
-
else
|
143
|
-
self.class.put(self.id, { self.class.model_name_downcase => hash_of_safe_content })
|
144
|
-
end
|
145
|
-
end
|
146
|
-
|
147
|
-
def import_response_errors(response)
|
148
|
-
if response.present? && response.parsed_response.include?('errors')
|
149
|
-
self.errors = response.parsed_response['errors']
|
150
|
-
end
|
151
|
-
end
|
152
|
-
|
153
|
-
end
|
154
|
-
end
|
@@ -1,26 +0,0 @@
|
|
1
|
-
require 'httparty'
|
2
|
-
|
3
|
-
module SmoothOperator
|
4
|
-
class ProtocolHandler
|
5
|
-
|
6
|
-
include ::HTTParty
|
7
|
-
format :json
|
8
|
-
|
9
|
-
def self.get_options(options)
|
10
|
-
{ query: options }
|
11
|
-
end
|
12
|
-
|
13
|
-
def self.post_options(options)
|
14
|
-
{ body: options }
|
15
|
-
end
|
16
|
-
|
17
|
-
def self.put_options(options)
|
18
|
-
{ body: options }
|
19
|
-
end
|
20
|
-
|
21
|
-
def self.delete_options(options)
|
22
|
-
{ body: options }
|
23
|
-
end
|
24
|
-
|
25
|
-
end
|
26
|
-
end
|