apicasso_brush 0.1.0b

Sign up to get free protection for your applications and to get access to all the features.
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: []