apicasso_brush 0.1.0b → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9d687264e478c7398bac882c02edd348896975b1e4fda478a14fb1531d977a36
4
- data.tar.gz: cba93721ac292c43fc66c46ecebe66bac92a54ca6f2c1f9a278ecc48527a35b7
3
+ metadata.gz: 8b19e94043a6ffdbb970fdcb3d2d7a278e11100a08c430265706df50346b352c
4
+ data.tar.gz: 8c09dc87c6fbe6385282b872ff68526019c5aedad7a47ff118becbdfb175cb09
5
5
  SHA512:
6
- metadata.gz: 9ad23ef2f60ba6aa4467707d449bec97b1f2349df7518c41c1fb5e911e02562c63c04bdc7d622fb383acb3004fb43d8193f3ee5e83eb19400493d9820a426075
7
- data.tar.gz: ac979f16a84c275f6370b16800b911eefe5695fb852364c52c6248238dbb7408a21ae8039f749238d0c73dc4371892b4af322bd898c6d7e906fe8259fc9609c3
6
+ metadata.gz: d2b972d420190fc42d1a07a026473467617726ceb3f7d3b0daf958b31ca1a06f06956a40316221d52a8a641608c08d289c26b3f88ae61d8f066e2e2fead1d72f
7
+ data.tar.gz: 02b45578d4324c291827f1523e21e479ace990383d9b3a506566d5fd2be6db0e4986b22bad61acb8c21b897d5adafecfdb71beca6ede21a7306ff32f14a2591b
@@ -0,0 +1,262 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Apicasso
4
+ # A module to inject Autoracing data consumption behavior
5
+ class Brush
6
+ # A plugger to classes on a service using APIcasso.
7
+ # Receives the URL to get an object from the API.
8
+ def self.brush_on(resource_url = nil, opts = {})
9
+ if resource_url.nil?
10
+ raise Exception, "Configuration error.\nYou should pass the URL for your APIcasso resource as the first parameter\n# => brush_on 'https://my.api/path/to/resource'"
11
+ elsif opts[:token].nil?
12
+ raise Exception, "Configuration error.\nYou should pass a token option to authenticate.\n# => brush_on 'https://my.api/path/to/resource', token: '5e1o5ba77ca7f0d4'"
13
+ end
14
+
15
+ # Interface into options set on initialization
16
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
17
+ # Token used to connect into resource's APIcasso instance
18
+ def self.apicasso_token
19
+ "#{opts[:token]}"
20
+ end
21
+ # Base URL to resource, used to query from APIcasso instance
22
+ def self.resource_url
23
+ "#{resource_url}"
24
+ end
25
+ # Method that sets a default include query on this resource
26
+ def self.getter_include
27
+ "include=#{opts[:include].try(:map, &:to_s).try(:join, ',')}" if "#{opts[:include]}".present?
28
+ end
29
+ RUBY
30
+ end
31
+
32
+ # Constructor that can receive the ID of the object to search as first parameter
33
+ # and/or object attributes as second parameters
34
+ def initialize(id = nil, object = {})
35
+ @object = object
36
+ @id = id || object[:id]
37
+ instantiate if id.present? && !object.present?
38
+ end
39
+
40
+ # Instantiates the object by getting it from APIcasso
41
+ def instantiate
42
+ objectify! URI.parse(getter_url)
43
+ end
44
+
45
+ # Base getter URL for current resource
46
+ def getter_url
47
+ self.class.resource_url + @id.to_s + '?' + self.class.getter_include.to_s
48
+ end
49
+
50
+ # Attributes from this object
51
+ def attributes
52
+ @object
53
+ end
54
+
55
+ # Overriding `method_missing` to enable fields being retrieved from @object
56
+ def method_missing(meth, *args, &block)
57
+ @object[meth.to_sym] || super
58
+ end
59
+
60
+ # Reloads the object by getting from APIcasso
61
+ def reload!
62
+ instantiate if @id.present?
63
+ end
64
+
65
+ # Reloads @object variable by receiving the URL used to get the object
66
+ def objectify!(url)
67
+ @object = get_object(url)
68
+ end
69
+
70
+ # Saves current object to APIcasso
71
+ def save
72
+ @object = save_object(URI.parse(getter_url)) if @object.is_a? Hash
73
+ @id = @object[:id]
74
+ end
75
+
76
+ # Updates current object to APIcasso
77
+ def update(opts = {})
78
+ @object.merge(opts)
79
+ @object = save_object(URI.parse(getter_url)) if @object.is_a? Hash
80
+ @id = @object[:id]
81
+ end
82
+
83
+ # Create objects, either using batch or individual request.
84
+ def self.update(opts = {})
85
+ if opts.is_a? Array
86
+ url = URI.parse("#{resource_url.chomp('/' + resource_url.split('/').last)}/batch_update")
87
+ http = Net::HTTP.new(url.host, url.port)
88
+
89
+ brush_collection(JSON.parse(retrieve(http, patch_request(url)).read_body))
90
+ elsif opts.is_a? Hash
91
+ object = find(opts[:id])
92
+ object.update(opts)
93
+ end
94
+ end
95
+
96
+ # Lists all objects from APIcasso of this class.
97
+ # Can receive a hash of parameters to be passed
98
+ # as query string into the APIcasso instance
99
+ def self.all(opts = {})
100
+ query = "?per_page=-1#{url_encode(opts)}&#{self.getter_include}"
101
+ url = URI.parse("#{resource_url}#{query}")
102
+ http = Net::HTTP.new(url.host, url.port)
103
+
104
+ response = JSON.parse(retrieve(http, get_request(url)).read_body).deep_symbolize_keys
105
+ brush_collection(response)
106
+ end
107
+
108
+ # Create objects, either using batch or individual request.
109
+ def self.create(opts = {})
110
+ if opts.is_a? Array
111
+ url = URI.parse("#{resource_url.chomp('/' + resource_url.split('/').last)}/batch_create")
112
+ http = Net::HTTP.new(url.host, url.port)
113
+
114
+ brush_collection(JSON.parse(retrieve(http, post_request(url, opts)).read_body))
115
+ else
116
+ http = Net::HTTP.new(url.host, url.port)
117
+ url = URI.parse(resource_url.to_s)
118
+
119
+ new(nil, JSON.parse(retrieve(http, post_request(url, opts)).read_body))
120
+ end
121
+ end
122
+
123
+ # Finds object based on opts finder, if no record is find it
124
+ # creates one using individual request.
125
+ def self.find_or_create_by(opts = {})
126
+ raise ::Exception, 'Pass attributes as hash' unless opts.is_a? Hash
127
+
128
+ object = nil
129
+ unless object = find_by(opts)
130
+ object = create(opts)
131
+ end
132
+ object
133
+ end
134
+
135
+ # Finds a object based on attributes conditions
136
+ def self.find_by(opts = {})
137
+ where(opts)[:entries][0]
138
+ end
139
+
140
+ # Finds a object based on attributes conditions,
141
+ # raises not found exception when query has no match
142
+ def self.find_by!(opts = {})
143
+ object = find_by(opts)
144
+ raise ::Exception, 'Not Found' if object.nil?
145
+ end
146
+
147
+ # Finds a resource with the given id
148
+ def self.find(id)
149
+ new(id)
150
+ end
151
+
152
+ # Returns an array of objects that matches a given query,
153
+ # which is the first parameter. This query can be a string of an
154
+ # ransack URL query or a hash where the keys would be *_eq operators.
155
+ def self.where(query = '', opts = {})
156
+ query = "?per_page=-1#{stringfy_ransackable(query)}#{url_encode(opts)}&#{getter_include}"
157
+ url = URI.parse("#{resource_url}#{query}")
158
+ http = Net::HTTP.new(url.host, url.port)
159
+
160
+ response = JSON.parse(retrieve(http, get_request(url)).read_body).deep_symbolize_keys
161
+ brush_collection(response)
162
+ end
163
+
164
+ # Destroys current object on APIcasso
165
+ def destroy
166
+ @object = @id = delete_object(URI.parse(getter_url))
167
+ end
168
+
169
+ alias delete destroy
170
+
171
+ private
172
+
173
+ def get_object(url)
174
+ http = Net::HTTP.new(url.host, url.port)
175
+ JSON.parse(self.class.retrieve(http, self.class.get_request(url)).read_body).deep_symbolize_keys
176
+ end
177
+
178
+ def save_object(url)
179
+ http = Net::HTTP.new(url.host, url.port)
180
+ meth = (@id.present? ? 'patch' : 'post')
181
+ JSON.parse(self.class.retrieve(http, self.class.send("#{meth}_request", url, @object)).read_body).deep_symbolize_keys
182
+ end
183
+
184
+ def delete_object(url)
185
+ http = Net::HTTP.new(url.host, url.port)
186
+ self.class.retrieve(http, self.class.delete_request(url))
187
+ end
188
+
189
+ class << self
190
+ def stringfy_ransackable(query = nil)
191
+ if query.is_a? String
192
+ '&' + query
193
+ elsif query.is_a? Hash
194
+ '&q={' + query.map do |key, value|
195
+ "\"#{key}_eq\": \"#{value}\""
196
+ end.join(',') + '}'
197
+ end
198
+ end
199
+
200
+ def url_encode(query = {})
201
+ return if query.nil?
202
+
203
+ '&' + query.map do |key, value|
204
+ "#{key}=#{value}"
205
+ end.join('&')
206
+ end
207
+
208
+ def retrieve(http, request)
209
+ response = http.request(request)
210
+ check_success response.code
211
+ response
212
+ end
213
+
214
+ def check_success(status)
215
+ case status
216
+ when '404', 404
217
+ raise ::Exception, 'Resource not found, are you sure you have configured your `brush_on` URL'
218
+ when '401', 401
219
+ raise ::Exception, 'Invalid Token'
220
+ when '403', 403
221
+ raise ::Exception, "Not authorized to #{request::METHOD} on resource"
222
+ else
223
+ raise ::Exception, 'Error when fetching from APIcasso' if status.to_i > 400
224
+ end
225
+ end
226
+
227
+ def delete_request(url)
228
+ request = Net::HTTP::Delete.new(url)
229
+ request['Authorization'] = "Token token=#{apicasso_token}"
230
+ request
231
+ end
232
+
233
+ def get_request(url)
234
+ request = Net::HTTP::Get.new(url)
235
+ request['Authorization'] = "Token token=#{apicasso_token}"
236
+ request
237
+ end
238
+
239
+ def patch_request(url, body)
240
+ request = Net::HTTP::Patch.new(url)
241
+ request['Authorization'] = "Token token=#{apicasso_token}"
242
+ request['Content-Type'] = 'application/json'
243
+ request.body = { name.underscore => body }.to_json
244
+ request
245
+ end
246
+
247
+ def post_request(url, body)
248
+ request = Net::HTTP::Post.new(url)
249
+ request['Authorization'] = "Token token=#{apicasso_token}"
250
+ request['Content-Type'] = 'application/json'
251
+ request.body = { name.underscore => body }.to_json
252
+ request
253
+ end
254
+
255
+ def brush_collection(response)
256
+ (response.is_a?(Hash) ? response[:entries] : response).map do |object|
257
+ new(nil, object)
258
+ end
259
+ end
260
+ end
261
+ end
262
+ end
@@ -1,204 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "apicasso_brush/railtie"
3
+ require 'apicasso_brush/railtie'
4
+ require 'apicasso/brush'
4
5
 
