apicasso_brush 0.1.0b → 0.1.1

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 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: []