axel 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/.gitignore +21 -0
- data/.octopolo.yml +2 -0
- data/.rspec +2 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +23 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +271 -0
- data/Rakefile +13 -0
- data/app/models/axel/api_proxy.rb +86 -0
- data/app/models/axel/associations/base.rb +64 -0
- data/app/models/axel/associations/belongs_to.rb +43 -0
- data/app/models/axel/associations/has_many.rb +29 -0
- data/app/models/axel/associations/has_one.rb +30 -0
- data/app/models/axel/payload.rb +4 -0
- data/app/models/axel/payload/base.rb +107 -0
- data/app/models/axel/payload/errors.rb +62 -0
- data/app/models/axel/payload/metadata.rb +57 -0
- data/app/models/axel/querier.rb +166 -0
- data/app/models/axel/router.rb +119 -0
- data/app/models/axel/service_resource.rb +4 -0
- data/app/models/axel/service_resource/associations.rb +80 -0
- data/app/models/axel/service_resource/attributes.rb +23 -0
- data/app/models/axel/service_resource/automatic_resource.rb +23 -0
- data/app/models/axel/service_resource/base.rb +47 -0
- data/app/models/axel/service_resource/builder.rb +40 -0
- data/app/models/axel/service_resource/inspects.rb +17 -0
- data/app/models/axel/service_resource/payload_parser.rb +46 -0
- data/app/models/axel/service_resource/queries.rb +25 -0
- data/app/models/axel/service_resource/requesters.rb +49 -0
- data/app/models/axel/service_resource/routes.rb +19 -0
- data/app/models/axel/service_resource/typhoid_extensions.rb +134 -0
- data/app/views/axel/base/empty.json.erb +0 -0
- data/app/views/axel/base/empty.xml.builder +0 -0
- data/app/views/layouts/axel.json.jbuilder +7 -0
- data/app/views/layouts/axel.xml.builder +12 -0
- data/axel.gemspec +42 -0
- data/lib/axel.rb +56 -0
- data/lib/axel/application_extensions.rb +13 -0
- data/lib/axel/application_helper.rb +27 -0
- data/lib/axel/base_controller.rb +6 -0
- data/lib/axel/cascadable_attribute.rb +33 -0
- data/lib/axel/configurations/resource.rb +29 -0
- data/lib/axel/configurations/service.rb +28 -0
- data/lib/axel/configurator.rb +54 -0
- data/lib/axel/configurators/services.rb +29 -0
- data/lib/axel/controller_base.rb +27 -0
- data/lib/axel/controller_helpers.rb +209 -0
- data/lib/axel/controller_parameters.rb +32 -0
- data/lib/axel/engine.rb +14 -0
- data/lib/axel/inspector.rb +91 -0
- data/lib/axel/payload/remote_error.rb +14 -0
- data/lib/axel/request_options.rb +26 -0
- data/lib/axel/uri.rb +47 -0
- data/lib/axel/version.rb +3 -0
- data/lib/generators/axel/install_generator.rb +16 -0
- data/lib/generators/templates/README.md +22 -0
- data/lib/generators/templates/axel.rb +81 -0
- data/script/rails +5 -0
- data/spec/controllers/pages_controller_spec.rb +217 -0
- data/spec/dummy/README.rdoc +261 -0
- data/spec/dummy/Rakefile +7 -0
- data/spec/dummy/app/assets/javascripts/application.js +15 -0
- data/spec/dummy/app/assets/stylesheets/application.css +13 -0
- data/spec/dummy/app/controllers/application_controller.rb +3 -0
- data/spec/dummy/app/controllers/pages_controller.rb +6 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/mailers/.gitkeep +0 -0
- data/spec/dummy/app/models/.gitkeep +0 -0
- data/spec/dummy/app/views/layouts/application.html.erb +14 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/config/application.rb +62 -0
- data/spec/dummy/config/boot.rb +10 -0
- data/spec/dummy/config/database.yml +25 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +37 -0
- data/spec/dummy/config/environments/production.rb +67 -0
- data/spec/dummy/config/environments/test.rb +37 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/inflections.rb +15 -0
- data/spec/dummy/config/initializers/mime_types.rb +5 -0
- data/spec/dummy/config/initializers/secret_token.rb +7 -0
- data/spec/dummy/config/initializers/session_store.rb +8 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/locales/en.yml +5 -0
- data/spec/dummy/config/routes.rb +3 -0
- data/spec/dummy/db/development.sqlite3 +0 -0
- data/spec/dummy/db/test.sqlite3 +0 -0
- data/spec/dummy/lib/assets/.gitkeep +0 -0
- data/spec/dummy/log/.gitignore +1 -0
- data/spec/dummy/log/.gitkeep +0 -0
- data/spec/dummy/public/404.html +26 -0
- data/spec/dummy/public/422.html +26 -0
- data/spec/dummy/public/500.html +25 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/dummy/script/rails +6 -0
- data/spec/envelope_integration_check.rb +96 -0
- data/spec/helpers/axel/application_helper_spec.rb +31 -0
- data/spec/lib/axel/associations/base_spec.rb +28 -0
- data/spec/lib/axel/associations/belongs_to_spec.rb +62 -0
- data/spec/lib/axel/associations/has_many_spec.rb +23 -0
- data/spec/lib/axel/associations/has_one_spec.rb +23 -0
- data/spec/lib/axel/configurations/resource_spec.rb +15 -0
- data/spec/lib/axel/configurations/service_spec.rb +31 -0
- data/spec/lib/axel/configurator_spec.rb +26 -0
- data/spec/lib/axel/configurators/services_spec.rb +37 -0
- data/spec/lib/axel/controller_base_spec.rb +16 -0
- data/spec/lib/axel/controller_parameters_spec.rb +31 -0
- data/spec/lib/axel/inspector_spec.rb +45 -0
- data/spec/lib/axel/request_options_spec.rb +50 -0
- data/spec/lib/axel/uri_spec.rb +42 -0
- data/spec/lib/axel_spec.rb +64 -0
- data/spec/models/axel/api_proxy_spec.rb +66 -0
- data/spec/models/axel/payload/errors_spec.rb +165 -0
- data/spec/models/axel/payload/metadata_spec.rb +141 -0
- data/spec/models/axel/querier_spec.rb +158 -0
- data/spec/models/axel/router_spec.rb +115 -0
- data/spec/models/axel/service_resource/base_spec.rb +244 -0
- data/spec/models/axel/service_resource/builder_spec.rb +64 -0
- data/spec/models/axel/service_resource/payload_parser_spec.rb +60 -0
- data/spec/spec_helper.rb +39 -0
- data/spec/support/address.rb +5 -0
- data/spec/support/persona.rb +15 -0
- data/spec/support/user.rb +6 -0
- data/spec/views/axel/base/empty_spec.rb +34 -0
- metadata +508 -0
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
ZjgxOWQxNTg1OTVmYjU3OWZhMWFhZGUxMjczNDY0M2Q0Y2NlNDJkOQ==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
MTE2MjQ1ZDUwNDc4MTQyZTBmY2EwYzU0NjdmN2VhOWM5ZWY4MjZiMA==
|
7
|
+
SHA512:
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
Nzc5ODRlOWZhZjU2M2ExNTYyZmM5YTdjMWUwMmIwYWE3M2IwNjY0MmIyNmIw
|
10
|
+
NGVmZDRiMWRkMWRmZmFlOGM4ZTE5MmUzYjNkMjdkZDUxMzNlMmQ4Yjk3NjQ1
|
11
|
+
YjQ1ZGQzODczYWJiNTExMTdhYTkyZDBkNWY4ODgwNjU0YWZiM2U=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
ZWZlYWVhM2VjZGNjMjBlNGY3ODM1ZTczNTVjZWE5ZWRiZTY4OGYwYmRhN2Yx
|
14
|
+
MWUwN2JjM2UxZTk2MGU4MjQ5ODUwMTExOWMwY2NjNGJjNGMwNzgzNTA5MzI4
|
15
|
+
ZjE5MWI2NDdmMGMzNjYyM2NmODZkNGRmMmRlMWI2YWY0ZmFjMGY=
|
data/.gitignore
ADDED
data/.octopolo.yml
ADDED
data/.rspec
ADDED
data/.ruby-gemset
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
axel
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
1.9.3
|
data/.travis.yml
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
sudo: false
|
2
|
+
branches:
|
3
|
+
only:
|
4
|
+
- master
|
5
|
+
|
6
|
+
notifications:
|
7
|
+
slack: sportngin:Yr24DXJUvIOegSPwKMG099D1
|
8
|
+
|
9
|
+
after_script:
|
10
|
+
- uptime && vmstat -S M
|
11
|
+
|
12
|
+
language: ruby
|
13
|
+
rvm:
|
14
|
+
- 1.9.3
|
15
|
+
- 2.0.0
|
16
|
+
- 2.1.0
|
17
|
+
- 2.2
|
18
|
+
|
19
|
+
cache: bundler
|
20
|
+
|
21
|
+
script:
|
22
|
+
- bundle exec rake spec
|
23
|
+
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 Sport Ngin
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,271 @@
|
|
1
|
+
# Axel
|
2
|
+
[![Build Status](https://magnum.travis-ci.com/sportngin/axel.svg?token=JKgYC4kfXqjpwsFoBpzq)](https://magnum.travis-ci.com/sportngin/axel)
|
3
|
+
|
4
|
+
The building blocks for building a Sport Ngin Back-end API service:
|
5
|
+
|
6
|
+
* add some error helpers (error object)
|
7
|
+
* standardized XML/JSON envelope (metadata/error/result objects)
|
8
|
+
* adding `suppress_response_codes` to params will force a 200 and will only show errors
|
9
|
+
in the error object of the response body
|
10
|
+
* standard controller responders to ensure JSON is always prefered, XML is acceptable, and
|
11
|
+
the response always has a correct body that handles all errors
|
12
|
+
|
13
|
+
## Installation
|
14
|
+
|
15
|
+
Add this line to your application's Gemfile:
|
16
|
+
|
17
|
+
```ruby
|
18
|
+
source 'https://D9W5miiTyaf8EzrkWdTy@gem.fury.io/me/' # BELOW THE `source rubygems`
|
19
|
+
gem 'axel'
|
20
|
+
```
|
21
|
+
|
22
|
+
Then execute:
|
23
|
+
|
24
|
+
```bash
|
25
|
+
$ bundle
|
26
|
+
```
|
27
|
+
|
28
|
+
In your `app/controllers/application_controller.rb` (or wherever your base class is that will
|
29
|
+
use these helpers) change to:
|
30
|
+
|
31
|
+
```ruby
|
32
|
+
class ApplicationController < ActionController::Base
|
33
|
+
include Axel::ControllerHelpers
|
34
|
+
end
|
35
|
+
```
|
36
|
+
|
37
|
+
## Usage
|
38
|
+
|
39
|
+
### Controller Specific Helpers
|
40
|
+
|
41
|
+
#### Errors
|
42
|
+
|
43
|
+
Errors object is available (as `errors`) from any controller:
|
44
|
+
|
45
|
+
```ruby
|
46
|
+
errors.header_status # suppress_response_codes param will set to 200 by default
|
47
|
+
errors << "Error message!" # add error messages
|
48
|
+
errors.display # { status: errors.status_code, messages: errors.messages }
|
49
|
+
errors.display? # Display errors? (was there an error added or status changed?)
|
50
|
+
errors.messages
|
51
|
+
errors.messages = []
|
52
|
+
errors.status = :not_found
|
53
|
+
errors.status_code # => 404
|
54
|
+
errors.status = 403
|
55
|
+
errors.status_code # => 403
|
56
|
+
errors.new_error(status, *m) # set status, add an error or list of errors (not in an array object)
|
57
|
+
|
58
|
+
# Can also set on the fly:
|
59
|
+
rescue_error status: :not_found, message: "Error!"
|
60
|
+
rescue_error status: :unproccessable_entity, messages: ["Error!", "Error2!"]
|
61
|
+
```
|
62
|
+
|
63
|
+
#### Metadata
|
64
|
+
|
65
|
+
Metadata object that will be placed on every outgoing response body. You can add to the object like
|
66
|
+
so:
|
67
|
+
|
68
|
+
```ruby
|
69
|
+
metadata[:current_user] = current_user
|
70
|
+
|
71
|
+
# the body will then set this on the outgoing body:
|
72
|
+
# => { "metadata": { "current_user": "..." }, "result": "...", "...": "..." }
|
73
|
+
```
|
74
|
+
|
75
|
+
#### Responders
|
76
|
+
At the top of each controller (for APIs) should be:
|
77
|
+
|
78
|
+
```ruby
|
79
|
+
respond_to_json_xml
|
80
|
+
```
|
81
|
+
|
82
|
+
This defines `respond_to :json, :xml`.
|
83
|
+
|
84
|
+
making these responders good to use for defaults:
|
85
|
+
|
86
|
+
```ruby
|
87
|
+
respond_with_action :show # Good for the end of a create to show the created object
|
88
|
+
render_action :show
|
89
|
+
respond_to_empty # Render empty `result`, fill in `metadata`, `error` if necessary
|
90
|
+
render_empty
|
91
|
+
```
|
92
|
+
|
93
|
+
#### Resource finder
|
94
|
+
|
95
|
+
`find_resource` will automatically find a resource:
|
96
|
+
|
97
|
+
```ruby
|
98
|
+
class PostsController < ApplicationController
|
99
|
+
before_filter :find_resource
|
100
|
+
def show
|
101
|
+
respond_with
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
curl http://localhost:3000/posts/1 # finds Post 1 and renders
|
106
|
+
curl http://localhost:3000/posts/1
|
107
|
+
# => {"metadata":{},"error":{"status":404,"messages":["Record not found"]},"result":null}
|
108
|
+
|
109
|
+
# can also customize the find_resource with `finder` and `value`
|
110
|
+
# were `finder` is column and `value` is the value of the column
|
111
|
+
```
|
112
|
+
|
113
|
+
#### Param helpers
|
114
|
+
|
115
|
+
```ruby
|
116
|
+
query_params # Only params on the query string
|
117
|
+
post_params # POST params
|
118
|
+
object_params # Either params under the object name (ie. {"user":".."} or all POST params
|
119
|
+
object_name # singularized controller name for finding object_params
|
120
|
+
```
|
121
|
+
|
122
|
+
#### General workflow helpers
|
123
|
+
|
124
|
+
Helpers:
|
125
|
+
|
126
|
+
```ruby
|
127
|
+
force_ssl! # raises Errors::ForceSSL
|
128
|
+
drop_meta! # we don't want the requester to get data like current_user, etc.
|
129
|
+
drop_meta? # Did we call `drop_meta!`?
|
130
|
+
format # The format passed from the request OR JSON
|
131
|
+
render_nil_format # This is for rendering nils in json or XML (XML is blank, JSON is "null")
|
132
|
+
safe_json_load # If you've already manually rendered some to json this helper safely loads it to a hash for re-JSONing
|
133
|
+
```
|
134
|
+
|
135
|
+
Errors are rescued to make for easier API workflow and responding:
|
136
|
+
|
137
|
+
```ruby
|
138
|
+
Axel::Errors::ForceSSL # Drop meta
|
139
|
+
# Status: Forbidden
|
140
|
+
# Message: SSL is required
|
141
|
+
|
142
|
+
ActiveRecord::RecordNotFound # Status: 404
|
143
|
+
# Message: Record not found
|
144
|
+
|
145
|
+
Axel::Errors::NotAuthorized # Status: 401
|
146
|
+
# Message: User not authorized
|
147
|
+
|
148
|
+
ActiveModel::MassAssignmentSecurity::Error # Status: 422
|
149
|
+
# Message: Unacceptable parameter being used
|
150
|
+
```
|
151
|
+
|
152
|
+
### Interservice Helpers
|
153
|
+
|
154
|
+
#### Some setup examples
|
155
|
+
|
156
|
+
```ruby
|
157
|
+
Axel.config do |config|
|
158
|
+
config.add_resource :user_service,
|
159
|
+
:group,
|
160
|
+
service: { url: "https://user-service.your-platform.com" }
|
161
|
+
|
162
|
+
# Custom Path (otherwise defaults to plural of the resource_name (:user => "/users"))
|
163
|
+
# config.add_resource :user_service,
|
164
|
+
# :user,
|
165
|
+
# service: { url: "https://user-service.your-platform.com" },
|
166
|
+
# attributes: [:user_name, :first_name],
|
167
|
+
# path: "owner"
|
168
|
+
end
|
169
|
+
|
170
|
+
class Group < Axel::ServiceResource::Base
|
171
|
+
# Let's say your class doesn't match the configured resource, you can:
|
172
|
+
resource_name :group
|
173
|
+
|
174
|
+
# Setup fields (gets accessors, all available in #attributes)
|
175
|
+
field :name
|
176
|
+
field :owner_id
|
177
|
+
field :owner_type
|
178
|
+
field :uri
|
179
|
+
|
180
|
+
route "/groups/mine", :mine
|
181
|
+
route "/user/:user_id/groups", :by_user_id
|
182
|
+
|
183
|
+
# Attached to every Group request, you can define instance defaults as well
|
184
|
+
def self.default_request_options
|
185
|
+
{ headers: { "Accepts" => "stuff!", Authorization: "Bearer #{some_access_token}" } }
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
group = Group.new name: "test", owner_id: 1, owner_type: "Organization", uri: "blargh"
|
190
|
+
group.save
|
191
|
+
|
192
|
+
my_groups = Group.mine
|
193
|
+
user_groups = Group.by_user_id(1)
|
194
|
+
user_groups = Group.by_user_id(user_id: 1)
|
195
|
+
|
196
|
+
a_group = Group.find(1)
|
197
|
+
```
|
198
|
+
|
199
|
+
#### Related data!
|
200
|
+
|
201
|
+
```ruby
|
202
|
+
class User < Axel::ServiceResource::Base
|
203
|
+
has_many :personas
|
204
|
+
has_one :email_address, class: Email, included: true
|
205
|
+
end
|
206
|
+
|
207
|
+
class Email < Axel::ServiceResource::Base
|
208
|
+
belongs_to :user, find_nested: true
|
209
|
+
end
|
210
|
+
|
211
|
+
class Persona < Axel::ServiceResource::Base
|
212
|
+
belongs_to :user
|
213
|
+
end
|
214
|
+
|
215
|
+
u = User.find(1)
|
216
|
+
u.personas # => API call to /users/1/personas puts data in an array of Persona objects
|
217
|
+
u.email_address # => Uses `email_address` in the User data to put into Email objects
|
218
|
+
Persona.find(1).user # => API call to /users/#{persona.user_id} fills User object
|
219
|
+
Email.find(1).user # => API call to /personas/1/user fills User Object
|
220
|
+
```
|
221
|
+
|
222
|
+
#### Some Chainable Query methods
|
223
|
+
```ruby
|
224
|
+
Group.where(name: "test")
|
225
|
+
# => https://user-service.your-platform.com/groups?name=test
|
226
|
+
|
227
|
+
Group.all params: { name: "test" }, body: jsonified_stuffs, headers: {}, method: :post
|
228
|
+
|
229
|
+
Group.uri("https://user-service.dev/groups").where(name: "Jon").all(headers: {})
|
230
|
+
# => http://user-service.dev/groups?name=Jon
|
231
|
+
|
232
|
+
Group.uri("https://user-service.dev").at_path("/other_groups_path").where name: "test"
|
233
|
+
# => http://user-service.dev/other_groups_path?name=test
|
234
|
+
|
235
|
+
Group.where(name: "Jon").all(headers: {}).none
|
236
|
+
# => [] # Will always be empty array
|
237
|
+
|
238
|
+
Group.none.where(name: "Jon")
|
239
|
+
# => [] # can chain
|
240
|
+
|
241
|
+
Group.without_default_path.at_path("groupies")
|
242
|
+
# => https://user-service.your-platform.com/groupies
|
243
|
+
|
244
|
+
# Enumerable works!
|
245
|
+
Groups.where(name: "test").each { |g| puts e.attributes.inspect }
|
246
|
+
groups = Groups.where(name: "test").to_a
|
247
|
+
|
248
|
+
# drop cached JSON, then you can requery
|
249
|
+
groups.reload.where(owner_id: 1)
|
250
|
+
```
|
251
|
+
|
252
|
+
#### More General Usage
|
253
|
+
``` ruby
|
254
|
+
new_user.metadata # => { .. } # Metadata section of JSON output
|
255
|
+
new_user.metadata[:current_user] # => { "id"=>1, "user_name"=>"admin", "first_name"=>"Happy", "last_name"=>"Gilmore", "uri"=>"http://user-service.dev/users/1"}
|
256
|
+
new_user.errors.status_code # => 200
|
257
|
+
new_user.errors.messages # => []
|
258
|
+
new_user.result # => { ... } # Main envelope JSON body
|
259
|
+
```
|
260
|
+
For more info on what the requester is and can do check out
|
261
|
+
[Axel::ServiceResource::Base][srb]
|
262
|
+
|
263
|
+
## Contributing
|
264
|
+
|
265
|
+
1. Fork it
|
266
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
267
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
268
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
269
|
+
5. Create new Pull Request
|
270
|
+
|
271
|
+
[srb]: http://github.com/sportngin/axel/tree/master/app/models/axel/service_resource/base.rb
|
data/Rakefile
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
#!/usr/bin/env rake
|
2
|
+
require "rspec/core/rake_task"
|
3
|
+
require "bundler/gem_tasks"
|
4
|
+
|
5
|
+
RSpec::Core::RakeTask.new(:spec)
|
6
|
+
|
7
|
+
namespace :spec do
|
8
|
+
RSpec::Core::RakeTask.new(:docs) do |t|
|
9
|
+
t.rspec_opts = ["--format doc"]
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
task :default => :spec
|
@@ -0,0 +1,86 @@
|
|
1
|
+
module Axel
|
2
|
+
class ApiProxy < ServiceResource::Base
|
3
|
+
attr_reader :endpoint
|
4
|
+
attr_reader :services
|
5
|
+
attr_reader :request_options
|
6
|
+
|
7
|
+
def self.cache_file
|
8
|
+
cache_dir.join("api_registry_cache.json")
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.cache_dir
|
12
|
+
Rails.root.join("cache")
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.build(_, response)
|
16
|
+
if response.success?
|
17
|
+
MultiJson.load(response.body)
|
18
|
+
else
|
19
|
+
{}
|
20
|
+
end
|
21
|
+
rescue MultiJson::DecodeError
|
22
|
+
{}
|
23
|
+
end
|
24
|
+
|
25
|
+
def initialize(endpoint, request_options = {})
|
26
|
+
@endpoint = endpoint
|
27
|
+
@request_options = {}
|
28
|
+
end
|
29
|
+
|
30
|
+
def register!
|
31
|
+
resource_settings.each do |resource_setting|
|
32
|
+
Axel.config do |config|
|
33
|
+
config.add_resource extract_service_name(resource_setting["service"]),
|
34
|
+
extract_resource_name(resource_setting["path"]),
|
35
|
+
service: { url: resource_setting["service"] }
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def make_cache
|
41
|
+
Dir.exists?(self.class.cache_dir) ? true : Dir.mkdir(self.class.cache_dir)
|
42
|
+
end
|
43
|
+
private :make_cache
|
44
|
+
|
45
|
+
def write_cache(to_cache)
|
46
|
+
make_cache
|
47
|
+
File.open(self.class.cache_file, 'w') { |file| file.write to_cache.to_json }
|
48
|
+
to_cache
|
49
|
+
end
|
50
|
+
private :write_cache
|
51
|
+
|
52
|
+
def open_json(string_data)
|
53
|
+
begin
|
54
|
+
MultiJson.load string_data
|
55
|
+
rescue MultiJson::DecodeError
|
56
|
+
[]
|
57
|
+
end
|
58
|
+
end
|
59
|
+
private :open_json
|
60
|
+
|
61
|
+
def read_cache
|
62
|
+
File.exists?(self.class.cache_file) ? open_json(File.open(self.class.cache_file).read) : []
|
63
|
+
end
|
64
|
+
private :read_cache
|
65
|
+
|
66
|
+
def resource_settings
|
67
|
+
fresh = self.class.request(endpoint, "/registry.json", request_options)
|
68
|
+
if fresh.has_key? "routes"
|
69
|
+
write_cache fresh["routes"]
|
70
|
+
else
|
71
|
+
read_cache
|
72
|
+
end
|
73
|
+
end
|
74
|
+
private :resource_settings
|
75
|
+
|
76
|
+
def extract_service_name(service_url)
|
77
|
+
service_url.gsub(/http[s]*\:\/\//,"").gsub(/\..*$/, "").underscore
|
78
|
+
end
|
79
|
+
private :extract_service_name
|
80
|
+
|
81
|
+
def extract_resource_name(resource_path)
|
82
|
+
resource_path.gsub(/^\//, "")
|
83
|
+
end
|
84
|
+
private :extract_resource_name
|
85
|
+
end
|
86
|
+
end
|