5
- module Apicasso
6
- # A module to inject Autoracing data consumption behavior
7
- class Brush
8
- # A plugger to classes on a service using APIcasso.
9
- # Receives the URL to get an object from the API.
10
- def self.brush_on(resource_url = nil, opts = {})
11
- if resource_url.nil?
12
- raise Exception.new("Configuration error.\nYou should pass the URL for your APIcasso resource as the first parameter\n# => brush_on 'https://my.api/path/to/resource'")
13
- elsif opts[:token].nil?
14
- raise Exception.new("Configuration error.\nYou should pass a token option to authenticate.\n# => brush_on 'https://my.api/path/to/resource', token: '5e1o5ba77ca7f0d4'")
15
- end
16
-
17
- # Interface into options set on initialization
18
- class_eval <<-RUBY, __FILE__, __LINE__+1
19
- # Token used to connect into resource's APIcasso instance
20
- def self.apicasso_token
21
- "#{opts[:token]}"
22
- end
23
- # Base URL to resource, used to query from APIcasso instance
24
- def self.resource_url
25
- "#{resource_url}"
26
- end
27
- # Method that sets a default include query on this resource
28
- def getter_include
29
- "?include=#{opts[:include].try(:map, &:to_s).try(:join, ',')}" if "#{opts[:include]}".present?
30
- end
31
- RUBY
32
- end
33
-
34
- # Constructor that can receive the ID of the object to search as first parameter
35
- # and/or object attributes as second parameters
36
- def initialize(id = nil, object = {})
37
- @object = object
38
- @id = id || object[:id]
39
- instantiate if id.present? && !object.present?
40
- end
41
-
42
- # Instantiates the object by getting it from APIcasso
43
- def instantiate
44
- objectify! URI.parse(getter_url)
45
- end
46
-
47
- # Base getter URL for current resource
48
- def getter_url
49
- self.class.resource_url + @id.to_s + getter_include.to_s
50
- end
51
-
52
- # Attributes from this object
53
- def attributes
54
- @object
55
- end
56
-
57
- # Reloads the object by getting from APIcasso
58
- def reload!
59
- instantiate if @id.present?
60
- end
61
-
62
- # Reloads @object variable by receiving the URL used to get the object
63
- def objectify!(url)
64
- @object = get_object(url)
65
- end
66
-
67
- # Overrides method missing so that it points attribute
68
- # getters and setters into the object itself
69
- def method_missing(meth, *args, &block)
70
- if meth.to_s.ends_with?('=')
71
- @object[meth.to_s.delete('=').to_sym] = args.first
72
- else
73
- @object[meth.to_sym]
74
- end
75
- rescue StandardError
76
- super
77
- end
78
-
79
- # Avoid inconsistencies on respond_to? calls
80
- def respond_to_missing?(meth, include_private = false)
81
- meth.to_s.ends_with?('=') || @object.key?(meth) || super
82
- end
83
-
84
- # Saves current object to APIcasso
85
- def save
86
- @object = save_object(URI.parse(getter_url)) if @object.is_a? Hash
87
- @id = @object[:id]
88
- end
89
-
90
- # Lists all objects from APIcasso of this class.
91
- # Can receive a hash of parameters to be passed
92
- # as query string into the APIcasso instance
93
- def self.all(opts = {})
94
- query = "?per_page=-1#{url_encode(opts)}"
95
- url = URI.parse("#{self.resource_url}#{query}")
96
- http = Net::HTTP.new(url.host, url.port)
97
-
98
- JSON.parse(retrieve(http, get_request(url)).read_body).deep_symbolize_keys.tap do |response|
99
- response[:entries] = response[:entries].map { |object| Lead.new(nil, object) }
100
- end
101
- end
102
-
103
- # Finds a resource with the given id
104
- def self.find(id)
105
- new(id)
106
- end
107
-
108
- # Returns an array of objects that matches a given query,
109
- # which is the first parameter. This query can be a string of an
110
- # ransack URL query or a hash where the keys would be *_eq operators.
111
- def self.where(query = "", opts = {})
112
- query = "?per_page=-1#{stringfy_ransackable(query)}#{url_encode(opts)}"
113
- url = URI.parse("#{self.resource_url}#{query}")
114
- http = Net::HTTP.new(url.host, url.port)
115
-
116
- JSON.parse(retrieve(http, get_request(url)).read_body).deep_symbolize_keys.tap do |response|
117
- response[:entries] = response[:entries].map { |object| Lead.new(nil, object) }
118
- end
119
- end
120
-
121
- private
122
-
123
- def get_object(url)
124
- http = Net::HTTP.new(url.host, url.port)
125
-
126
- JSON.parse(self.class.retrieve(http, self.class.get_request(url)).read_body).deep_symbolize_keys
127
- end
128
-
129
- def save_object(url)
130
- http = Net::HTTP.new(url.host, url.port)
131
- meth = (@id.present? ? "patch" : "post")
132
- JSON.parse(self.class.retrieve(http, self.class.send("#{meth}_request", url, @object)).read_body).deep_symbolize_keys
133
- end
134
-
135
- private_class_method :stringfy_ransackable, :url_encode, :retrieve,
136
- :check_success, :delete_request, :get_request,
137
- :patch_request, :post_request
138
-
139
- def self.stringfy_ransackable(query = nil)
140
- if query.is_a? String
141
- '&' + query
142
- elsif query.is_a? Hash
143
- '&q={' + query.map do |key, value|
144
- "\"#{key}_eq\": \"#{value}\""
145
- end.join(',') + '}'
146
- end
147
- end
148
-
149
- def self.url_encode(query = {})
150
- return if query.nil?
151
-
152
- '&' + query.map do |key, value|
153
- "#{key}=#{value}"
154
- end.join('&')
155
- end
156
-
157
- def self.retrieve(http, request)
158
- response = http.request(request)
159
- check_success response.code
160
- response
161
- end
162
-
163
- def self.check_success(status)
164
- case status
165
- when '404', 404
166
- raise ::Exception.new("Resource not found, are you sure you have configured your `brush_on` URL")
167
- when '401', 401
168
- raise ::Exception.new("Invalid Token")
169
- when '403', 403
170
- raise ::Exception.new("Not authorized to #{request::METHOD} on resource")
171
- else
172
- raise ::Exception.new("Error when fetching from APIsso")
173
- end
174
- end
175
-
176
- def self.delete_request(url)
177
- request = Net::HTTP::Delete.new(url)
178
- request['Authorization'] = "Token token=#{apicasso_token}"
179
- request
180
- end
181
-
182
- def self.get_request(url)
183
- request = Net::HTTP::Get.new(url)
184
- request['Authorization'] = "Token token=#{apicasso_token}"
185
- request
186
- end
187
-
188
- def self.patch_request(url, body)
189
- request = Net::HTTP::Patch.new(url)
190
- request['Authorization'] = "Token token=#{apicasso_token}"
191
- request['Content-Type'] = 'application/json'
192
- request.body = { name.underscore => body }.to_json
193
- request
194
- end
195
-
196
- def self.post_request(url, body)
197
- request = Net::HTTP::Post.new(url)
198
- request['Authorization'] = "Token token=#{apicasso_token}"
199
- request['Content-Type'] = 'application/json'
200
- request.body = { name.underscore => body }.to_json
201
- request
202
- end
203
- end
204
- end
6
+ module ApicassoBrush
7
+ end
@@ -1,3 +1,3 @@
1
1
  module ApicassoBrush
