smooth_operator 0.4.4 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (68) hide show
  1. checksums.yaml +9 -9
  2. data/.gitignore +2 -1
  3. data/.rspec +4 -0
  4. data/Gemfile +13 -0
  5. data/README.md +258 -10
  6. data/console.rb +44 -0
  7. data/lib/smooth_operator/array_with_meta_data.rb +31 -0
  8. data/lib/smooth_operator/attribute_assignment.rb +102 -0
  9. data/lib/smooth_operator/attribute_methods.rb +87 -0
  10. data/lib/smooth_operator/attributes/base.rb +107 -0
  11. data/lib/smooth_operator/attributes/dirty.rb +29 -0
  12. data/lib/smooth_operator/attributes/normal.rb +15 -0
  13. data/lib/smooth_operator/delegation.rb +60 -0
  14. data/lib/smooth_operator/finder_methods.rb +43 -0
  15. data/lib/smooth_operator/helpers.rb +79 -0
  16. data/lib/smooth_operator/model_schema.rb +81 -0
  17. data/lib/smooth_operator/open_struct.rb +37 -0
  18. data/lib/smooth_operator/operator.rb +145 -0
  19. data/lib/smooth_operator/operators/faraday.rb +75 -0
  20. data/lib/smooth_operator/operators/typhoeus.rb +77 -0
  21. data/lib/smooth_operator/persistence.rb +144 -0
  22. data/lib/smooth_operator/relation/array_relation.rb +13 -0
  23. data/lib/smooth_operator/relation/association_reflection.rb +75 -0
  24. data/lib/smooth_operator/relation/associations.rb +75 -0
  25. data/lib/smooth_operator/relation/reflection.rb +41 -0
  26. data/lib/smooth_operator/relation/single_relation.rb +14 -0
  27. data/lib/smooth_operator/remote_call/base.rb +80 -0
  28. data/lib/smooth_operator/remote_call/errors/connection_failed.rb +20 -0
  29. data/lib/smooth_operator/remote_call/errors/timeout.rb +20 -0
  30. data/lib/smooth_operator/remote_call/faraday.rb +19 -0
  31. data/lib/smooth_operator/remote_call/typhoeus.rb +19 -0
  32. data/lib/smooth_operator/serialization.rb +79 -0
  33. data/lib/smooth_operator/translation.rb +27 -0
  34. data/lib/smooth_operator/validations.rb +15 -0
  35. data/lib/smooth_operator/version.rb +1 -1
  36. data/lib/smooth_operator.rb +26 -5
  37. data/smooth_operator.gemspec +12 -3
  38. data/spec/factories/user_factory.rb +34 -0
  39. data/spec/require_helper.rb +11 -0
  40. data/spec/smooth_operator/attribute_assignment_spec.rb +351 -0
  41. data/spec/smooth_operator/attributes_dirty_spec.rb +53 -0
  42. data/spec/smooth_operator/delegation_spec.rb +139 -0
  43. data/spec/smooth_operator/finder_methods_spec.rb +105 -0
  44. data/spec/smooth_operator/model_schema_spec.rb +31 -0
  45. data/spec/smooth_operator/operator_spec.rb +46 -0
  46. data/spec/smooth_operator/persistence_spec.rb +424 -0
  47. data/spec/smooth_operator/remote_call_spec.rb +320 -0
  48. data/spec/smooth_operator/serialization_spec.rb +80 -0
  49. data/spec/smooth_operator/validations_spec.rb +42 -0
  50. data/spec/spec_helper.rb +25 -0
  51. data/spec/support/helpers/persistence_helper.rb +38 -0
  52. data/spec/support/localhost_server.rb +97 -0
  53. data/spec/support/models/address.rb +14 -0
  54. data/spec/support/models/comment.rb +3 -0
  55. data/spec/support/models/post.rb +13 -0
  56. data/spec/support/models/user.rb +41 -0
  57. data/spec/support/models/user_with_address_and_posts.rb +89 -0
  58. data/spec/support/test_server.rb +165 -0
  59. metadata +108 -18
  60. data/lib/smooth_operator/base.rb +0 -30
  61. data/lib/smooth_operator/core.rb +0 -218
  62. data/lib/smooth_operator/http_handlers/typhoeus/base.rb +0 -58
  63. data/lib/smooth_operator/http_handlers/typhoeus/orm.rb +0 -34
  64. data/lib/smooth_operator/http_handlers/typhoeus/remote_call.rb +0 -28
  65. data/lib/smooth_operator/operator/base.rb +0 -43
  66. data/lib/smooth_operator/operator/exceptions.rb +0 -64
  67. data/lib/smooth_operator/operator/orm.rb +0 -118
  68. 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