placid 0.0.1
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.
- 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:
|