signaling 1.0.0
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/.gitignore +15 -0
- data/.rspec +2 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +160 -0
- data/Rakefile +1 -0
- data/lib/signaling/api.rb +26 -0
- data/lib/signaling/base/errors.rb +19 -0
- data/lib/signaling/base/finders.rb +24 -0
- data/lib/signaling/base/http.rb +97 -0
- data/lib/signaling/base/persistence.rb +59 -0
- data/lib/signaling/base/use_api.rb +17 -0
- data/lib/signaling/base.rb +32 -0
- data/lib/signaling/error.rb +8 -0
- data/lib/signaling/faraday_middleware/raise_error.rb +18 -0
- data/lib/signaling/resource_invalid.rb +10 -0
- data/lib/signaling/version.rb +3 -0
- data/lib/signaling.rb +10 -0
- data/signaling.gemspec +34 -0
- data/spec/integration/readme_features/api_setup_spec.rb +27 -0
- data/spec/integration/readme_features/attributes_spec.rb +35 -0
- data/spec/integration/readme_features/controller_usage_spec.rb +96 -0
- data/spec/integration/readme_features/errors_spec.rb +30 -0
- data/spec/integration/readme_features/nested_models_spec.rb +53 -0
- data/spec/spec_helper.rb +24 -0
- data/spec/support/example_com/item.rb +5 -0
- data/spec/support/example_com/item_list.rb +10 -0
- data/spec/support/example_com.rb +9 -0
- data/spec/support/integration/application_controller.rb +22 -0
- data/spec/support/integration/item_lists_controller.rb +55 -0
- data/spec/support/integration/spec_helpers.rb +59 -0
- metadata +255 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 8784fb8e4727801f77797c4e4ebabf9fe826f079
|
4
|
+
data.tar.gz: dc9aa50680a5bc469f12c00bc03a86faddfef1b6
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 5eedfa6faf94e6514bdf7f2111d8c232f0b7ca75c4afc58fb70246bd77191fb6ce025d3e585ba73ec9f350a42da132ad753b72631b7fc9c3ddf6520a7c59131f
|
7
|
+
data.tar.gz: 00adad312a2ad7716ee5fe8da03c3ef17c6f355e1898235f98121019d6524e45503af5e6e0724f267ca1ec6433cd49283dd6050815daa1802a670d9656a2ab8c
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Neer Friedman
|
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,160 @@
|
|
1
|
+
# Signaling
|
2
|
+
|
3
|
+
Signaling is a Ruby library that provides a
|
4
|
+
[simple](http://www.infoq.com/presentations/Simple-Made-Easy)
|
5
|
+
API to work with remote objects exposed through HTTP services.
|
6
|
+
|
7
|
+
Main goals for this project are:
|
8
|
+
|
9
|
+
1. expose a simple API that will feel native in rails controllers (read
|
10
|
+
ActiveRecord like)
|
11
|
+
1. have as least lines of code as possible
|
12
|
+
1. To **NEVER** implement auto-loading associations
|
13
|
+
|
14
|
+
## Installation
|
15
|
+
|
16
|
+
Add this line to your application's Gemfile:
|
17
|
+
|
18
|
+
gem 'signaling'
|
19
|
+
|
20
|
+
And then execute:
|
21
|
+
|
22
|
+
$ bundle
|
23
|
+
|
24
|
+
Or install it yourself as:
|
25
|
+
|
26
|
+
$ gem install signaling
|
27
|
+
|
28
|
+
## Usage
|
29
|
+
|
30
|
+
1. Define a namespace and remote API:
|
31
|
+
```ruby
|
32
|
+
# app/models/example_com.rb
|
33
|
+
module ExampleCom
|
34
|
+
Api = Signaling::Api.new(url: "http://example.com/api")
|
35
|
+
|
36
|
+
def self.use_relative_model_naming?
|
37
|
+
true
|
38
|
+
end
|
39
|
+
end
|
40
|
+
```
|
41
|
+
|
42
|
+
The `use_relative_model_naming?` tells `ActiveModel::Naming` to build the
|
43
|
+
names of classes without the namespace.
|
44
|
+
[read more](https://coderwall.com/p/heed_q)
|
45
|
+
|
46
|
+
1. Define a model:
|
47
|
+
```ruby
|
48
|
+
# app/models/example_com/item_list.rb
|
49
|
+
class ExampleCom::ItemList < Signaling::Base
|
50
|
+
use_api ExampleCom::Api
|
51
|
+
|
52
|
+
attribute :name, String
|
53
|
+
end
|
54
|
+
```
|
55
|
+
|
56
|
+
## Features
|
57
|
+
|
58
|
+
### Attributes
|
59
|
+
|
60
|
+
Attribute definition is implemented by [virtus](https://github.com/solnic/virtus).
|
61
|
+
|
62
|
+
To change the key used to send the attribute to the remote server use the
|
63
|
+
`:param_name` option.
|
64
|
+
|
65
|
+
attribute :name, String, param_name: :title
|
66
|
+
|
67
|
+
This will send the attribute to the server as `:title`. this will **not**
|
68
|
+
parse it as `:title` from the remote response.
|
69
|
+
|
70
|
+
|
71
|
+
### Nested models
|
72
|
+
|
73
|
+
Virtus provides an API for typed arrays, these can contain other Signaling models.
|
74
|
+
|
75
|
+
```ruby
|
76
|
+
# app/models/example_com/item.rb
|
77
|
+
module ExampleCom
|
78
|
+
class Item < Signaling::Base
|
79
|
+
attribtue :name, String
|
80
|
+
end
|
81
|
+
end
|
82
|
+
```
|
83
|
+
|
84
|
+
```
|
85
|
+
# app/models/example_com/item_list.rb
|
86
|
+
module ExampleCom
|
87
|
+
class ItemList < Signaling::Base
|
88
|
+
use_api ExampleCom::Api
|
89
|
+
|
90
|
+
attribute :name
|
91
|
+
attribute :items, Array[ExampleCom::Item]
|
92
|
+
end
|
93
|
+
end
|
94
|
+
```
|
95
|
+
|
96
|
+
**Tip!**™ Use this in combination with `:param_name` when using rails's
|
97
|
+
`accepts_nested_attributes_for`.
|
98
|
+
|
99
|
+
module ExampleCom
|
100
|
+
class ItemList < Signaling::Base
|
101
|
+
use_api ExampleCom::Api
|
102
|
+
|
103
|
+
attribute :items, Array[ExampleCom::Item], param_name: :items_attributes
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
|
108
|
+
### Api Setup
|
109
|
+
|
110
|
+
`Signaling::Api.new` accepts the following options:
|
111
|
+
|
112
|
+
* `:url` - The base URL for the remote API
|
113
|
+
* `:logger` - A `Logger`-compatible object to use for logging
|
114
|
+
|
115
|
+
It also accepts a block that yields with the faraday connection to allow adding
|
116
|
+
custom middleware.
|
117
|
+
|
118
|
+
Api = Signaling::Api.new(url: api_base_url) do |faraday|
|
119
|
+
faraday.use SomeCustomFaradayMiddleware
|
120
|
+
end
|
121
|
+
|
122
|
+
|
123
|
+
### Errors
|
124
|
+
|
125
|
+
Signaling handles errors returned from the remote service in the `errors` json
|
126
|
+
field. It uses `ActiveModel::Errors` for this so all the rails tricks should
|
127
|
+
work here.
|
128
|
+
|
129
|
+
> list = ExampleCom::ItemList.new(name: 'My list')
|
130
|
+
> list.save
|
131
|
+
|
132
|
+
If the remote service will respond with HTTP status code 422 (Unprocessable Entity)
|
133
|
+
and the following json body:
|
134
|
+
|
135
|
+
{
|
136
|
+
"name": "My list",
|
137
|
+
"errors": {
|
138
|
+
"items": ["can not be empty"]
|
139
|
+
}
|
140
|
+
}
|
141
|
+
|
142
|
+
Then signaling will load these errors to the `ItemList` models
|
143
|
+
|
144
|
+
> list.errors.full_messages
|
145
|
+
=> ["Items can not be empty"]
|
146
|
+
|
147
|
+
### Usage in controllers
|
148
|
+
|
149
|
+
[Here's a typical rails controller for a Signaling model](spec/support/integration/item_lists_controller.rb)
|
150
|
+
|
151
|
+
This file serves as a reference implementation for a controller and as a guide
|
152
|
+
to make sure Signaling follows it's main goal.
|
153
|
+
|
154
|
+
## Contributing
|
155
|
+
|
156
|
+
1. Fork it
|
157
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
158
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
159
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
160
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'faraday_middleware'
|
2
|
+
require_relative 'faraday_middleware/raise_error'
|
3
|
+
|
4
|
+
class Signaling::Api
|
5
|
+
attr_reader :connection
|
6
|
+
|
7
|
+
def initialize(options, &block)
|
8
|
+
@connection = Faraday.new(url: options[:url]) do |conn|
|
9
|
+
block.call(conn) if block_given?
|
10
|
+
|
11
|
+
conn.request :multipart
|
12
|
+
conn.request :url_encoded
|
13
|
+
|
14
|
+
conn.use Signaling::FaradayMiddleware::RaiseError
|
15
|
+
|
16
|
+
if options[:logger]
|
17
|
+
conn.response :logger, options[:logger]
|
18
|
+
end
|
19
|
+
|
20
|
+
conn.response :mashify, mash_class: (options[:mash_class])
|
21
|
+
conn.response :json, content_type: /\bjson$/
|
22
|
+
|
23
|
+
conn.adapter options[:adapter] || Faraday.default_adapter
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Signaling::Base::Errors
|
2
|
+
extend ActiveSupport::Concern
|
3
|
+
|
4
|
+
included do
|
5
|
+
attr_reader :errors
|
6
|
+
end
|
7
|
+
|
8
|
+
def initialize(*args)
|
9
|
+
self.errors = {}
|
10
|
+
super
|
11
|
+
end
|
12
|
+
|
13
|
+
def errors=(error_hash)
|
14
|
+
@errors = ActiveModel::Errors.new(self)
|
15
|
+
error_hash.each do |attr, errors|
|
16
|
+
errors.each {|error| self.errors.add(attr, error) }
|
17
|
+
end if error_hash
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Signaling::Base::Finders
|
2
|
+
extend ActiveSupport::Concern
|
3
|
+
|
4
|
+
module ClassMethods
|
5
|
+
def find(id)
|
6
|
+
from_response(request(:show, id: id))
|
7
|
+
end
|
8
|
+
|
9
|
+
def all(params = {})
|
10
|
+
from_response(request(:index, params))
|
11
|
+
end
|
12
|
+
|
13
|
+
def from_response(response)
|
14
|
+
case response
|
15
|
+
when Hash, Hashie::Mash
|
16
|
+
self.new(response)
|
17
|
+
when Array
|
18
|
+
response.map {|i| from_response(i) }
|
19
|
+
else
|
20
|
+
response
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
module Signaling::Base::Http
|
2
|
+
extend ActiveSupport::Concern
|
3
|
+
|
4
|
+
class UndefinedAction < StandardError
|
5
|
+
def initialize(action)
|
6
|
+
super("Undefined action: #{action.inspect}")
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
included do
|
11
|
+
class_attribute :_defined_actions
|
12
|
+
end
|
13
|
+
|
14
|
+
module ClassMethods
|
15
|
+
def request(action, params = {}, &block)
|
16
|
+
http_method = http_method_for(action)
|
17
|
+
path = path_for(action, id: params[:id])
|
18
|
+
scoped_params = scope_params(params.except(:id))
|
19
|
+
|
20
|
+
response = connection.send(http_method, path, scoped_params)
|
21
|
+
|
22
|
+
block ? block.call(response.body) : response.body
|
23
|
+
end
|
24
|
+
|
25
|
+
def define_action(action_name, options)
|
26
|
+
self._defined_actions ||= {}
|
27
|
+
unless options[:method] && options[:path]
|
28
|
+
raise ArgumentError, ':method and :path options are required'
|
29
|
+
end
|
30
|
+
self._defined_actions[action_name.to_sym] = options
|
31
|
+
end
|
32
|
+
|
33
|
+
# convert value to params-friendly hash/array
|
34
|
+
# this also sets parameter names for Signaling models
|
35
|
+
def to_params(value, set_param_names_first = false)
|
36
|
+
case value
|
37
|
+
when Signaling::Base
|
38
|
+
value.class.set_param_names(value.to_params)
|
39
|
+
when Array
|
40
|
+
value.map {|e| to_params(e, set_param_names_first) }
|
41
|
+
when Hash
|
42
|
+
value = set_param_names(value) if set_param_names_first
|
43
|
+
Hash[value.map {|k,v| [k, to_params(v)]}]
|
44
|
+
else
|
45
|
+
value
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
protected
|
50
|
+
|
51
|
+
def path_for(action, options = nil)
|
52
|
+
path = _defined_actions.try(:[], action).try(:[], :path)
|
53
|
+
|
54
|
+
raise(UndefinedAction, action) unless path
|
55
|
+
|
56
|
+
path.gsub(":route_key", route_key)
|
57
|
+
.gsub(":id") { options[:id] || raise('missing :id parameter for route') }
|
58
|
+
end
|
59
|
+
|
60
|
+
def http_method_for(action)
|
61
|
+
method = _defined_actions.try(:[], action).try(:[], :method)
|
62
|
+
|
63
|
+
method || raise(UndefinedAction, action)
|
64
|
+
end
|
65
|
+
|
66
|
+
def scope_params(params)
|
67
|
+
key = params.is_a?(Array) ? param_key.pluralize : param_key
|
68
|
+
|
69
|
+
{ key => to_params(params, true) }
|
70
|
+
end
|
71
|
+
|
72
|
+
def route_key
|
73
|
+
ActiveModel::Naming.route_key(self).pluralize
|
74
|
+
end
|
75
|
+
|
76
|
+
def param_key
|
77
|
+
ActiveModel::Naming.param_key(self)
|
78
|
+
end
|
79
|
+
|
80
|
+
# changes attribtue names to param names (defined by ":param_name")
|
81
|
+
def set_param_names(params)
|
82
|
+
HashWithIndifferentAccess.new.tap do |result|
|
83
|
+
params.each do |k, v|
|
84
|
+
attribute = attribute_set[k.to_sym]
|
85
|
+
name = attribute.try(:options).try(:[], :param_name) || k
|
86
|
+
result[name] = v
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def to_params
|
93
|
+
clean_attributes = persisted? ? attributes : attributes.except(:id)
|
94
|
+
self.class.to_params(clean_attributes, true)
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module Signaling::Base::Persistence
|
2
|
+
extend ActiveSupport::Concern
|
3
|
+
|
4
|
+
module ClassMethods
|
5
|
+
def create(params)
|
6
|
+
from_response(request(:create, params))
|
7
|
+
rescue Signaling::Error::UnprocessableEntity => e
|
8
|
+
from_response(e.response[:body])
|
9
|
+
end
|
10
|
+
|
11
|
+
def destroy(id)
|
12
|
+
from_response(request(:destroy, id: id))
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
|
17
|
+
def new?
|
18
|
+
self.id.blank?
|
19
|
+
end
|
20
|
+
|
21
|
+
def persisted?
|
22
|
+
!new?
|
23
|
+
end
|
24
|
+
|
25
|
+
def save
|
26
|
+
new? ? create : update
|
27
|
+
true
|
28
|
+
rescue Signaling::Error::UnprocessableEntity => e
|
29
|
+
self.errors = e.response[:body][:errors]
|
30
|
+
false
|
31
|
+
end
|
32
|
+
|
33
|
+
def save!
|
34
|
+
save || raise(ResourceInvalid.new(self))
|
35
|
+
end
|
36
|
+
|
37
|
+
def update_attributes(params)
|
38
|
+
update(params)
|
39
|
+
rescue Signaling::Error::UnprocessableEntity => e
|
40
|
+
self.attributes = e.response[:body]
|
41
|
+
false
|
42
|
+
end
|
43
|
+
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def update(params = attributes)
|
48
|
+
self.class.request(:update, params.merge(id: self.id)) do |response|
|
49
|
+
self.attributes = response
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def create
|
54
|
+
self.class.request(:create, attributes.except(:id)) do |response|
|
55
|
+
self.attributes = response
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Signaling::Base::UseApi
|
2
|
+
extend ActiveSupport::Concern
|
3
|
+
|
4
|
+
module ClassMethods
|
5
|
+
def use_api(api)
|
6
|
+
@api = api
|
7
|
+
end
|
8
|
+
|
9
|
+
def api
|
10
|
+
@api or raise "API is not set for #{self.name}"
|
11
|
+
end
|
12
|
+
|
13
|
+
def connection
|
14
|
+
api.connection
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Signaling
|
2
|
+
class Base
|
3
|
+
include Virtus.model
|
4
|
+
extend ActiveModel::Naming
|
5
|
+
extend ActiveModel::Translation
|
6
|
+
|
7
|
+
autoload :Errors, 'signaling/base/errors'
|
8
|
+
autoload :Finders, 'signaling/base/finders'
|
9
|
+
autoload :Http, 'signaling/base/http'
|
10
|
+
autoload :Persistence, 'signaling/base/persistence'
|
11
|
+
autoload :UseApi, 'signaling/base/use_api'
|
12
|
+
|
13
|
+
include Base::Errors
|
14
|
+
include Base::Finders
|
15
|
+
include Base::Http
|
16
|
+
include Base::Persistence
|
17
|
+
include Base::UseApi
|
18
|
+
|
19
|
+
attribute :id, String
|
20
|
+
|
21
|
+
define_action :index, method: :get, path: ":route_key.json"
|
22
|
+
define_action :create, method: :post, path: ":route_key.json"
|
23
|
+
define_action :show, method: :get, path: ":route_key/:id.json"
|
24
|
+
define_action :update, method: :put, path: ":route_key/:id.json"
|
25
|
+
define_action :destroy, method: :delete, path: ":route_key/:id.json"
|
26
|
+
|
27
|
+
def to_param
|
28
|
+
id
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'faraday/response/raise_error'
|
2
|
+
|
3
|
+
module Signaling
|
4
|
+
module FaradayMiddleware
|
5
|
+
class RaiseError < ::Faraday::Response::RaiseError
|
6
|
+
def on_complete(env)
|
7
|
+
case env[:status]
|
8
|
+
when 422
|
9
|
+
raise Signaling::Error::UnprocessableEntity, response_values(env)
|
10
|
+
when 403
|
11
|
+
raise Signaling::Error::Forbidden, response_values(env)
|
12
|
+
else
|
13
|
+
super
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/lib/signaling.rb
ADDED
data/signaling.gemspec
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'signaling/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "signaling"
|
8
|
+
spec.version = Signaling::VERSION
|
9
|
+
spec.authors = ["Neer Friedman"]
|
10
|
+
spec.email = ["neerfri@gmail.com"]
|
11
|
+
spec.description = %q{Signaling maps REST-like APIs to ruby objects}
|
12
|
+
spec.summary = %q{Signaling maps REST-like APIs to ruby objects}
|
13
|
+
spec.homepage = ""
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_dependency "activemodel", ">= 3.1.0"
|
22
|
+
spec.add_dependency "activesupport", ">= 3.1.0"
|
23
|
+
spec.add_dependency "virtus", ">= 1.0.0"
|
24
|
+
spec.add_dependency "faraday"
|
25
|
+
spec.add_dependency "faraday_middleware"
|
26
|
+
spec.add_dependency "hashie"
|
27
|
+
|
28
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
29
|
+
spec.add_development_dependency "rake"
|
30
|
+
spec.add_development_dependency "rspec"
|
31
|
+
spec.add_development_dependency "webmock"
|
32
|
+
spec.add_development_dependency "rack"
|
33
|
+
spec.add_development_dependency "addressable"
|
34
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "README features" do
|
4
|
+
describe 'Api setup' do
|
5
|
+
let(:api_base_url) { "http://example.com/api" }
|
6
|
+
let(:logger) { double("logger", info: nil, debug: nil) }
|
7
|
+
|
8
|
+
it "sets base url" do
|
9
|
+
expect(new_api.connection.url_prefix.to_s).to eq(api_base_url)
|
10
|
+
end
|
11
|
+
|
12
|
+
it "allows setting a logger" do
|
13
|
+
stub_request(:get, api_base_url)
|
14
|
+
|
15
|
+
logger.should_receive(:info)
|
16
|
+
new_api(logger: logger).connection.get('')
|
17
|
+
end
|
18
|
+
|
19
|
+
it "yields block with faraday builder" do
|
20
|
+
expect {|b| new_api(&b) }.to yield_with_args(Faraday::Connection)
|
21
|
+
end
|
22
|
+
|
23
|
+
def new_api(options = {}, &block)
|
24
|
+
Signaling::Api.new({url: api_base_url}.merge(options), &block)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "README features" do
|
4
|
+
describe "attribtues" do
|
5
|
+
describe ":param_name option" do
|
6
|
+
let(:item_list) { ExampleCom::ItemList.new(tags: ['tag1']) }
|
7
|
+
|
8
|
+
before do
|
9
|
+
stub_request(*create_item_list_request).to_return(successful_response)
|
10
|
+
stub_request(*update_item_list_request).to_return(successful_response)
|
11
|
+
end
|
12
|
+
|
13
|
+
it "changes remote attribute name on #save" do
|
14
|
+
item_list.save
|
15
|
+
|
16
|
+
expected_body = { item_list: { name: nil, list_tags: ["tag1"]}}
|
17
|
+
expect_request(:post, path: item_lists_path, body: expected_body)
|
18
|
+
end
|
19
|
+
|
20
|
+
it "changes remote attribute name on #update_attributes" do
|
21
|
+
item_list.save
|
22
|
+
item_list.update_attributes(tags: ['tag2'])
|
23
|
+
|
24
|
+
expected_body = { item_list: { list_tags: ["tag2"]}}
|
25
|
+
expect_request(:put, path: item_list_path, body: expected_body)
|
26
|
+
end
|
27
|
+
|
28
|
+
def successful_response
|
29
|
+
{ status: 200, body: { id: '1' } }
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
@@ -0,0 +1,96 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "README Features" do
|
4
|
+
describe "Usage in rails controllers" do
|
5
|
+
let(:controller) { ItemListsController.new }
|
6
|
+
|
7
|
+
describe "#index" do
|
8
|
+
it "finds all item lists" do
|
9
|
+
stub_request(*index_item_list_request).to_return(successful_response)
|
10
|
+
|
11
|
+
get :index
|
12
|
+
expect(assigns(:item_lists).count).to eq(2)
|
13
|
+
end
|
14
|
+
|
15
|
+
def successful_response
|
16
|
+
{ status: 200, body: [{}, {}] }
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe "#new" do
|
21
|
+
it "assigns a new item list" do
|
22
|
+
get :new
|
23
|
+
expect(assigns(:item_list)).to be_kind_of(ExampleCom::ItemList)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe "#create" do
|
28
|
+
let(:item_list_param) { { name: "my list" } }
|
29
|
+
|
30
|
+
before do
|
31
|
+
stub_request(*create_item_list_request).to_return(successful_response)
|
32
|
+
end
|
33
|
+
|
34
|
+
it "creates a new item list on remote service" do
|
35
|
+
post :create, item_list: item_list_param
|
36
|
+
expect_request(:post, path: item_lists_path, body: expected_body)
|
37
|
+
end
|
38
|
+
|
39
|
+
def expected_body
|
40
|
+
{ item_list: item_list_param }
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe "#show / #edit" do
|
45
|
+
it "finds the item list" do
|
46
|
+
stub_request(*get_item_list_request).to_return(successful_response)
|
47
|
+
|
48
|
+
get :show, { id: '1' }
|
49
|
+
expect_request(:get, path: item_list_path)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
describe "#update" do
|
54
|
+
let(:item_list_param) { { name: 'my list' } }
|
55
|
+
|
56
|
+
before do
|
57
|
+
stub_request(*update_item_list_request).to_return(successful_response)
|
58
|
+
end
|
59
|
+
|
60
|
+
it "does NOT fetch the item list from remote" do
|
61
|
+
put :update, { id: '1', item_list: {} }
|
62
|
+
expect_no_request(:get, path: item_list_path)
|
63
|
+
end
|
64
|
+
|
65
|
+
it "updates record on remote service" do
|
66
|
+
put :update, { id: '1', item_list: item_list_param }
|
67
|
+
|
68
|
+
expected_body = { item_list: item_list_param }
|
69
|
+
expect_request(:put, path: item_list_path, body: expected_body)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
describe "#destroy" do
|
74
|
+
it "destroys record on remote" do
|
75
|
+
stub_request(*destroy_item_list_request).to_return(successful_response)
|
76
|
+
delete :destroy, { id: '1' }
|
77
|
+
|
78
|
+
expect_request(:delete, path: item_list_path)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def successful_response
|
83
|
+
{ status: 200, body: { id: '1' } }
|
84
|
+
end
|
85
|
+
|
86
|
+
%w(get post put delete).each do |http_method|
|
87
|
+
define_method(http_method) do |action, params = {}|
|
88
|
+
controller.process(action, params)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def assigns(name)
|
93
|
+
controller.instance_variable_get("@#{name}")
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "README Features" do
|
4
|
+
describe "Errors" do
|
5
|
+
let(:item_list) { ExampleCom::ItemList.new(name: 'My list') }
|
6
|
+
|
7
|
+
it "exposes ActiveModel::Errors object" do
|
8
|
+
expect(item_list.errors).to be_kind_of(ActiveModel::Errors)
|
9
|
+
end
|
10
|
+
|
11
|
+
it "loads errors from remote response" do
|
12
|
+
stub_request(*create_item_list_request).to_return(response_with_errors)
|
13
|
+
item_list.save
|
14
|
+
|
15
|
+
expect(item_list.errors[:items]).to eq(["can not be empty"])
|
16
|
+
end
|
17
|
+
|
18
|
+
def response_with_errors
|
19
|
+
{
|
20
|
+
status: 422,
|
21
|
+
body: {
|
22
|
+
name: "My list",
|
23
|
+
errors: {
|
24
|
+
items: ["can not be empty"]
|
25
|
+
},
|
26
|
+
}
|
27
|
+
}
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "README features" do
|
4
|
+
describe "Nested models" do
|
5
|
+
let(:item_hash) { {name: 'item 1'} }
|
6
|
+
let(:item_list) { ExampleCom::ItemList.new(items: [item_hash]) }
|
7
|
+
|
8
|
+
context "with a new record" do
|
9
|
+
before do
|
10
|
+
stub_request(*create_item_list_request).to_return(successful_response)
|
11
|
+
end
|
12
|
+
|
13
|
+
it "converts items" do
|
14
|
+
expect(item_list.items.first).to be_kind_of(ExampleCom::Item)
|
15
|
+
end
|
16
|
+
|
17
|
+
it "sends items as hashes to the remote service" do
|
18
|
+
item_list.save
|
19
|
+
expect_request(:post, path: item_lists_path, body: expected_body)
|
20
|
+
end
|
21
|
+
|
22
|
+
def expected_body
|
23
|
+
{item_list: { name: nil, items: [item_hash]}}
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
context "with a record from the remote service" do
|
28
|
+
let(:item_list) { ExampleCom::ItemList.find(1) }
|
29
|
+
|
30
|
+
before do
|
31
|
+
stub_request(*get_item_list_request).to_return(successful_response)
|
32
|
+
stub_request(*update_item_list_request).to_return(successful_response)
|
33
|
+
end
|
34
|
+
|
35
|
+
it "converts items" do
|
36
|
+
expect(item_list.items.first).to be_kind_of(ExampleCom::Item)
|
37
|
+
end
|
38
|
+
|
39
|
+
it "sends items as hashes to the remote service" do
|
40
|
+
new_items_array = [item_hash, {name: 'item 2'}]
|
41
|
+
item_list.update_attributes(items: new_items_array)
|
42
|
+
|
43
|
+
expected_body = {item_list: { items: new_items_array}}
|
44
|
+
expect_request(:put, path: item_list_path, body: expected_body)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def successful_response
|
49
|
+
{ status: 200, body: { id: '1', items: [item_hash] } }
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'signaling'
|
2
|
+
require 'pathname'
|
3
|
+
require 'webmock/rspec'
|
4
|
+
require 'pry'
|
5
|
+
require 'rack'
|
6
|
+
require 'addressable/uri'
|
7
|
+
|
8
|
+
project_root = Pathname.new(__FILE__).join('../..').expand_path
|
9
|
+
|
10
|
+
# Requires supporting ruby files with custom matchers and macros, etc,
|
11
|
+
# in spec/support/ and its subdirectories.
|
12
|
+
Dir[project_root.join("spec/support/**/*.rb").to_s].each { |f| require f }
|
13
|
+
|
14
|
+
RSpec.configure do |config|
|
15
|
+
config.treat_symbols_as_metadata_keys_with_true_values = true
|
16
|
+
config.run_all_when_everything_filtered = true
|
17
|
+
config.filter_run :focus
|
18
|
+
|
19
|
+
# Run specs in random order to surface order dependencies. If you find an
|
20
|
+
# order dependency and want to debug it, you can fix the order by providing
|
21
|
+
# the seed, which is printed after each run.
|
22
|
+
# --seed 1234
|
23
|
+
config.order = 'random'
|
24
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
require_relative '../example_com'
|
2
|
+
require_relative 'item'
|
3
|
+
|
4
|
+
class ExampleCom::ItemList < Signaling::Base
|
5
|
+
use_api ExampleCom::Api
|
6
|
+
|
7
|
+
attribute :name, String
|
8
|
+
attribute :items, Array[ExampleCom::Item]
|
9
|
+
attribute :tags, Array, param_name: :list_tags
|
10
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
class ApplicationController
|
2
|
+
attr_accessor :params
|
3
|
+
|
4
|
+
def process(action, params)
|
5
|
+
self.params = params.with_indifferent_access
|
6
|
+
send(action)
|
7
|
+
end
|
8
|
+
|
9
|
+
def redirect_to(path)
|
10
|
+
end
|
11
|
+
|
12
|
+
def render(template, options = {})
|
13
|
+
end
|
14
|
+
|
15
|
+
def item_list_path(item_list)
|
16
|
+
"/item_lists/#{item_list.id}"
|
17
|
+
end
|
18
|
+
|
19
|
+
def item_lists_path
|
20
|
+
"/item_lists"
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require_relative 'application_controller'
|
2
|
+
|
3
|
+
class ItemListsController < ApplicationController
|
4
|
+
def index
|
5
|
+
@item_lists = ExampleCom::ItemList.all
|
6
|
+
end
|
7
|
+
|
8
|
+
def new
|
9
|
+
@item_list = ExampleCom::ItemList.new
|
10
|
+
end
|
11
|
+
|
12
|
+
def create
|
13
|
+
@item_list = ExampleCom::ItemList.new(item_list_params)
|
14
|
+
|
15
|
+
if @item_list.save
|
16
|
+
redirect_to(item_list_path(@item_list))
|
17
|
+
else
|
18
|
+
render 'new'
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def show
|
23
|
+
@item_list = find_item_list
|
24
|
+
end
|
25
|
+
|
26
|
+
def edit
|
27
|
+
@item_list = find_item_list
|
28
|
+
end
|
29
|
+
|
30
|
+
def update
|
31
|
+
@item_list = ExampleCom::ItemList.new(id: params[:id])
|
32
|
+
|
33
|
+
if @item_list.update_attributes(item_list_params)
|
34
|
+
redirect_to item_list_path(@item_list)
|
35
|
+
else
|
36
|
+
render 'edit'
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
|
41
|
+
def destroy
|
42
|
+
ExampleCom::ItemList.destroy(params[:id])
|
43
|
+
|
44
|
+
redirect_to item_lists_path
|
45
|
+
end
|
46
|
+
|
47
|
+
protected
|
48
|
+
def find_item_list
|
49
|
+
ExampleCom::ItemList.find(params[:id])
|
50
|
+
end
|
51
|
+
|
52
|
+
def item_list_params
|
53
|
+
params[:item_list]
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module IntegrationSpecHelpers
|
2
|
+
extend ActiveSupport::Concern
|
3
|
+
|
4
|
+
included do
|
5
|
+
let(:item_lists_path) { example_com_build_url("item_lists.json") }
|
6
|
+
let(:item_list_path) { example_com_build_url("item_lists/1.json") }
|
7
|
+
|
8
|
+
let(:index_item_list_request) { [:get, item_lists_path] }
|
9
|
+
let(:create_item_list_request) { [:post, item_lists_path] }
|
10
|
+
let(:get_item_list_request) { [:get, item_list_path ] }
|
11
|
+
let(:update_item_list_request) { [:put, item_list_path ] }
|
12
|
+
let(:destroy_item_list_request) { [:delete, item_list_path ] }
|
13
|
+
end
|
14
|
+
|
15
|
+
class BodyPattern < Struct.new(:expected)
|
16
|
+
def ===(body)
|
17
|
+
normalize!(expected) == Rack::Utils.parse_nested_query(body)
|
18
|
+
end
|
19
|
+
|
20
|
+
def normalize!(hash)
|
21
|
+
Hash[stringify!(hash).sort]
|
22
|
+
end
|
23
|
+
|
24
|
+
def stringify!(hash)
|
25
|
+
WebMock::Util::HashKeysStringifier.stringify_keys!(hash)
|
26
|
+
end
|
27
|
+
|
28
|
+
def inspect
|
29
|
+
expected.inspect
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def expect_request(method, options)
|
34
|
+
matcher = have_requested(method, options[:path])
|
35
|
+
if options[:body]
|
36
|
+
matcher.with(body: body_matcher(options[:body]))
|
37
|
+
end
|
38
|
+
|
39
|
+
to_or_to_not = options[:not] ? :to_not : :to
|
40
|
+
expect(WebMock).send(to_or_to_not, matcher)
|
41
|
+
end
|
42
|
+
|
43
|
+
def expect_no_request(method, options)
|
44
|
+
expect_request(method, options.merge(not: true))
|
45
|
+
end
|
46
|
+
|
47
|
+
def body_matcher(expected)
|
48
|
+
BodyPattern.new(expected)
|
49
|
+
end
|
50
|
+
|
51
|
+
def example_com_build_url(path = '')
|
52
|
+
ExampleCom::Api.connection.build_url(path).to_s
|
53
|
+
end
|
54
|
+
|
55
|
+
RSpec.configure do |config|
|
56
|
+
config.include self, type: :integration,
|
57
|
+
example_group: { :file_path => %r(spec/integration) }
|
58
|
+
end
|
59
|
+
end
|
metadata
ADDED
@@ -0,0 +1,255 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: signaling
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Neer Friedman
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-01-18 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: activemodel
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 3.1.0
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 3.1.0
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: activesupport
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 3.1.0
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 3.1.0
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: virtus
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - '>='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 1.0.0
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 1.0.0
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: faraday
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - '>='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: faraday_middleware
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - '>='
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - '>='
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: hashie
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - '>='
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - '>='
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: bundler
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ~>
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '1.3'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ~>
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '1.3'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: rake
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - '>='
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - '>='
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: rspec
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - '>='
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - '>='
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: webmock
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - '>='
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '0'
|
146
|
+
type: :development
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - '>='
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '0'
|
153
|
+
- !ruby/object:Gem::Dependency
|
154
|
+
name: rack
|
155
|
+
requirement: !ruby/object:Gem::Requirement
|
156
|
+
requirements:
|
157
|
+
- - '>='
|
158
|
+
- !ruby/object:Gem::Version
|
159
|
+
version: '0'
|
160
|
+
type: :development
|
161
|
+
prerelease: false
|
162
|
+
version_requirements: !ruby/object:Gem::Requirement
|
163
|
+
requirements:
|
164
|
+
- - '>='
|
165
|
+
- !ruby/object:Gem::Version
|
166
|
+
version: '0'
|
167
|
+
- !ruby/object:Gem::Dependency
|
168
|
+
name: addressable
|
169
|
+
requirement: !ruby/object:Gem::Requirement
|
170
|
+
requirements:
|
171
|
+
- - '>='
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: '0'
|
174
|
+
type: :development
|
175
|
+
prerelease: false
|
176
|
+
version_requirements: !ruby/object:Gem::Requirement
|
177
|
+
requirements:
|
178
|
+
- - '>='
|
179
|
+
- !ruby/object:Gem::Version
|
180
|
+
version: '0'
|
181
|
+
description: Signaling maps REST-like APIs to ruby objects
|
182
|
+
email:
|
183
|
+
- neerfri@gmail.com
|
184
|
+
executables: []
|
185
|
+
extensions: []
|
186
|
+
extra_rdoc_files: []
|
187
|
+
files:
|
188
|
+
- .gitignore
|
189
|
+
- .rspec
|
190
|
+
- Gemfile
|
191
|
+
- LICENSE.txt
|
192
|
+
- README.md
|
193
|
+
- Rakefile
|
194
|
+
- lib/signaling.rb
|
195
|
+
- lib/signaling/api.rb
|
196
|
+
- lib/signaling/base.rb
|
197
|
+
- lib/signaling/base/errors.rb
|
198
|
+
- lib/signaling/base/finders.rb
|
199
|
+
- lib/signaling/base/http.rb
|
200
|
+
- lib/signaling/base/persistence.rb
|
201
|
+
- lib/signaling/base/use_api.rb
|
202
|
+
- lib/signaling/error.rb
|
203
|
+
- lib/signaling/faraday_middleware/raise_error.rb
|
204
|
+
- lib/signaling/resource_invalid.rb
|
205
|
+
- lib/signaling/version.rb
|
206
|
+
- signaling.gemspec
|
207
|
+
- spec/integration/readme_features/api_setup_spec.rb
|
208
|
+
- spec/integration/readme_features/attributes_spec.rb
|
209
|
+
- spec/integration/readme_features/controller_usage_spec.rb
|
210
|
+
- spec/integration/readme_features/errors_spec.rb
|
211
|
+
- spec/integration/readme_features/nested_models_spec.rb
|
212
|
+
- spec/spec_helper.rb
|
213
|
+
- spec/support/example_com.rb
|
214
|
+
- spec/support/example_com/item.rb
|
215
|
+
- spec/support/example_com/item_list.rb
|
216
|
+
- spec/support/integration/application_controller.rb
|
217
|
+
- spec/support/integration/item_lists_controller.rb
|
218
|
+
- spec/support/integration/spec_helpers.rb
|
219
|
+
homepage: ''
|
220
|
+
licenses:
|
221
|
+
- MIT
|
222
|
+
metadata: {}
|
223
|
+
post_install_message:
|
224
|
+
rdoc_options: []
|
225
|
+
require_paths:
|
226
|
+
- lib
|
227
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
228
|
+
requirements:
|
229
|
+
- - '>='
|
230
|
+
- !ruby/object:Gem::Version
|
231
|
+
version: '0'
|
232
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
233
|
+
requirements:
|
234
|
+
- - '>='
|
235
|
+
- !ruby/object:Gem::Version
|
236
|
+
version: '0'
|
237
|
+
requirements: []
|
238
|
+
rubyforge_project:
|
239
|
+
rubygems_version: 2.0.2
|
240
|
+
signing_key:
|
241
|
+
specification_version: 4
|
242
|
+
summary: Signaling maps REST-like APIs to ruby objects
|
243
|
+
test_files:
|
244
|
+
- spec/integration/readme_features/api_setup_spec.rb
|
245
|
+
- spec/integration/readme_features/attributes_spec.rb
|
246
|
+
- spec/integration/readme_features/controller_usage_spec.rb
|
247
|
+
- spec/integration/readme_features/errors_spec.rb
|
248
|
+
- spec/integration/readme_features/nested_models_spec.rb
|
249
|
+
- spec/spec_helper.rb
|
250
|
+
- spec/support/example_com.rb
|
251
|
+
- spec/support/example_com/item.rb
|
252
|
+
- spec/support/example_com/item_list.rb
|
253
|
+
- spec/support/integration/application_controller.rb
|
254
|
+
- spec/support/integration/item_lists_controller.rb
|
255
|
+
- spec/support/integration/spec_helpers.rb
|