apicasso_brush 0.1.0b
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 +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +42 -0
- data/Rakefile +27 -0
- data/lib/apicasso_brush/railtie.rb +4 -0
- data/lib/apicasso_brush/version.rb +3 -0
- data/lib/apicasso_brush.rb +204 -0
- data/lib/tasks/apicasso_brush_tasks.rake +4 -0
- metadata +57 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 9d687264e478c7398bac882c02edd348896975b1e4fda478a14fb1531d977a36
|
4
|
+
data.tar.gz: cba93721ac292c43fc66c46ecebe66bac92a54ca6f2c1f9a278ecc48527a35b7
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 9ad23ef2f60ba6aa4467707d449bec97b1f2349df7518c41c1fb5e911e02562c63c04bdc7d622fb383acb3004fb43d8193f3ee5e83eb19400493d9820a426075
|
7
|
+
data.tar.gz: ac979f16a84c275f6370b16800b911eefe5695fb852364c52c6248238dbb7408a21ae8039f749238d0c73dc4371892b4af322bd898c6d7e906fe8259fc9609c3
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2019 Fernando Bellincanta
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
# APIcasso Brush [](https://travis-ci.com/ErvalhouS/apicasso_brush) [](https://codeclimate.com/github/ErvalhouS/apicasso_brush/maintainability) [](https://codeclimate.com/github/ErvalhouS/apicasso_brush/test_coverage) [](http://inch-ci.org/github/ErvalhouS/apicasso_brush)
|
2
|
+
## Consume your APIcasso microservices
|
3
|
+
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:
|
4
|
+
|
5
|
+
- `find()`
|
6
|
+
- `all()`
|
7
|
+
- `where()`
|
8
|
+
- `save`
|
9
|
+
|
10
|
+
Instead of translating those calls into ORM **APIcasso Brush** retrieves it's data from your configured service. This makes it possible to make a convergent application, that gets data from multiple API sources.
|
11
|
+
|
12
|
+
## Usage
|
13
|
+
To start consuming from your services just create a class inheriting from `Apicasso::Brush` and declare how APIcasso should stroke the brush. First declare the base for current class, which is a index action from your API for the given resource, then pass a token that has access into that resource.
|
14
|
+
|
15
|
+
```ruby
|
16
|
+
class ModelFromService < Apicasso::Brush
|
17
|
+
brush_on 'http://my.service.com/model',
|
18
|
+
token: '5e1057e7a51ee7a55',
|
19
|
+
include: :a_relation, :a_method # Optional
|
20
|
+
end
|
21
|
+
```
|
22
|
+
You can also by default include relations or methods that should get built into your `ModelFromService` objects.
|
23
|
+
|
24
|
+
## Installation
|
25
|
+
Add this line to your application's Gemfile:
|
26
|
+
|
27
|
+
```ruby
|
28
|
+
gem 'apicasso_brush'
|
29
|
+
```
|
30
|
+
|
31
|
+
And then execute:
|
32
|
+
```bash
|
33
|
+
$ bundle
|
34
|
+
```
|
35
|
+
|
36
|
+
Or install it yourself as:
|
37
|
+
```bash
|
38
|
+
$ gem install apicasso_brush
|
39
|
+
```
|
40
|
+
|
41
|
+
## License
|
42
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
begin
|
2
|
+
require 'bundler/setup'
|
3
|
+
rescue LoadError
|
4
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
5
|
+
end
|
6
|
+
|
7
|
+
require 'rdoc/task'
|
8
|
+
|
9
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
10
|
+
rdoc.rdoc_dir = 'rdoc'
|
11
|
+
rdoc.title = 'ApicassoBrush'
|
12
|
+
rdoc.options << '--line-numbers'
|
13
|
+
rdoc.rdoc_files.include('README.md')
|
14
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
15
|
+
end
|
16
|
+
|
17
|
+
require 'bundler/gem_tasks'
|
18
|
+
|
19
|
+
require 'rake/testtask'
|
20
|
+
|
21
|
+
Rake::TestTask.new(:test) do |t|
|
22
|
+
t.libs << 'test'
|
23
|
+
t.pattern = 'test/**/*_test.rb'
|
24
|
+
t.verbose = false
|
25
|
+
end
|
26
|
+
|
27
|
+
task default: :test
|
@@ -0,0 +1,204 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "apicasso_brush/railtie"
|
4
|
+
|
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
|
metadata
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: apicasso_brush
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0b
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Fernando Bellincanta
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
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`
|
20
|
+
email:
|
21
|
+
- ervalhous@hotmail.com
|
22
|
+
executables: []
|
23
|
+
extensions: []
|
24
|
+
extra_rdoc_files: []
|
25
|
+
files:
|
26
|
+
- MIT-LICENSE
|
27
|
+
- README.md
|
28
|
+
- Rakefile
|
29
|
+
- lib/apicasso_brush.rb
|
30
|
+
- lib/apicasso_brush/railtie.rb
|
31
|
+
- lib/apicasso_brush/version.rb
|
32
|
+
- lib/tasks/apicasso_brush_tasks.rake
|
33
|
+
homepage: https://github.com/ErvalhouS/APIcasso_Brush
|
34
|
+
licenses:
|
35
|
+
- MIT
|
36
|
+
metadata: {}
|
37
|
+
post_install_message:
|
38
|
+
rdoc_options: []
|
39
|
+
require_paths:
|
40
|
+
- lib
|
41
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
42
|
+
requirements:
|
43
|
+
- - ">="
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
47
|
+
requirements:
|
48
|
+
- - ">"
|
49
|
+
- !ruby/object:Gem::Version
|
50
|
+
version: 1.3.1
|
51
|
+
requirements: []
|
52
|
+
rubyforge_project:
|
53
|
+
rubygems_version: 2.7.7
|
54
|
+
signing_key:
|
55
|
+
specification_version: 4
|
56
|
+
summary: Consume your APIcasso microservices
|
57
|
+
test_files: []
|