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 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 [![Build Status](https://travis-ci.com/ErvalhouS/apicasso_brush.svg?branch=master)](https://travis-ci.com/ErvalhouS/apicasso_brush) [![Maintainability](https://api.codeclimate.com/v1/badges/5286908f324e1446e1ac/maintainability)](https://codeclimate.com/github/ErvalhouS/apicasso_brush/maintainability) [![Test Coverage](https://api.codeclimate.com/v1/badges/5286908f324e1446e1ac/test_coverage)](https://codeclimate.com/github/ErvalhouS/apicasso_brush/test_coverage) [![Inline docs](http://inch-ci.org/github/ErvalhouS/apicasso_brush.svg?branch=master)](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,4 @@
1
+ module ApicassoBrush
2
+ class Railtie < ::Rails::Railtie
3
+ end
4
+ end
@@ -0,0 +1,3 @@
1
+ module ApicassoBrush
2
+ VERSION = '0.1.0b'
3
+ end
@@ -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
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :apicasso_brush do
3
+ # # Task goes here
4
+ # 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: []