well_rested 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.rspec +1 -0
- data/Gemfile +27 -0
- data/Gemfile.lock +69 -0
- data/LICENSE.txt +20 -0
- data/README.rdoc +140 -0
- data/Rakefile +49 -0
- data/VERSION +1 -0
- data/bin/well_rested +27 -0
- data/examples/hn_search.rb +29 -0
- data/lib/generic_utils.rb +17 -0
- data/lib/key_transformer.rb +43 -0
- data/lib/well_rested.rb +35 -0
- data/lib/well_rested/api.rb +343 -0
- data/lib/well_rested/base.rb +290 -0
- data/lib/well_rested/camel_case_formatter.rb +15 -0
- data/lib/well_rested/json_formatter.rb +12 -0
- data/lib/well_rested/utils.rb +26 -0
- data/spec/api_spec.rb +619 -0
- data/spec/base_spec.rb +352 -0
- data/spec/generic_utils_spec.rb +13 -0
- data/spec/key_transformer_spec.rb +42 -0
- data/spec/spec_helper.rb +19 -0
- data/spec/spec_spec.rb +18 -0
- data/spec/support/api.rb +26 -0
- data/spec/utils_spec.rb +37 -0
- data/spec/well_rested_spec.rb +4 -0
- data/well_rested.gemspec +107 -0
- metadata +295 -0
data/.document
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/Gemfile
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
source "http://rubygems.org"
|
2
|
+
# Add dependencies required to use your gem here.
|
3
|
+
# Example:
|
4
|
+
# gem "activesupport", ">= 2.3.5"
|
5
|
+
|
6
|
+
gem 'json'
|
7
|
+
gem 'activemodel'
|
8
|
+
gem 'activeresource'
|
9
|
+
gem 'uuidtools'
|
10
|
+
|
11
|
+
gem 'rest-client'
|
12
|
+
gem 'rest-client-components'
|
13
|
+
|
14
|
+
# Add dependencies to develop your gem here.
|
15
|
+
# Include everything needed to run rake, tests, features, etc.
|
16
|
+
group :development do
|
17
|
+
gem "rspec", "~> 2.8.0"
|
18
|
+
gem "rdoc", "~> 3.12"
|
19
|
+
gem "bundler", ">= 1.0.0"
|
20
|
+
gem "jeweler", "~> 1.8.3"
|
21
|
+
gem "rcov", ">= 0"
|
22
|
+
|
23
|
+
gem 'autotest'
|
24
|
+
gem 'fakeweb'
|
25
|
+
gem 'fakeweb-matcher'
|
26
|
+
#gem 'rack-cache'
|
27
|
+
end
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://rubygems.org/
|
3
|
+
specs:
|
4
|
+
ZenTest (4.6.2)
|
5
|
+
activemodel (3.2.2)
|
6
|
+
activesupport (= 3.2.2)
|
7
|
+
builder (~> 3.0.0)
|
8
|
+
activeresource (3.2.2)
|
9
|
+
activemodel (= 3.2.2)
|
10
|
+
activesupport (= 3.2.2)
|
11
|
+
activesupport (3.2.2)
|
12
|
+
i18n (~> 0.6)
|
13
|
+
multi_json (~> 1.0)
|
14
|
+
autotest (4.4.6)
|
15
|
+
ZenTest (>= 4.4.1)
|
16
|
+
builder (3.0.0)
|
17
|
+
diff-lcs (1.1.3)
|
18
|
+
fakeweb (1.3.0)
|
19
|
+
fakeweb-matcher (1.2.2)
|
20
|
+
fakeweb (>= 1.2.5)
|
21
|
+
rspec (>= 1.2.0)
|
22
|
+
git (1.2.5)
|
23
|
+
i18n (0.6.0)
|
24
|
+
jeweler (1.8.3)
|
25
|
+
bundler (~> 1.0)
|
26
|
+
git (>= 1.2.5)
|
27
|
+
rake
|
28
|
+
rdoc
|
29
|
+
json (1.6.5)
|
30
|
+
mime-types (1.17.2)
|
31
|
+
multi_json (1.1.0)
|
32
|
+
rack (1.4.1)
|
33
|
+
rake (0.9.2.2)
|
34
|
+
rcov (1.0.0)
|
35
|
+
rdoc (3.12)
|
36
|
+
json (~> 1.4)
|
37
|
+
rest-client (1.6.7)
|
38
|
+
mime-types (>= 1.16)
|
39
|
+
rest-client-components (1.2.0)
|
40
|
+
rack (>= 1.0.1)
|
41
|
+
rest-client (>= 1.6.0, < 1.7.0)
|
42
|
+
rspec (2.8.0)
|
43
|
+
rspec-core (~> 2.8.0)
|
44
|
+
rspec-expectations (~> 2.8.0)
|
45
|
+
rspec-mocks (~> 2.8.0)
|
46
|
+
rspec-core (2.8.0)
|
47
|
+
rspec-expectations (2.8.0)
|
48
|
+
diff-lcs (~> 1.1.2)
|
49
|
+
rspec-mocks (2.8.0)
|
50
|
+
uuidtools (2.1.2)
|
51
|
+
|
52
|
+
PLATFORMS
|
53
|
+
ruby
|
54
|
+
|
55
|
+
DEPENDENCIES
|
56
|
+
activemodel
|
57
|
+
activeresource
|
58
|
+
autotest
|
59
|
+
bundler (>= 1.0.0)
|
60
|
+
fakeweb
|
61
|
+
fakeweb-matcher
|
62
|
+
jeweler (~> 1.8.3)
|
63
|
+
json
|
64
|
+
rcov
|
65
|
+
rdoc (~> 3.12)
|
66
|
+
rest-client
|
67
|
+
rest-client-components
|
68
|
+
rspec (~> 2.8.0)
|
69
|
+
uuidtools
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2012 Deep Web Technologies, inc.
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,140 @@
|
|
1
|
+
= well_rested
|
2
|
+
|
3
|
+
WellRested (WR) is a resource-based REST-API client library for Ruby.
|
4
|
+
|
5
|
+
Like ActiveResource, it supports ActiveRecord-style validations and works with Rails form helpers.
|
6
|
+
|
7
|
+
WellRested was created to address some limitations in ActiveResource:
|
8
|
+
|
9
|
+
- Supports transparent HTTP caching (via Rack::Cache / rest-client-components).
|
10
|
+
- Avoids race conditions when changing HTTP authentication by specifying it on an API object rather than in the Base class.
|
11
|
+
- Easy to override the object loading / serializing methods to modify attributes on the way in or out.
|
12
|
+
- Handles camel-cased attribute and resource names.
|
13
|
+
- Will not put file-extensions (e.g. .json) on the end of paths unless you tell it to.
|
14
|
+
|
15
|
+
== Features to Add
|
16
|
+
|
17
|
+
- XML support. Currently, only JSON is supported.
|
18
|
+
|
19
|
+
If you want to use WR, but are encountering a limitation, I encourage you to file an issue on GitHub
|
20
|
+
or to fork your own version and send a pull request with your changes.
|
21
|
+
|
22
|
+
== Installation
|
23
|
+
|
24
|
+
gem install well_rested
|
25
|
+
|
26
|
+
Or, if using bundler, add the following to your Gemfile
|
27
|
+
|
28
|
+
gem 'well_rested'
|
29
|
+
|
30
|
+
Then
|
31
|
+
|
32
|
+
bundle install
|
33
|
+
|
34
|
+
== Sample Usage with Rails
|
35
|
+
|
36
|
+
In app/models/base.rb
|
37
|
+
|
38
|
+
# It is often convenient to set your defaults in a base class.
|
39
|
+
class Base < WellRested::Base
|
40
|
+
self.protocol = 'https'
|
41
|
+
self.server = 'example.com:8888'
|
42
|
+
|
43
|
+
# Send and receive request bodies in JSON. This is the default.
|
44
|
+
self.body_formatter = WellRested::JSONFormatter.new
|
45
|
+
self.extension = '.json' # add '.json' to the end of URLs. Default is an empty string.
|
46
|
+
|
47
|
+
# Encode multi-word attributes names into lowerCamelCase. This is the default.
|
48
|
+
self.attribute_formatter = WellRested::CamelCaseFormatter.new(:lower)
|
49
|
+
end
|
50
|
+
|
51
|
+
In app/models/my_resource.rb
|
52
|
+
|
53
|
+
class MyResource < Base
|
54
|
+
self.path = '/users/:user_id/my_resources' # NOTE: Path must begin with a slash!
|
55
|
+
|
56
|
+
define_schema :id, :user_id, :name, :status => 'active' # status defaults to 'active'
|
57
|
+
|
58
|
+
# ActiveModel validations may be applied here.
|
59
|
+
# They will be checked before a save or create request is made.
|
60
|
+
validates :name, :length => { :maximum => 127 }, :presence => true
|
61
|
+
end
|
62
|
+
|
63
|
+
In app/controllers/application_controller.rb
|
64
|
+
|
65
|
+
before_filter :load_api
|
66
|
+
|
67
|
+
def load_api
|
68
|
+
@api = WellRested::API.new
|
69
|
+
# set basic auth
|
70
|
+
@api.user = 'username'
|
71
|
+
@api.password = 'pass'
|
72
|
+
end
|
73
|
+
|
74
|
+
In app/controllers/my_resources_controller.rb
|
75
|
+
|
76
|
+
def index
|
77
|
+
@my_resources = @api.find_many(MyResource, :user_id => current_user.id)
|
78
|
+
end
|
79
|
+
|
80
|
+
def create
|
81
|
+
@my_resource = MyResource.new(params[:my_resource])
|
82
|
+
@my_resource.user_id = current_user.id
|
83
|
+
|
84
|
+
# If current_user.id is 22, then this will POST to https://example.com:8888/users/22/my_resources
|
85
|
+
# with a JSON payload e.g. '{"name":"My Resource","user_id":22,"status":"active"}'
|
86
|
+
if @api.save(@my_resource)
|
87
|
+
flash[:notice] = 'Created resource!'
|
88
|
+
redirect_to my_resources_path
|
89
|
+
else
|
90
|
+
flash[:warning] = 'Error creating resource!'
|
91
|
+
render :new
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def update
|
96
|
+
@my_resource = MyResource.new(params[:my_resource])
|
97
|
+
|
98
|
+
# Assuming @my_resource.user_id is 22 and @my_resource.id is 41
|
99
|
+
# If valid, PUT to https://example.com:8888/users/22/my_resources/41
|
100
|
+
# with a JSON payload e.g. '{"name":"My Resource","user_id":22,"status":"active","id":41}'
|
101
|
+
if @api.save(@my_resource)
|
102
|
+
flash[:notice] = 'Resource Saved!'
|
103
|
+
redirect_to @my_resource
|
104
|
+
else
|
105
|
+
flash[:error] 'Error saving resource!'
|
106
|
+
render :edit and return
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def destroy
|
111
|
+
# If params[:id] is 41, then this will DELETE https://example.com:8888/users/22/my_resources/41
|
112
|
+
if @api.delete(MyResource, :user_id => current_user.id, :id => params[:id])
|
113
|
+
flash[:notice] = 'Deleted resource.'
|
114
|
+
else
|
115
|
+
# If the server returns a 422 and an errors attribute containing a list of error messages,
|
116
|
+
# they will automatically be added to the object's errors array.
|
117
|
+
flash[:error] = 'Failed to delete resource.'
|
118
|
+
end
|
119
|
+
redirect_to my_resources_path
|
120
|
+
end
|
121
|
+
|
122
|
+
# etc.
|
123
|
+
|
124
|
+
== Contributing to well_rested
|
125
|
+
|
126
|
+
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
|
127
|
+
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it.
|
128
|
+
* Fork the project.
|
129
|
+
* Start a feature/bugfix branch.
|
130
|
+
* Commit and push until you are happy with your contribution.
|
131
|
+
* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
|
132
|
+
* Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
|
133
|
+
|
134
|
+
== Copyright
|
135
|
+
|
136
|
+
Copyright (c) 2012 Deep Web Technologies, inc. See LICENSE.txt for further details.
|
137
|
+
|
138
|
+
Written by Nick Urban.
|
139
|
+
|
140
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler'
|
5
|
+
begin
|
6
|
+
Bundler.setup(:default, :development)
|
7
|
+
rescue Bundler::BundlerError => e
|
8
|
+
$stderr.puts e.message
|
9
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
10
|
+
exit e.status_code
|
11
|
+
end
|
12
|
+
require 'rake'
|
13
|
+
|
14
|
+
require 'jeweler'
|
15
|
+
Jeweler::Tasks.new do |gem|
|
16
|
+
# gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
|
17
|
+
gem.name = "well_rested"
|
18
|
+
gem.homepage = "https://github.com/DeepWebTechnologies/well_rested"
|
19
|
+
gem.license = "MIT"
|
20
|
+
gem.summary = %Q{A Resource-based REST Client library (ActiveResource replacement).}
|
21
|
+
gem.description = %Q{An Active-Resource replacement that supports camel-cased APIs and HTTP caching, and avoids race conditions when changing authentication information on the fly.}
|
22
|
+
gem.email = "nickurban@gmail.com"
|
23
|
+
gem.authors = ["Nick Urban"]
|
24
|
+
# dependencies defined in Gemfile
|
25
|
+
end
|
26
|
+
Jeweler::RubygemsDotOrgTasks.new
|
27
|
+
|
28
|
+
require 'rspec/core'
|
29
|
+
require 'rspec/core/rake_task'
|
30
|
+
RSpec::Core::RakeTask.new(:spec) do |spec|
|
31
|
+
spec.pattern = FileList['spec/**/*_spec.rb']
|
32
|
+
end
|
33
|
+
|
34
|
+
RSpec::Core::RakeTask.new(:rcov) do |spec|
|
35
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
36
|
+
spec.rcov = true
|
37
|
+
end
|
38
|
+
|
39
|
+
task :default => :spec
|
40
|
+
|
41
|
+
require 'rdoc/task'
|
42
|
+
Rake::RDocTask.new do |rdoc|
|
43
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
44
|
+
|
45
|
+
rdoc.rdoc_dir = 'rdoc'
|
46
|
+
rdoc.title = "well_rested #{version}"
|
47
|
+
rdoc.rdoc_files.include('README*')
|
48
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
49
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.6.0
|
data/bin/well_rested
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
#!/usr/bin/env ruby -w
|
2
|
+
|
3
|
+
# RubyGems sets up the load path, but we're loading from the local directry too.
|
4
|
+
$LOAD_PATH.unshift File.dirname(__FILE__) + '/../lib'
|
5
|
+
|
6
|
+
require 'rubygems'
|
7
|
+
require 'irb'
|
8
|
+
require 'well_rested'
|
9
|
+
|
10
|
+
#require File.dirname(__FILE__) + '/../spec/spec_helper'
|
11
|
+
|
12
|
+
# Enable request logging.
|
13
|
+
require 'restclient/components'
|
14
|
+
RestClient.enable Rack::CommonLogger
|
15
|
+
|
16
|
+
# Enable request caching via HTTP rules.
|
17
|
+
#require 'rack/cache'
|
18
|
+
#RestClient.enable Rack::Cache
|
19
|
+
|
20
|
+
include WellRested
|
21
|
+
|
22
|
+
@api = API.new
|
23
|
+
|
24
|
+
puts "\nWelcome to the WellRested command-line application. A default-configure WellRested::API instance is available in @api.\n\n"
|
25
|
+
|
26
|
+
IRB.start(__FILE__)
|
27
|
+
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# HNSearch
|
2
|
+
# API access for news.ycombinator.com
|
3
|
+
module HNSearch
|
4
|
+
class Base < WellRested::Base
|
5
|
+
self.server = 'api.thriftdb.com/api.hnsearch.com'
|
6
|
+
end
|
7
|
+
|
8
|
+
class User < Base
|
9
|
+
self.path = '/users'
|
10
|
+
|
11
|
+
def items(api)
|
12
|
+
Item.search_items_by_username(api, self.username)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class Item < Base
|
17
|
+
self.path = '/items'
|
18
|
+
|
19
|
+
def self.search_items_by_username(api, username)
|
20
|
+
api.get("#{protocol}://#{server}/items/_search", :filter => { :fields => { :username => username } })
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.your_author(api)
|
25
|
+
api.find(User, :id => 'nick_urban')
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module GenericUtils
|
2
|
+
extend self
|
3
|
+
|
4
|
+
# Return a class object given its name.
|
5
|
+
def get_class(name)
|
6
|
+
Kernel.const_get(name)
|
7
|
+
rescue NameError
|
8
|
+
nil
|
9
|
+
end
|
10
|
+
|
11
|
+
# Return whether a class exists for a given name.
|
12
|
+
def class_exists?(name)
|
13
|
+
get_class(name) != nil
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
|
@@ -0,0 +1,43 @@
|
|
1
|
+
|
2
|
+
# This module is used to transform the keys found in resources between Rail's native underscore_case
|
3
|
+
# and the camelCase typically found in Java, etc.
|
4
|
+
|
5
|
+
module KeyTransformer
|
6
|
+
extend self
|
7
|
+
|
8
|
+
# Run through a hash or array of hashes and replace the keys with underscored versions.
|
9
|
+
# NOTE: Hash values may include simple values, other hashes or arrays, but Arrays may only include hashes.
|
10
|
+
# If we had an array of symbols (e.g. for permissions), we'd have to do this slightly differently
|
11
|
+
def underscore_keys(hash_or_array)
|
12
|
+
key_transform = Proc.new { |key| key.to_s.underscore }
|
13
|
+
transform_keys(hash_or_array, key_transform)
|
14
|
+
end
|
15
|
+
|
16
|
+
def camelize_keys(hash_or_array, upper_or_lower = :lower)
|
17
|
+
key_transform = Proc.new { |key| key.to_s.camelize(upper_or_lower) }
|
18
|
+
transform_keys(hash_or_array, key_transform)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Takes a hash or array to process and a key transform (Proc),
|
22
|
+
# which should accept a key and return a transformed key.
|
23
|
+
# Returns a hash or array, depending on which was passed.
|
24
|
+
# NOTE: any objects which respond to 'attributes' (active record, active resource) will be turned into hashes.
|
25
|
+
# NOTE: all hashes are returned with indifferent access
|
26
|
+
def transform_keys(hash_or_array, key_transform)
|
27
|
+
if hash_or_array.is_a? Array
|
28
|
+
hash_or_array.map { |obj| transform_keys(obj, key_transform) }
|
29
|
+
elsif hash_or_array.is_a? Hash
|
30
|
+
new_hash = {}.with_indifferent_access
|
31
|
+
hash_or_array.each do |key, val|
|
32
|
+
new_hash[key_transform.call(key)] = transform_keys(val, key_transform)
|
33
|
+
end
|
34
|
+
new_hash
|
35
|
+
# Note: this case has been disabled because we now convert objects to hashes before passing them into underscore/camelize_keys.
|
36
|
+
# elsif hash_or_array.respond_to? :attributes # if this is an object rather than a hash / array, get the hash from its attributes
|
37
|
+
# transform_keys(hash_or_array.attributes, key_transform)
|
38
|
+
else
|
39
|
+
hash_or_array
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
data/lib/well_rested.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
# require external dependencies
|
4
|
+
require 'active_support/core_ext/hash/indifferent_access'
|
5
|
+
require 'active_support/core_ext/hash/reverse_merge'
|
6
|
+
|
7
|
+
# require internal general-use libs
|
8
|
+
require 'key_transformer'
|
9
|
+
require 'generic_utils'
|
10
|
+
|
11
|
+
# require internal libs
|
12
|
+
require 'well_rested/api'
|
13
|
+
require 'well_rested/base'
|
14
|
+
require 'well_rested/utils'
|
15
|
+
require 'well_rested/json_formatter'
|
16
|
+
require 'well_rested/camel_case_formatter'
|
17
|
+
|
18
|
+
# Make sure 'bases' singularizes to 'base' instead of 'basis'.
|
19
|
+
# Otherwise, we get an error that no class Basis is found in Base.
|
20
|
+
ActiveSupport::Inflector.inflections do |inflect|
|
21
|
+
inflect.irregular 'base', 'bases'
|
22
|
+
end
|
23
|
+
|
24
|
+
module WellRested
|
25
|
+
def logger
|
26
|
+
return Rails.logger if Utils.class_exists? 'Rails'
|
27
|
+
return @logger if @logger
|
28
|
+
|
29
|
+
require 'logger'
|
30
|
+
@logger = Logger.new(STDERR)
|
31
|
+
@logger.datetime_format = "%H:%M:%S"
|
32
|
+
@logger
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|