2
- VERSION = '0.1.0b'
2
+ VERSION = '0.1.1'
3
3
  end
metadata CHANGED
@@ -1,22 +1,46 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: apicasso_brush
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0b
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Fernando Bellincanta
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-02-14 00:00:00.000000000 Z
12
- dependencies: []
13
- description: |-
14
- This is a client to consume data from microservices built upon [APIcasso](https://github.com/ErvalhouS/apicasso). It makes PORO classes supercharged by injecting Rails-like behavior through the methods:
15
-
16
- - `find()`
17
- - `all()`
18
- - `where()`
19
- - `save`
11
+ date: 2019-04-15 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">"
18
+ - !ruby/object:Gem::Version
19
+ version: '5'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">"
25
+ - !ruby/object:Gem::Version
26
+ version: '5'
27
+ - !ruby/object:Gem::Dependency
28
+ name: sqlite3
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ description: Instead of translating method calls into ORM, APIcasso Brush retrieves
42
+ it's data from your configured service. This makes it possible to make a convergent
43
+ application, that gets data from multiple API sources.
20
44
  email:
21
45
  - ervalhous@hotmail.com
22
46
  executables: []
@@ -26,11 +50,12 @@ files:
26
50
  - MIT-LICENSE
27
51
  - README.md
28
52
  - Rakefile
53
+ - lib/apicasso/brush.rb
29
54
  - lib/apicasso_brush.rb
30
55
  - lib/apicasso_brush/railtie.rb
31
56
  - lib/apicasso_brush/version.rb
32
57
  - lib/tasks/apicasso_brush_tasks.rake
33
- homepage: https://github.com/ErvalhouS/APIcasso_Brush
58
+ homepage: https://github.com/ErvalhouS/apicasso_brush
34
59
  licenses:
35
60
  - MIT
36
61
  metadata: {}
@@ -45,13 +70,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
45
70
  version: '0'
46
71
  required_rubygems_version: !ruby/object:Gem::Requirement
47
72
  requirements:
48
- - - ">"
73
+ - - ">="
49
74
  - !ruby/object:Gem::Version
50
- version: 1.3.1
75
+ version: '0'
51
76
  requirements: []
52
77
  rubyforge_project:
53
- rubygems_version: 2.7.7
78
+ rubygems_version: 2.7.5
54
79
  signing_key:
55
80
  specification_version: 4
56
- summary: Consume your APIcasso microservices
81
+ summary: 'APIcasso Brush is a client to consume data from microservices built upon
82
+ APIcasso. It makes PORO classes supercharged by injecting Rails-like behavior through
83
+ the methods:'
57
84
  test_files: []