placid 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +7 -0
- data/.yardopts +1 -0
- data/Gemfile +2 -0
- data/MIT-LICENSE +23 -0
- data/README.md +87 -0
- data/Rakefile +12 -0
- data/lib/placid/config.rb +14 -0
- data/lib/placid/exceptions.rb +4 -0
- data/lib/placid/helper.rb +182 -0
- data/lib/placid/model.rb +159 -0
- data/lib/placid.rb +6 -0
- data/placid.gemspec +25 -0
- data/spec/placid_config_spec.rb +20 -0
- data/spec/placid_helper_spec.rb +109 -0
- data/spec/placid_model_spec.rb +330 -0
- data/spec/spec_helper.rb +12 -0
- metadata +192 -0
data/.gitignore
ADDED
data/.yardopts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--markup markdown
|
data/Gemfile
ADDED
data/MIT-LICENSE
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
The MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2012 Society for Human Resource Management
|
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.
|
23
|
+
|
data/README.md
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
placid
|
2
|
+
======
|
3
|
+
|
4
|
+
Placid is an ActiveRecord-ish model using a REST API for storage. The REST API
|
5
|
+
can be any backend you choose or create yourself, provided it follows some basic
|
6
|
+
conventions.
|
7
|
+
|
8
|
+
Installation
|
9
|
+
------------
|
10
|
+
|
11
|
+
$ gem install placid
|
12
|
+
|
13
|
+
Usage
|
14
|
+
-----
|
15
|
+
|
16
|
+
Define a subclass with the name of your REST model:
|
17
|
+
|
18
|
+
class Person < Placid::Model
|
19
|
+
end
|
20
|
+
|
21
|
+
and you'll get these class methods, and their REST equivalents:
|
22
|
+
|
23
|
+
Person.list # GET /people
|
24
|
+
Person.create(attrs) # POST /person (attrs)
|
25
|
+
Person.find(id) # GET /person/:id
|
26
|
+
Person.destroy(id) # DELETE /person/:id
|
27
|
+
Person.update(id, attrs) # PUT /person/:id (attrs)
|
28
|
+
|
29
|
+
By default, placid assumes that your REST API is running on `localhost`. To
|
30
|
+
change this, set:
|
31
|
+
|
32
|
+
Placid::Config.rest_url = 'http://my.rest.host:8080'
|
33
|
+
|
34
|
+
Each model has a field that is used for uniquely identifying instances. This
|
35
|
+
would be called the "primary key" in a relational database. If you don't
|
36
|
+
specify the name of the field, `id` is assumed. If your model uses a
|
37
|
+
different field name, you can specify it like this:
|
38
|
+
|
39
|
+
class Person < Placid::Model
|
40
|
+
unique_id :email
|
41
|
+
end
|
42
|
+
|
43
|
+
The `Placid::Model` base class includes helper methods for basic HTTP requests.
|
44
|
+
You can use these from any model instance, or call them from custom methods you
|
45
|
+
define on your model. For example:
|
46
|
+
|
47
|
+
class Person < Placid::Model
|
48
|
+
unique_id :email
|
49
|
+
|
50
|
+
def add_phone(phone_number)
|
51
|
+
put(model, id, 'add_phone', phone_number)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
jenny = Person.new(:email => 'jenny@example.com')
|
56
|
+
|
57
|
+
jenny.add_phone('867-5309')
|
58
|
+
# Same as:
|
59
|
+
jenny.put('person', 'jenny@example.com', 'add_phone', '867-5309')
|
60
|
+
|
61
|
+
|
62
|
+
License
|
63
|
+
-------
|
64
|
+
|
65
|
+
The MIT License
|
66
|
+
|
67
|
+
Copyright (c) 2012 Society for Human Resource Management
|
68
|
+
|
69
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
70
|
+
a copy of this software and associated documentation files (the
|
71
|
+
"Software"), to deal in the Software without restriction, including
|
72
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
73
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
74
|
+
permit persons to whom the Software is furnished to do so, subject to
|
75
|
+
the following conditions:
|
76
|
+
|
77
|
+
The above copyright notice and this permission notice shall be
|
78
|
+
included in all copies or substantial portions of the Software.
|
79
|
+
|
80
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
81
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
82
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
83
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
84
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
85
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
86
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
87
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,182 @@
|
|
1
|
+
require 'uri'
|
2
|
+
require 'json'
|
3
|
+
require 'hashie'
|
4
|
+
require 'rest-client'
|
5
|
+
require 'placid/exceptions'
|
6
|
+
|
7
|
+
module Placid
|
8
|
+
module Helper
|
9
|
+
# Escape any special URI characters in `text` and return the escaped string.
|
10
|
+
# `nil` is treated as an empty string.
|
11
|
+
#
|
12
|
+
# @return [String]
|
13
|
+
#
|
14
|
+
def escape(text)
|
15
|
+
URI.escape(text.to_s, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]"))
|
16
|
+
end
|
17
|
+
|
18
|
+
# If the last arg in `args` is a hash, pop it off and return it. Otherwise,
|
19
|
+
# return an empty hash. `args` is modified in-place. Behaves like
|
20
|
+
# ActiveSupport's `String#extract_options!` method.
|
21
|
+
#
|
22
|
+
# @param [Array] args
|
23
|
+
# Zero or more arguments, the last of which might be a Hash.
|
24
|
+
#
|
25
|
+
# @return [Hash]
|
26
|
+
#
|
27
|
+
def extract_options(args)
|
28
|
+
args.last.is_a?(::Hash) ? args.pop : {}
|
29
|
+
end
|
30
|
+
|
31
|
+
# Return a full URL for a REST API request to the given path, relative to
|
32
|
+
# the configured `Placid::Config.rest_url`. Each path component is
|
33
|
+
# URI-escaped.
|
34
|
+
#
|
35
|
+
# @example
|
36
|
+
# url('people', 'eric') #=> 'http://localhost/people/eric'
|
37
|
+
# url('a b', 'c:d') #=> 'http://localhost/a%20b/c%3Ad'
|
38
|
+
#
|
39
|
+
# @param [Array] path
|
40
|
+
# Parts of the path to request. These will be escaped and joined with '/'.
|
41
|
+
#
|
42
|
+
# @return [String]
|
43
|
+
#
|
44
|
+
def url(*path)
|
45
|
+
url = Placid::Config.rest_url.to_s.gsub(/\/$/, '')
|
46
|
+
joined_path = path.map { |p| escape(p) }.join('/')
|
47
|
+
return "#{url}/#{joined_path}"
|
48
|
+
end
|
49
|
+
|
50
|
+
# Send a request and return the parsed JSON response.
|
51
|
+
#
|
52
|
+
# @example
|
53
|
+
# request('get', 'people', 'eric')
|
54
|
+
# request(:put, 'people', 'eric', {:title => "Developer"})
|
55
|
+
#
|
56
|
+
# @overload request(method, *path, params={})
|
57
|
+
# @param [String, Symbol] method
|
58
|
+
# Request method to use ('get', 'post', 'put', 'delete', etc.)
|
59
|
+
# @param [Array] path
|
60
|
+
# Path components for the request
|
61
|
+
# @param [Hash] params
|
62
|
+
# Optional parameters to send in the request.
|
63
|
+
#
|
64
|
+
# @return [Hash]
|
65
|
+
# Parsed response, or an empty hash if parsing failed
|
66
|
+
#
|
67
|
+
def request(method, *path)
|
68
|
+
method = method.to_sym
|
69
|
+
params = extract_options(path)
|
70
|
+
params = {:params => params} if method == :get
|
71
|
+
base_url = Placid::Config.rest_url
|
72
|
+
begin
|
73
|
+
response = RestClient.send(method, url(*path), params)
|
74
|
+
rescue RestClient::Exception => e
|
75
|
+
response = e.response
|
76
|
+
rescue => e
|
77
|
+
raise
|
78
|
+
end
|
79
|
+
return JSON.parse(response) rescue {}
|
80
|
+
end
|
81
|
+
|
82
|
+
# Send a GET request and return the parsed JSON response.
|
83
|
+
#
|
84
|
+
# @example
|
85
|
+
# get('people', 'eric')
|
86
|
+
# get('people', {:name => 'eric'})
|
87
|
+
#
|
88
|
+
# @overload get(*path, params={})
|
89
|
+
# See {#request} for allowed parameters.
|
90
|
+
#
|
91
|
+
# @return [Hash]
|
92
|
+
# Parsed response, or an empty hash if parsing failed
|
93
|
+
#
|
94
|
+
def get(*path)
|
95
|
+
request('get', *path)
|
96
|
+
end
|
97
|
+
|
98
|
+
# Send a POST request and return the parsed JSON response.
|
99
|
+
#
|
100
|
+
# @example
|
101
|
+
# post('people', 'new', {:name => 'eric'})
|
102
|
+
#
|
103
|
+
# @overload post(*path, params={})
|
104
|
+
# See {#request} for allowed parameters.
|
105
|
+
#
|
106
|
+
# @return [Hash]
|
107
|
+
# Parsed response, or an empty hash if parsing failed
|
108
|
+
#
|
109
|
+
def post(*path)
|
110
|
+
request('post', *path)
|
111
|
+
end
|
112
|
+
|
113
|
+
# Send a PUT request and return the parsed JSON response.
|
114
|
+
#
|
115
|
+
# @example
|
116
|
+
# put('people', 'eric', {:title => 'Developer'})
|
117
|
+
#
|
118
|
+
# @overload put(*path, params={})
|
119
|
+
# See {#request} for allowed parameters.
|
120
|
+
#
|
121
|
+
# @return [Hash]
|
122
|
+
# Parsed response, or an empty hash if parsing failed
|
123
|
+
#
|
124
|
+
def put(*path)
|
125
|
+
request('put', *path)
|
126
|
+
end
|
127
|
+
|
128
|
+
# Send a DELETE request and return the parsed JSON response.
|
129
|
+
#
|
130
|
+
# @example
|
131
|
+
# delete('people', 'eric')
|
132
|
+
# delete('people', {:name => 'eric'})
|
133
|
+
#
|
134
|
+
# @overload delete(*path, params={})
|
135
|
+
# See {#request} for allowed parameters.
|
136
|
+
#
|
137
|
+
# @return [Hash]
|
138
|
+
# Parsed response, or an empty hash if parsing failed
|
139
|
+
#
|
140
|
+
def delete(*path)
|
141
|
+
request('delete', *path)
|
142
|
+
end
|
143
|
+
|
144
|
+
# Send a GET to a path that returns a single JSON object, and return the
|
145
|
+
# result as a Hashie::Mash.
|
146
|
+
#
|
147
|
+
# @overload get_mash(*path, params={})
|
148
|
+
# See {#request} for allowed parameters.
|
149
|
+
#
|
150
|
+
# @return [Hashie::Mash]
|
151
|
+
#
|
152
|
+
def get_mash(*path)
|
153
|
+
json = get(*path)
|
154
|
+
begin
|
155
|
+
return Hashie::Mash.new(json)
|
156
|
+
rescue => e
|
157
|
+
raise Placid::JSONParseError,
|
158
|
+
"Cannot parse JSON as key-value pairs: #{e.message}"
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
# Send a GET to a path that returns a JSON array of objects, and return the
|
163
|
+
# result as an array of Hashie::Mash objects.
|
164
|
+
#
|
165
|
+
# @overload get_mashes(*path, params={})
|
166
|
+
# See {#request} for allowed parameters.
|
167
|
+
#
|
168
|
+
# @return [Array]
|
169
|
+
#
|
170
|
+
def get_mashes(*path)
|
171
|
+
json = get(*path)
|
172
|
+
begin
|
173
|
+
return json.map {|rec| Hashie::Mash.new(rec)}
|
174
|
+
rescue => e
|
175
|
+
raise Placid::JSONParseError,
|
176
|
+
"Cannot parse JSON as an array of key-value pairs: #{e.message}"
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
data/lib/placid/model.rb
ADDED
@@ -0,0 +1,159 @@
|
|
1
|
+
require 'hashie'
|
2
|
+
require 'placid/helper'
|
3
|
+
require 'active_support/inflector' # for `pluralize` and `underscore`
|
4
|
+
|
5
|
+
module Placid
|
6
|
+
# Base class for RESTful models
|
7
|
+
class Model < Hashie::Mash
|
8
|
+
|
9
|
+
include Placid::Helper
|
10
|
+
extend Placid::Helper
|
11
|
+
|
12
|
+
# ------------------
|
13
|
+
# Instance methods
|
14
|
+
# ------------------
|
15
|
+
|
16
|
+
# Set the list of errors on this instance.
|
17
|
+
#
|
18
|
+
def errors=(new_errors)
|
19
|
+
self['errors'] = new_errors
|
20
|
+
end
|
21
|
+
|
22
|
+
# Return the list of errors on this instance. If the 'errors' attribute is
|
23
|
+
# either not set, or set to nil, then return an empty list.
|
24
|
+
#
|
25
|
+
def errors
|
26
|
+
return self['errors'] if self['errors']
|
27
|
+
return []
|
28
|
+
end
|
29
|
+
|
30
|
+
# Return true if there are any errors with this model.
|
31
|
+
#
|
32
|
+
def errors?
|
33
|
+
errors && !errors.empty?
|
34
|
+
end
|
35
|
+
|
36
|
+
# Return true if the given field is required.
|
37
|
+
#
|
38
|
+
def required?(field)
|
39
|
+
meta = self.class.meta
|
40
|
+
return meta[field] && meta[field][:required] == true
|
41
|
+
end
|
42
|
+
|
43
|
+
# Save this instance. This creates a new instance, or updates an existing
|
44
|
+
# one, with the attributes in this instance. Return true if creation or
|
45
|
+
# update were successful, false if there were any errors.
|
46
|
+
#
|
47
|
+
# @return [Boolean]
|
48
|
+
# true if save was successful, false if there were any errors
|
49
|
+
#
|
50
|
+
def save
|
51
|
+
existing = self.class.find(self.id)
|
52
|
+
if existing.nil?
|
53
|
+
obj = self.class.create(self.to_hash)
|
54
|
+
else
|
55
|
+
obj = self.class.update(self.id, self.to_hash)
|
56
|
+
end
|
57
|
+
self.merge!(obj)
|
58
|
+
return !errors?
|
59
|
+
end
|
60
|
+
|
61
|
+
# Return the value in the unique_id field.
|
62
|
+
#
|
63
|
+
def id
|
64
|
+
self[self.class.unique_id]
|
65
|
+
end
|
66
|
+
|
67
|
+
|
68
|
+
# ------------------
|
69
|
+
# Class methods
|
70
|
+
# ------------------
|
71
|
+
|
72
|
+
@unique_id = nil
|
73
|
+
@meta = nil
|
74
|
+
|
75
|
+
# Return the `snake_case` name of this model, based on the derived class
|
76
|
+
# name. This name should match the REST API path component used to interact
|
77
|
+
# with the corresponding model.
|
78
|
+
#
|
79
|
+
def self.model
|
80
|
+
return self.name.underscore
|
81
|
+
end
|
82
|
+
|
83
|
+
# Get or set the field name used for uniquely identifying instances of this
|
84
|
+
# model.
|
85
|
+
def self.unique_id(field=nil)
|
86
|
+
if field.nil?
|
87
|
+
return @unique_id || :id
|
88
|
+
else
|
89
|
+
@unique_id = field
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
# Return a Hashie::Mash of meta-data for this model.
|
94
|
+
#
|
95
|
+
def self.meta
|
96
|
+
@meta ||= get_mash(model, 'meta')
|
97
|
+
end
|
98
|
+
|
99
|
+
# Return a Hashie::Mash with a list of all model instances.
|
100
|
+
#
|
101
|
+
def self.list
|
102
|
+
get_mashes(model.pluralize)
|
103
|
+
end
|
104
|
+
|
105
|
+
# Return a Model instance matching the given id
|
106
|
+
#
|
107
|
+
# @param [String] id
|
108
|
+
# Identifier for the model instance to fetch
|
109
|
+
#
|
110
|
+
# @return [Model]
|
111
|
+
#
|
112
|
+
def self.find(id)
|
113
|
+
json = get(model, id)
|
114
|
+
return self.new(json)
|
115
|
+
end
|
116
|
+
|
117
|
+
# Create a new model instance and return it.
|
118
|
+
#
|
119
|
+
# @param [Hash] attrs
|
120
|
+
# Attribute values for the new instance
|
121
|
+
#
|
122
|
+
# @return [Model]
|
123
|
+
#
|
124
|
+
def self.create(attrs={})
|
125
|
+
obj = self.new(attrs)
|
126
|
+
json = post(model, attrs)
|
127
|
+
obj.merge!(json)
|
128
|
+
return obj
|
129
|
+
end
|
130
|
+
|
131
|
+
# Update an existing model instance.
|
132
|
+
#
|
133
|
+
# @param [String] id
|
134
|
+
# Identifier of the model instance to update
|
135
|
+
# @param [Hash] attrs
|
136
|
+
# New attribute values to set
|
137
|
+
#
|
138
|
+
# @return [Model]
|
139
|
+
#
|
140
|
+
def self.update(id, attrs={})
|
141
|
+
obj = self.new(attrs)
|
142
|
+
json = put(model, id, attrs)
|
143
|
+
obj.merge!(json)
|
144
|
+
#obj.errors = json['errors']
|
145
|
+
return obj
|
146
|
+
end
|
147
|
+
|
148
|
+
# Destroy a model instance.
|
149
|
+
#
|
150
|
+
# @param [String] id
|
151
|
+
# Identifier for the model instance to delete
|
152
|
+
#
|
153
|
+
def self.destroy(id)
|
154
|
+
delete(model, id)
|
155
|
+
end
|
156
|
+
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
data/lib/placid.rb
ADDED
data/placid.gemspec
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = "placid"
|
3
|
+
s.version = "0.0.1"
|
4
|
+
s.summary = "Models from REST"
|
5
|
+
s.description = <<-EOS
|
6
|
+
EOS
|
7
|
+
s.authors = ["Eric Pierce"]
|
8
|
+
s.email = "epierce@automation-excellence.com"
|
9
|
+
s.homepage = "http://github.com/a-e/placid"
|
10
|
+
s.platform = Gem::Platform::RUBY
|
11
|
+
|
12
|
+
s.add_dependency 'hashie'
|
13
|
+
s.add_dependency 'json'
|
14
|
+
s.add_dependency 'rest-client'
|
15
|
+
s.add_dependency 'activesupport'
|
16
|
+
|
17
|
+
s.add_development_dependency 'rspec'
|
18
|
+
s.add_development_dependency 'yard' # For documentation
|
19
|
+
s.add_development_dependency 'redcarpet' # For YARD / Markdown
|
20
|
+
s.add_development_dependency 'rcov'
|
21
|
+
|
22
|
+
s.files = `git ls-files`.split("\n")
|
23
|
+
|
24
|
+
s.require_path = 'lib'
|
25
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Placid::Config do
|
4
|
+
after(:each) do
|
5
|
+
Placid::Config.rest_url = Placid::Config.default_url
|
6
|
+
end
|
7
|
+
|
8
|
+
it "has a default rest_url that is read-only" do
|
9
|
+
Placid::Config.rest_url.should == Placid::Config.default_url
|
10
|
+
lambda do
|
11
|
+
Placid::Config.default_url = 'foo'
|
12
|
+
end.should raise_error(NoMethodError)
|
13
|
+
end
|
14
|
+
|
15
|
+
it "allows setting rest_url" do
|
16
|
+
Placid::Config.rest_url = 'http://www.example.com'
|
17
|
+
Placid::Config.rest_url.should == 'http://www.example.com'
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
@@ -0,0 +1,109 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Placid::Helper do
|
4
|
+
describe "#escape" do
|
5
|
+
it "escapes all URI reserved characters" do
|
6
|
+
escape(";/?:@&=+$,[]").should == "%3B%2F%3F%3A%40%26%3D%2B%24%2C%5B%5D"
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
describe "#url" do
|
11
|
+
it "joins path components with '/'" do
|
12
|
+
url('foo', 'bar', 'baz').should == 'http://localhost/foo/bar/baz'
|
13
|
+
end
|
14
|
+
|
15
|
+
it "escapes path components to make them URI-safe" do
|
16
|
+
url('a b', 'c:d', 'e/f').should == 'http://localhost/a%20b/c%3Ad/e%2Ff'
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe "#request" do
|
21
|
+
it "returns a legitimate response as JSON" do
|
22
|
+
RestClient.stub(:get => '["success"]')
|
23
|
+
json = request('get')
|
24
|
+
json.should == ["success"]
|
25
|
+
end
|
26
|
+
|
27
|
+
it "returns a RestClient::Exception response as JSON" do
|
28
|
+
class RestException < RestClient::Exception
|
29
|
+
def response; '["fail"]'; end
|
30
|
+
end
|
31
|
+
RestClient.stub(:get).and_raise(RestException)
|
32
|
+
json = request('get')
|
33
|
+
json.should == ["fail"]
|
34
|
+
end
|
35
|
+
|
36
|
+
it "passes other exceptions through" do
|
37
|
+
RestClient.stub(:get).and_raise(URI::InvalidURIError)
|
38
|
+
lambda do
|
39
|
+
json = request('get')
|
40
|
+
end.should raise_error(URI::InvalidURIError)
|
41
|
+
end
|
42
|
+
|
43
|
+
it "returns an empty hash if there is no response" do
|
44
|
+
RestClient.stub(:get) { nil }
|
45
|
+
json = request('get')
|
46
|
+
json.should == {}
|
47
|
+
end
|
48
|
+
|
49
|
+
it "accepts a params hash as the last argument" do
|
50
|
+
RestClient.should_receive(:post).with('http://localhost/foo', {:bar => 'hi'})
|
51
|
+
json = request('post', 'foo', :bar => 'hi')
|
52
|
+
end
|
53
|
+
|
54
|
+
it "sends an empty params hash if none is given" do
|
55
|
+
RestClient.should_receive(:post).with('http://localhost/foo', {})
|
56
|
+
json = request('post', 'foo')
|
57
|
+
end
|
58
|
+
|
59
|
+
it "sends :params => params for get requests" do
|
60
|
+
RestClient.should_receive(:get).with('http://localhost/foo', {:params => {:x => 'y'}})
|
61
|
+
json = request('get', 'foo', :x => 'y')
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
describe "#get_mash" do
|
66
|
+
it "returns a Hashie::Mash for hash data" do
|
67
|
+
data = {
|
68
|
+
'first_name' => 'Nathan',
|
69
|
+
'last_name' => 'Stark',
|
70
|
+
}
|
71
|
+
RestClient.stub(:get => JSON(data))
|
72
|
+
mash = get_mash
|
73
|
+
mash.first_name.should == 'Nathan'
|
74
|
+
mash.last_name.should == 'Stark'
|
75
|
+
end
|
76
|
+
|
77
|
+
it "raises an exception when the JSON cannot be parsed" do
|
78
|
+
data = ['a', 'b', 'c']
|
79
|
+
RestClient.stub(:get => JSON(data))
|
80
|
+
lambda do
|
81
|
+
mashes = get_mash
|
82
|
+
end.should raise_error(Placid::JSONParseError)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
describe "#get_mashes" do
|
87
|
+
it "returns a list of Hashie::Mash for list data" do
|
88
|
+
data = [
|
89
|
+
{'first_name' => 'Jack', 'last_name' => 'Carter'},
|
90
|
+
{'first_name' => 'Allison', 'last_name' => 'Blake'},
|
91
|
+
]
|
92
|
+
RestClient.stub(:get => JSON(data))
|
93
|
+
mashes = get_mashes
|
94
|
+
mashes.first.first_name.should == 'Jack'
|
95
|
+
mashes.first.last_name.should == 'Carter'
|
96
|
+
mashes.last.first_name.should == 'Allison'
|
97
|
+
mashes.last.last_name.should == 'Blake'
|
98
|
+
end
|
99
|
+
|
100
|
+
it "raises an exception when the JSON cannot be parsed" do
|
101
|
+
data = ['a', 'b', 'c']
|
102
|
+
RestClient.stub(:get => JSON(data))
|
103
|
+
lambda do
|
104
|
+
mashes = get_mashes
|
105
|
+
end.should raise_error(Placid::JSONParseError)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
@@ -0,0 +1,330 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Placid::Model do
|
4
|
+
class Thing < Placid::Model
|
5
|
+
end
|
6
|
+
|
7
|
+
context "Instance methods" do
|
8
|
+
describe "#id" do
|
9
|
+
it "returns the value in the custom unique ID field" do
|
10
|
+
class Person < Placid::Model
|
11
|
+
unique_id :email
|
12
|
+
end
|
13
|
+
person_1 = Person.new(:email => 'foo1@bar.com')
|
14
|
+
person_2 = Person.new(:email => 'foo2@bar.org')
|
15
|
+
person_1.id.should == 'foo1@bar.com'
|
16
|
+
person_2.id.should == 'foo2@bar.org'
|
17
|
+
end
|
18
|
+
|
19
|
+
it "returns the value in the :id field if no custom field was set" do
|
20
|
+
thing_1 = Thing.new(:id => '111')
|
21
|
+
thing_2 = Thing.new(:id => '222')
|
22
|
+
thing_1.id.should == '111'
|
23
|
+
thing_2.id.should == '222'
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe "#save" do
|
28
|
+
it "creates a new instance if one doesn't exist" do
|
29
|
+
thing = Thing.new(:id => '123')
|
30
|
+
Thing.stub(:find => nil)
|
31
|
+
Thing.should_receive(:create).
|
32
|
+
with({'id' => '123'}).
|
33
|
+
and_return(Thing.new)
|
34
|
+
thing.save
|
35
|
+
end
|
36
|
+
|
37
|
+
it "updates an existing instance" do
|
38
|
+
thing = Thing.new(:id => '123')
|
39
|
+
Thing.stub(:find => {:id => '123'})
|
40
|
+
Thing.should_receive(:update).
|
41
|
+
with('123', {'id' => '123'}).
|
42
|
+
and_return(Thing.new)
|
43
|
+
thing.save
|
44
|
+
end
|
45
|
+
|
46
|
+
it "merges saved attributes on create" do
|
47
|
+
thing = Thing.new(:id => '123')
|
48
|
+
saved_attribs = {'id' => '123', 'name' => 'foo'}
|
49
|
+
Thing.stub(:find => nil)
|
50
|
+
Thing.should_receive(:create).
|
51
|
+
with({'id' => '123'}).
|
52
|
+
and_return(Thing.new(saved_attribs))
|
53
|
+
thing.save
|
54
|
+
thing.should == saved_attribs
|
55
|
+
end
|
56
|
+
|
57
|
+
it "merges saved attributes on update" do
|
58
|
+
thing = Thing.new(:id => '123')
|
59
|
+
saved_attribs = {'id' => '123', 'name' => 'foo'}
|
60
|
+
Thing.stub(:find => {:id => '123'})
|
61
|
+
Thing.should_receive(:update).
|
62
|
+
with('123', {'id' => '123'}).
|
63
|
+
and_return(Thing.new(saved_attribs))
|
64
|
+
thing.save
|
65
|
+
thing.should == saved_attribs
|
66
|
+
end
|
67
|
+
|
68
|
+
it "returns false if errors were reported" do
|
69
|
+
thing = Thing.new
|
70
|
+
Thing.stub(:find => nil)
|
71
|
+
Thing.stub(:post => {'errors' => 'Missing id'})
|
72
|
+
thing.save.should be_false
|
73
|
+
end
|
74
|
+
|
75
|
+
it "returns true if errors is an empty list" do
|
76
|
+
thing = Thing.new(:id => '123')
|
77
|
+
Thing.stub(:find => nil)
|
78
|
+
Thing.stub(:post => {'errors' => []})
|
79
|
+
thing.save.should be_true
|
80
|
+
end
|
81
|
+
|
82
|
+
it "returns true if no errors were reported" do
|
83
|
+
thing = Thing.new(:id => '123')
|
84
|
+
Thing.stub(:find => nil)
|
85
|
+
Thing.stub(:post => {})
|
86
|
+
thing.save.should be_true
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
describe "#required?" do
|
91
|
+
it "true if the given field is implicitly required" do
|
92
|
+
Thing.stub(:meta => {:id => {:required => true}})
|
93
|
+
thing = Thing.new
|
94
|
+
thing.required?(:id).should == true
|
95
|
+
end
|
96
|
+
|
97
|
+
it "false if the given field is explicitly optional" do
|
98
|
+
Thing.stub(:meta => {:id => {:required => false}})
|
99
|
+
thing = Thing.new
|
100
|
+
thing.required?(:id).should == false
|
101
|
+
end
|
102
|
+
|
103
|
+
it "false if the given field is implicitly optional" do
|
104
|
+
Thing.stub(:meta => {:id => {}})
|
105
|
+
thing = Thing.new
|
106
|
+
thing.required?(:id).should == false
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
describe "#errors=" do
|
111
|
+
it "sets the list of errors on the instance" do
|
112
|
+
thing = Thing.new
|
113
|
+
thing.errors = ['missing id']
|
114
|
+
thing['errors'].should == ['missing id']
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
describe "#errors" do
|
119
|
+
it "returns errors set on initialization" do
|
120
|
+
thing = Thing.new(:errors => ['missing id'])
|
121
|
+
thing.errors = ['missing id']
|
122
|
+
thing.errors.should == ['missing id']
|
123
|
+
end
|
124
|
+
|
125
|
+
it "returns errors set after initialization" do
|
126
|
+
thing = Thing.new
|
127
|
+
thing.errors = ['missing id']
|
128
|
+
thing.errors.should == ['missing id']
|
129
|
+
end
|
130
|
+
|
131
|
+
it "returns [] if errors are not set" do
|
132
|
+
thing = Thing.new
|
133
|
+
thing.errors.should == []
|
134
|
+
end
|
135
|
+
|
136
|
+
it "returns [] if errors is set to nil" do
|
137
|
+
thing = Thing.new
|
138
|
+
thing.errors = nil
|
139
|
+
thing.errors.should == []
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
describe "#errors?" do
|
144
|
+
it "returns true if errors is set to a nonempty value" do
|
145
|
+
thing = Thing.new(:errors => ['missing id'])
|
146
|
+
thing.errors?.should be_true
|
147
|
+
end
|
148
|
+
|
149
|
+
it "returns false if errors it not set" do
|
150
|
+
thing = Thing.new
|
151
|
+
thing.errors?.should be_false
|
152
|
+
end
|
153
|
+
|
154
|
+
it "returns false if errors is set to nil" do
|
155
|
+
thing = Thing.new
|
156
|
+
thing.errors = nil
|
157
|
+
thing.errors?.should be_false
|
158
|
+
end
|
159
|
+
|
160
|
+
it "returns false if errors is set to an empty list" do
|
161
|
+
thing = Thing.new
|
162
|
+
thing.errors = []
|
163
|
+
thing.errors?.should be_false
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
describe "helpers" do
|
168
|
+
it "can call #get on an instance" do
|
169
|
+
thing = Thing.new
|
170
|
+
RestClient.should_receive(:get).
|
171
|
+
with('http://localhost/thing/foo', {:params => {:x => 'y'}})
|
172
|
+
thing.get('thing', 'foo', :x => 'y')
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
context "Class methods" do
|
178
|
+
describe "#unique_id" do
|
179
|
+
it "returns :id if no ID was set in the derived class" do
|
180
|
+
class Default < Placid::Model
|
181
|
+
end
|
182
|
+
Default.unique_id.should == :id
|
183
|
+
end
|
184
|
+
|
185
|
+
it "returns the unique ID that was set in the derived class" do
|
186
|
+
class Explicit < Placid::Model
|
187
|
+
unique_id :custom
|
188
|
+
end
|
189
|
+
Explicit.unique_id.should == :custom
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
describe "#model" do
|
194
|
+
it "converts CamelCase to snake_case" do
|
195
|
+
class MyModelName < Placid::Model
|
196
|
+
end
|
197
|
+
MyModelName.model.should == 'my_model_name'
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
describe "#meta" do
|
202
|
+
it "returns a Mash of model meta-data" do
|
203
|
+
thing_meta = {
|
204
|
+
'name' => {'type' => 'String', 'required' => true}
|
205
|
+
}
|
206
|
+
RestClient.should_receive(:get).
|
207
|
+
with('http://localhost/thing/meta', {:params => {}}).
|
208
|
+
and_return(JSON(thing_meta))
|
209
|
+
Thing.meta.should == thing_meta
|
210
|
+
end
|
211
|
+
|
212
|
+
it "only sends a GET meta request once for the class" do
|
213
|
+
thing_meta = {
|
214
|
+
'name' => {'type' => 'String', 'required' => true}
|
215
|
+
}
|
216
|
+
RestClient.stub(:get => JSON(thing_meta))
|
217
|
+
RestClient.should_receive(:get).at_most(:once)
|
218
|
+
Thing.meta.should == thing_meta
|
219
|
+
Thing.meta.should == thing_meta
|
220
|
+
Thing.meta.should == thing_meta
|
221
|
+
end
|
222
|
+
|
223
|
+
it "stores meta-data separately for each derived class" do
|
224
|
+
class ThingOne < Placid::Model; end
|
225
|
+
class ThingTwo < Placid::Model; end
|
226
|
+
|
227
|
+
thing_one_meta = {
|
228
|
+
'one' => {'type' => 'String', 'required' => true}
|
229
|
+
}
|
230
|
+
RestClient.should_receive(:get).
|
231
|
+
with('http://localhost/thing_one/meta', {:params => {}}).
|
232
|
+
and_return(JSON(thing_one_meta))
|
233
|
+
ThingOne.meta.should == thing_one_meta
|
234
|
+
|
235
|
+
thing_two_meta = {
|
236
|
+
'two' => {'type' => 'String', 'required' => false}
|
237
|
+
}
|
238
|
+
RestClient.should_receive(:get).
|
239
|
+
with('http://localhost/thing_two/meta', {:params => {}}).
|
240
|
+
and_return(JSON(thing_two_meta))
|
241
|
+
ThingTwo.meta.should == thing_two_meta
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
describe "#list" do
|
246
|
+
it "returns a Mash list of all model instances" do
|
247
|
+
data = [
|
248
|
+
{'name' => 'Foo'},
|
249
|
+
{'name' => 'Bar'},
|
250
|
+
]
|
251
|
+
RestClient.stub(:get => JSON(data))
|
252
|
+
Thing.list.should == data
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
describe "#find" do
|
257
|
+
it "returns a Model instance matching the given id" do
|
258
|
+
data = {'name' => 'Foo'}
|
259
|
+
RestClient.stub(:get => JSON(data))
|
260
|
+
Thing.find(1).should == data
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
describe "#create" do
|
265
|
+
context "attributes include" do
|
266
|
+
it "posted attributes if no attributes were returned" do
|
267
|
+
RestClient.stub(:post => '{}')
|
268
|
+
attrs = {'name' => 'Foo'}
|
269
|
+
Thing.create(attrs).should == {'name' => 'Foo'}
|
270
|
+
end
|
271
|
+
|
272
|
+
it "returned attributes if no attributes were posted" do
|
273
|
+
RestClient.stub(:post => '{"uri": "foo"}')
|
274
|
+
attrs = {}
|
275
|
+
Thing.create(attrs).should == {'uri' => 'foo'}
|
276
|
+
end
|
277
|
+
|
278
|
+
it "original attributes merged with returned attributes" do
|
279
|
+
RestClient.stub(:post => '{"uri": "foo"}')
|
280
|
+
attrs = {'name' => 'Foo'}
|
281
|
+
Thing.create(attrs).should == {'name' => 'Foo', 'uri' => 'foo'}
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
285
|
+
it "sets errors on the Model instance" do
|
286
|
+
data = {'errors' => ['name is required']}
|
287
|
+
RestClient.stub(:post => JSON(data))
|
288
|
+
Thing.create().errors.should == ['name is required']
|
289
|
+
end
|
290
|
+
end
|
291
|
+
|
292
|
+
describe "#update" do
|
293
|
+
context "attributes include" do
|
294
|
+
it "posted attributes if no attributes were returned" do
|
295
|
+
RestClient.stub(:put => '{}')
|
296
|
+
attrs = {'name' => 'Foo'}
|
297
|
+
result = Thing.update(1, attrs)
|
298
|
+
Thing.update(1, attrs).should == {'name' => 'Foo'}
|
299
|
+
end
|
300
|
+
|
301
|
+
it "returned attributes if no attributes were posted" do
|
302
|
+
RestClient.stub(:put => '{"uri": "foo"}')
|
303
|
+
attrs = {}
|
304
|
+
Thing.update(1, attrs).should == {'uri' => 'foo'}
|
305
|
+
end
|
306
|
+
|
307
|
+
it "original attributes merged with returned attributes" do
|
308
|
+
RestClient.stub(:put => '{"uri": "foo"}')
|
309
|
+
attrs = {'name' => 'Foo'}
|
310
|
+
Thing.update(1, attrs).should == {'name' => 'Foo', 'uri' => 'foo'}
|
311
|
+
end
|
312
|
+
end
|
313
|
+
|
314
|
+
it "sets errors on the Model instance" do
|
315
|
+
data = {'errors' => ['name is required']}
|
316
|
+
RestClient.stub(:put => JSON(data))
|
317
|
+
Thing.update(1, {}).errors.should == ['name is required']
|
318
|
+
end
|
319
|
+
end
|
320
|
+
|
321
|
+
describe "#destroy" do
|
322
|
+
it "returns the parsed JSON response" do
|
323
|
+
data = {'status' => 'ok'}
|
324
|
+
RestClient.stub(:delete => JSON(data))
|
325
|
+
Thing.destroy(1).should == data
|
326
|
+
end
|
327
|
+
end
|
328
|
+
end
|
329
|
+
end
|
330
|
+
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
# This file includes RSpec configuration that is needed for all spec testing.
|
2
|
+
|
3
|
+
require 'rspec'
|
4
|
+
require 'rspec/autorun' # needed for RSpec 2.6.x
|
5
|
+
require 'placid'
|
6
|
+
require 'json'
|
7
|
+
|
8
|
+
RSpec.configure do |config|
|
9
|
+
config.color_enabled = true
|
10
|
+
config.include Placid
|
11
|
+
config.include Placid::Helper
|
12
|
+
end
|
metadata
ADDED
@@ -0,0 +1,192 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: placid
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 29
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 1
|
10
|
+
version: 0.0.1
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Eric Pierce
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2012-05-17 00:00:00 Z
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: hashie
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
25
|
+
requirements:
|
26
|
+
- - ">="
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
hash: 3
|
29
|
+
segments:
|
30
|
+
- 0
|
31
|
+
version: "0"
|
32
|
+
type: :runtime
|
33
|
+
version_requirements: *id001
|
34
|
+
- !ruby/object:Gem::Dependency
|
35
|
+
name: json
|
36
|
+
prerelease: false
|
37
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
38
|
+
none: false
|
39
|
+
requirements:
|
40
|
+
- - ">="
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
hash: 3
|
43
|
+
segments:
|
44
|
+
- 0
|
45
|
+
version: "0"
|
46
|
+
type: :runtime
|
47
|
+
version_requirements: *id002
|
48
|
+
- !ruby/object:Gem::Dependency
|
49
|
+
name: rest-client
|
50
|
+
prerelease: false
|
51
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
52
|
+
none: false
|
53
|
+
requirements:
|
54
|
+
- - ">="
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
hash: 3
|
57
|
+
segments:
|
58
|
+
- 0
|
59
|
+
version: "0"
|
60
|
+
type: :runtime
|
61
|
+
version_requirements: *id003
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: activesupport
|
64
|
+
prerelease: false
|
65
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
66
|
+
none: false
|
67
|
+
requirements:
|
68
|
+
- - ">="
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
hash: 3
|
71
|
+
segments:
|
72
|
+
- 0
|
73
|
+
version: "0"
|
74
|
+
type: :runtime
|
75
|
+
version_requirements: *id004
|
76
|
+
- !ruby/object:Gem::Dependency
|
77
|
+
name: rspec
|
78
|
+
prerelease: false
|
79
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
80
|
+
none: false
|
81
|
+
requirements:
|
82
|
+
- - ">="
|
83
|
+
- !ruby/object:Gem::Version
|
84
|
+
hash: 3
|
85
|
+
segments:
|
86
|
+
- 0
|
87
|
+
version: "0"
|
88
|
+
type: :development
|
89
|
+
version_requirements: *id005
|
90
|
+
- !ruby/object:Gem::Dependency
|
91
|
+
name: yard
|
92
|
+
prerelease: false
|
93
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
94
|
+
none: false
|
95
|
+
requirements:
|
96
|
+
- - ">="
|
97
|
+
- !ruby/object:Gem::Version
|
98
|
+
hash: 3
|
99
|
+
segments:
|
100
|
+
- 0
|
101
|
+
version: "0"
|
102
|
+
type: :development
|
103
|
+
version_requirements: *id006
|
104
|
+
- !ruby/object:Gem::Dependency
|
105
|
+
name: redcarpet
|
106
|
+
prerelease: false
|
107
|
+
requirement: &id007 !ruby/object:Gem::Requirement
|
108
|
+
none: false
|
109
|
+
requirements:
|
110
|
+
- - ">="
|
111
|
+
- !ruby/object:Gem::Version
|
112
|
+
hash: 3
|
113
|
+
segments:
|
114
|
+
- 0
|
115
|
+
version: "0"
|
116
|
+
type: :development
|
117
|
+
version_requirements: *id007
|
118
|
+
- !ruby/object:Gem::Dependency
|
119
|
+
name: rcov
|
120
|
+
prerelease: false
|
121
|
+
requirement: &id008 !ruby/object:Gem::Requirement
|
122
|
+
none: false
|
123
|
+
requirements:
|
124
|
+
- - ">="
|
125
|
+
- !ruby/object:Gem::Version
|
126
|
+
hash: 3
|
127
|
+
segments:
|
128
|
+
- 0
|
129
|
+
version: "0"
|
130
|
+
type: :development
|
131
|
+
version_requirements: *id008
|
132
|
+
description: ""
|
133
|
+
email: epierce@automation-excellence.com
|
134
|
+
executables: []
|
135
|
+
|
136
|
+
extensions: []
|
137
|
+
|
138
|
+
extra_rdoc_files: []
|
139
|
+
|
140
|
+
files:
|
141
|
+
- .gitignore
|
142
|
+
- .yardopts
|
143
|
+
- Gemfile
|
144
|
+
- MIT-LICENSE
|
145
|
+
- README.md
|
146
|
+
- Rakefile
|
147
|
+
- lib/placid.rb
|
148
|
+
- lib/placid/config.rb
|
149
|
+
- lib/placid/exceptions.rb
|
150
|
+
- lib/placid/helper.rb
|
151
|
+
- lib/placid/model.rb
|
152
|
+
- placid.gemspec
|
153
|
+
- spec/placid_config_spec.rb
|
154
|
+
- spec/placid_helper_spec.rb
|
155
|
+
- spec/placid_model_spec.rb
|
156
|
+
- spec/spec_helper.rb
|
157
|
+
homepage: http://github.com/a-e/placid
|
158
|
+
licenses: []
|
159
|
+
|
160
|
+
post_install_message:
|
161
|
+
rdoc_options: []
|
162
|
+
|
163
|
+
require_paths:
|
164
|
+
- lib
|
165
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
166
|
+
none: false
|
167
|
+
requirements:
|
168
|
+
- - ">="
|
169
|
+
- !ruby/object:Gem::Version
|
170
|
+
hash: 3
|
171
|
+
segments:
|
172
|
+
- 0
|
173
|
+
version: "0"
|
174
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
175
|
+
none: false
|
176
|
+
requirements:
|
177
|
+
- - ">="
|
178
|
+
- !ruby/object:Gem::Version
|
179
|
+
hash: 3
|
180
|
+
segments:
|
181
|
+
- 0
|
182
|
+
version: "0"
|
183
|
+
requirements: []
|
184
|
+
|
185
|
+
rubyforge_project:
|
186
|
+
rubygems_version: 1.8.23
|
187
|
+
signing_key:
|
188
|
+
specification_version: 3
|
189
|
+
summary: Models from REST
|
190
|
+
test_files: []
|
191
|
+
|
192
|
+
has_rdoc:
|