rocket_pants 1.0.0.rc.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/lib/rocket_pants.rb +41 -0
- data/lib/rocket_pants/base.rb +60 -0
- data/lib/rocket_pants/cache_middleware.rb +59 -0
- data/lib/rocket_pants/cacheable.rb +19 -0
- data/lib/rocket_pants/client.rb +135 -0
- data/lib/rocket_pants/controller/caching.rb +135 -0
- data/lib/rocket_pants/controller/error_handling.rb +108 -0
- data/lib/rocket_pants/controller/instrumentation.rb +30 -0
- data/lib/rocket_pants/controller/rescuable.rb +66 -0
- data/lib/rocket_pants/controller/respondable.rb +122 -0
- data/lib/rocket_pants/controller/url_for.rb +11 -0
- data/lib/rocket_pants/controller/versioning.rb +39 -0
- data/lib/rocket_pants/exceptions.rb +112 -0
- data/lib/rocket_pants/locale/en.yml +8 -0
- data/lib/rocket_pants/railtie.rb +44 -0
- data/lib/rocket_pants/routing.rb +26 -0
- data/lib/rocket_pants/rspec_matchers.rb +117 -0
- data/lib/rocket_pants/tasks/rocket_pants.rake +32 -0
- data/lib/rocket_pants/test_helper.rb +94 -0
- metadata +186 -0
@@ -0,0 +1,8 @@
|
|
1
|
+
en:
|
2
|
+
rocket_pants:
|
3
|
+
errors:
|
4
|
+
throttled: "You have gone over your allotted amount of requests and have been throttled."
|
5
|
+
unauthenticated: "This action requires authentication to continue."
|
6
|
+
invalid_version: "This action is not available in the given version of the api."
|
7
|
+
not_implemented: "The feature you requested has not yet been implemented and hence is currently unavailable."
|
8
|
+
not_found: "The requested resource could not be found."
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module RocketPants
|
2
|
+
class Railtie < Rails::Railtie
|
3
|
+
|
4
|
+
config.rocket_pants = ActiveSupport::OrderedOptions.new
|
5
|
+
config.rocket_pants.use_caching = nil
|
6
|
+
|
7
|
+
config.i18n.railties_load_path << File.expand_path('../locale/en.yml', __FILE__)
|
8
|
+
|
9
|
+
initializer "rocket_pants.logger" do
|
10
|
+
ActiveSupport.on_load(:rocket_pants) { self.logger ||= Rails.logger }
|
11
|
+
end
|
12
|
+
|
13
|
+
initializer "rocket_pants.configuration" do |app|
|
14
|
+
rp_config = app.config.rocket_pants
|
15
|
+
rp_config.use_caching = Rails.env.production? if rp_config.use_caching.nil?
|
16
|
+
RocketPants.caching_enabled = rp_config.use_caching
|
17
|
+
# Set the rocket pants cache if present.
|
18
|
+
RocketPants.cache = rp_config.cache if rp_config.cache
|
19
|
+
end
|
20
|
+
|
21
|
+
initializer "rocket_pants.url_helpers" do |app|
|
22
|
+
ActiveSupport.on_load(:rocket_pants) do
|
23
|
+
include app.routes.url_helpers
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
initializer "rocket_pants.setup_testing" do |app|
|
28
|
+
ActiveSupport.on_load(:rocket_pants) do
|
29
|
+
include ActionController::Testing if Rails.env.test?
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
initializer "rocket_pants.setup_caching" do |app|
|
34
|
+
if RocketPants.caching_enabled?
|
35
|
+
app.middleware.insert 'Rack::Runtime', RocketPants::CacheMiddleware
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
rake_tasks do
|
40
|
+
load "rocket_pants/tasks/rocket_pants.rake"
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module RocketPants
|
2
|
+
module Routing
|
3
|
+
|
4
|
+
# Scopes a set of given api routes, allowing for option versions.
|
5
|
+
# @param [Hash] options options to pass through to the route e.g. `:module`.
|
6
|
+
# @option options [Array<Integer>, Integer] :versions the versions to support
|
7
|
+
# @option options [Array<Integer>, Integer] :version the single version to support
|
8
|
+
# @raise [ArgumentError] raised when the version isn't provided.
|
9
|
+
def rocket_pants(options = {}, &blk)
|
10
|
+
versions = (Array(options.delete(:versions)) + Array(options.delete(:version))).flatten.map(&:to_s)
|
11
|
+
versions.each do |version|
|
12
|
+
raise ArgumentError, "Got invalid version: '#{version}'" unless version =~ /\A\d+\Z/
|
13
|
+
end
|
14
|
+
versions_regexp = /(#{versions.uniq.join("|")})/
|
15
|
+
raise ArgumentError, 'please provide atleast one version' if versions.empty?
|
16
|
+
options = options.deep_merge({
|
17
|
+
:constraints => {:version => versions_regexp},
|
18
|
+
:path => ':version',
|
19
|
+
:defaults => {:format => 'json'}
|
20
|
+
})
|
21
|
+
scope options, &blk
|
22
|
+
end
|
23
|
+
alias api rocket_pants
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
module RocketPants
|
2
|
+
module RSpecMatchers
|
3
|
+
extend RSpec::Matchers::DSL
|
4
|
+
|
5
|
+
def self.normalised_error(e)
|
6
|
+
if e.is_a?(String) || e.is_a?(Symbol)
|
7
|
+
Errors[e]
|
8
|
+
else
|
9
|
+
e
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.normalise_urls(object)
|
14
|
+
if object.is_a?(Array)
|
15
|
+
object.each { |o| o['url'] = nil }
|
16
|
+
elsif object.is_a?(Hash) || object.is_a?(APISmith::Smash)
|
17
|
+
object['url'] = nil
|
18
|
+
end
|
19
|
+
object
|
20
|
+
end
|
21
|
+
|
22
|
+
# Converts it to JSON and back again.
|
23
|
+
def self.normalise_as_json(object, options = {})
|
24
|
+
options = options.reverse_merge(:compact => true) if object.is_a?(Array)
|
25
|
+
object = RocketPants::Respondable.normalise_object(object, options)
|
26
|
+
j = ActiveSupport::JSON
|
27
|
+
j.decode(j.encode({'response' => object}))['response']
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.normalise_response(response, options = {})
|
31
|
+
normalise_urls normalise_as_json response, options
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.valid_for?(response, allowed, disallowed)
|
35
|
+
body = response.decoded_body
|
36
|
+
return false if body.blank?
|
37
|
+
body = body.to_hash
|
38
|
+
return false if body.has_key?("error")
|
39
|
+
allowed.all? { |f| body.has_key?(f) } && !disallowed.any? { |f| body.has_key?(f) }
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.differ
|
43
|
+
@_differ ||= RSpec::Expectations::Differ.new
|
44
|
+
end
|
45
|
+
|
46
|
+
matcher :_be_api_error do |error_type|
|
47
|
+
|
48
|
+
match do |response|
|
49
|
+
@error = response.decoded_body.error
|
50
|
+
@error.present? && (error_type.blank? || RSpecMatchers.normalised_error(@error) == error_type)
|
51
|
+
end
|
52
|
+
|
53
|
+
failure_message_for_should do |response|
|
54
|
+
if @error.blank?
|
55
|
+
"expected #{error_type || "any error"} on response, got no error"
|
56
|
+
else error_type.present? && (normalised = RSpecMatchers.normalised_error(@error)) != error_type
|
57
|
+
"expected #{error_type || "any error"} but got #{normalised} instead"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
failure_message_for_should_not do |response|
|
62
|
+
"expected response to not have an #{error_type || "error"}, but it did (#{@error})"
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
|
67
|
+
matcher :be_singular_resource do
|
68
|
+
|
69
|
+
match do |response|
|
70
|
+
RSpecMatchers.valid_for? response, %w(), %w(count pagination)
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
|
75
|
+
matcher :be_collection_resource do
|
76
|
+
|
77
|
+
match do |response|
|
78
|
+
RSpecMatchers.valid_for? response, %w(count), %w(pagination)
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
|
83
|
+
matcher :be_paginated_resource do
|
84
|
+
|
85
|
+
match do |response|
|
86
|
+
RSpecMatchers.valid_for? response, %w(count pagination), %w()
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
90
|
+
|
91
|
+
matcher :have_exposed do |*args|
|
92
|
+
normalised_response = RSpecMatchers.normalise_response(*args)
|
93
|
+
|
94
|
+
match do |response|
|
95
|
+
@decoded = RSpecMatchers.normalise_urls(response.parsed_body["response"])
|
96
|
+
normalised_response == @decoded
|
97
|
+
end
|
98
|
+
|
99
|
+
failure_message_for_should do |response|
|
100
|
+
message = "expected api to have exposed #{normalised_response.inspect}, got #{@decoded.inspect} instead."
|
101
|
+
message << "\n\nDiff: #{RSpecMatchers.differ.diff_as_object(@decoded, normalised_response)}"
|
102
|
+
message
|
103
|
+
end
|
104
|
+
|
105
|
+
failure_message_for_should_not do |response|
|
106
|
+
"expected api to not have exposed #{normalised_response.inspect}"
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
110
|
+
|
111
|
+
def be_api_error(error = nil)
|
112
|
+
_be_api_error error
|
113
|
+
end
|
114
|
+
|
115
|
+
|
116
|
+
end
|
117
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
namespace :rocket_pants do
|
2
|
+
|
3
|
+
desc "Generates a pretty listing of errors registered in the application"
|
4
|
+
task :errors => :environment do
|
5
|
+
errors = RocketPants::Errors.all
|
6
|
+
output = [["Error Name", "HTTP Status", "Class Name"]]
|
7
|
+
errors.keys.map(&:to_s).sort.each do |key|
|
8
|
+
klass = errors[key.to_sym]
|
9
|
+
http_status = klass.respond_to?(:http_status) && klass.http_status
|
10
|
+
status_code = Rack::Utils.status_code(http_status)
|
11
|
+
status_name = http_status.is_a?(Integer) ? Rack::Utils::HTTP_STATUS_CODES[http_status] : http_status.to_s.titleize
|
12
|
+
output << [key, "#{status_code} #{status_name}", klass.name]
|
13
|
+
end
|
14
|
+
total_width = 8
|
15
|
+
0.upto(2) do |column|
|
16
|
+
fields = output.map { |i| i[column] }
|
17
|
+
length = fields.map(&:length).max + 2
|
18
|
+
total_width += length
|
19
|
+
fields.each_with_index do |item, idx|
|
20
|
+
output[idx][column] = item.ljust(length)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
puts("+#{"-" * total_width}+")
|
24
|
+
puts "| #{output[0] * " | "} |"
|
25
|
+
puts("+#{"-" * total_width}+")
|
26
|
+
output[1..-1].each do |row|
|
27
|
+
puts "| #{row * " | "} |"
|
28
|
+
end
|
29
|
+
puts("+#{"-" * total_width}+")
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
require 'hashie/mash'
|
2
|
+
|
3
|
+
module RocketPants
|
4
|
+
module TestHelper
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
included do
|
8
|
+
# Extend the response on first include.
|
9
|
+
class_attribute :_default_version
|
10
|
+
unless ActionController::TestResponse < ResponseHelper
|
11
|
+
ActionController::TestResponse.send :include, ResponseHelper
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
module ResponseHelper
|
16
|
+
|
17
|
+
def parsed_body
|
18
|
+
@_parsed_body ||= begin
|
19
|
+
ActiveSupport::JSON.decode(body)
|
20
|
+
rescue StandardError => e
|
21
|
+
nil
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def decoded_body
|
26
|
+
@_decoded_body ||= begin
|
27
|
+
decoded = parsed_body
|
28
|
+
if decoded.is_a?(Hash)
|
29
|
+
Hashie::Mash.new(decoded)
|
30
|
+
else
|
31
|
+
decoded
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
module ClassMethods
|
39
|
+
|
40
|
+
def default_version(value)
|
41
|
+
self._default_version = value
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
def decoded_response
|
47
|
+
value = response.decoded_body.try(:response)
|
48
|
+
end
|
49
|
+
|
50
|
+
def decoded_pagination
|
51
|
+
response.decoded_body.try :pagination
|
52
|
+
end
|
53
|
+
|
54
|
+
def decoded_count
|
55
|
+
response.decoded_body.try :count
|
56
|
+
end
|
57
|
+
|
58
|
+
def decoded_error_class
|
59
|
+
error = response.decoded_body.try :error
|
60
|
+
error.presence && RocketPants::Errors[error]
|
61
|
+
end
|
62
|
+
|
63
|
+
# RSpec matcher foo.
|
64
|
+
|
65
|
+
def have_decoded_response(value)
|
66
|
+
response = normalise_value(value)
|
67
|
+
end
|
68
|
+
|
69
|
+
protected
|
70
|
+
|
71
|
+
# Like process, but automatically adds the api version.
|
72
|
+
def process(action, parameters = nil, session = nil, flash = nil, http_method = 'GET')
|
73
|
+
parameters ||= {}
|
74
|
+
if _default_version.present? && parameters[:version].blank? && parameters['version'].blank?
|
75
|
+
parameters[:version] = _default_version
|
76
|
+
end
|
77
|
+
super
|
78
|
+
end
|
79
|
+
|
80
|
+
def normalise_value(value)
|
81
|
+
if value.is_a?(Hash)
|
82
|
+
value.inject({}) do |acc, (k, v)|
|
83
|
+
acc[k.to_s] = normalise_value(v)
|
84
|
+
acc
|
85
|
+
end
|
86
|
+
elsif value.is_a?(Array)
|
87
|
+
value.map { |v| normalise_value v }
|
88
|
+
else
|
89
|
+
value
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
94
|
+
end
|
metadata
ADDED
@@ -0,0 +1,186 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rocket_pants
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0.rc.1
|
5
|
+
prerelease: 6
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Darcy Laycock
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-03-06 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: actionpack
|
16
|
+
requirement: &70156424600020 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '3.0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *70156424600020
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: railties
|
27
|
+
requirement: &70156424599460 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ~>
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '3.0'
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *70156424599460
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: will_paginate
|
38
|
+
requirement: &70156424598940 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ~>
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '3.0'
|
44
|
+
type: :runtime
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *70156424598940
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: hashie
|
49
|
+
requirement: &70156424598420 !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ~>
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.0'
|
55
|
+
type: :runtime
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: *70156424598420
|
58
|
+
- !ruby/object:Gem::Dependency
|
59
|
+
name: api_smith
|
60
|
+
requirement: &70156424598020 !ruby/object:Gem::Requirement
|
61
|
+
none: false
|
62
|
+
requirements:
|
63
|
+
- - ! '>='
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
version: '0'
|
66
|
+
type: :runtime
|
67
|
+
prerelease: false
|
68
|
+
version_requirements: *70156424598020
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: will_paginate
|
71
|
+
requirement: &70156424754640 !ruby/object:Gem::Requirement
|
72
|
+
none: false
|
73
|
+
requirements:
|
74
|
+
- - ! '>='
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '0'
|
77
|
+
type: :runtime
|
78
|
+
prerelease: false
|
79
|
+
version_requirements: *70156424754640
|
80
|
+
- !ruby/object:Gem::Dependency
|
81
|
+
name: moneta
|
82
|
+
requirement: &70156424754180 !ruby/object:Gem::Requirement
|
83
|
+
none: false
|
84
|
+
requirements:
|
85
|
+
- - ! '>='
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
version: '0'
|
88
|
+
type: :runtime
|
89
|
+
prerelease: false
|
90
|
+
version_requirements: *70156424754180
|
91
|
+
- !ruby/object:Gem::Dependency
|
92
|
+
name: rspec
|
93
|
+
requirement: &70156424753680 !ruby/object:Gem::Requirement
|
94
|
+
none: false
|
95
|
+
requirements:
|
96
|
+
- - ~>
|
97
|
+
- !ruby/object:Gem::Version
|
98
|
+
version: 2.4.0
|
99
|
+
type: :development
|
100
|
+
prerelease: false
|
101
|
+
version_requirements: *70156424753680
|
102
|
+
- !ruby/object:Gem::Dependency
|
103
|
+
name: rspec-rails
|
104
|
+
requirement: &70156424753060 !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ~>
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: 2.4.0
|
110
|
+
type: :development
|
111
|
+
prerelease: false
|
112
|
+
version_requirements: *70156424753060
|
113
|
+
- !ruby/object:Gem::Dependency
|
114
|
+
name: rr
|
115
|
+
requirement: &70156424752600 !ruby/object:Gem::Requirement
|
116
|
+
none: false
|
117
|
+
requirements:
|
118
|
+
- - ~>
|
119
|
+
- !ruby/object:Gem::Version
|
120
|
+
version: 1.0.0
|
121
|
+
type: :development
|
122
|
+
prerelease: false
|
123
|
+
version_requirements: *70156424752600
|
124
|
+
- !ruby/object:Gem::Dependency
|
125
|
+
name: webmock
|
126
|
+
requirement: &70156424751900 !ruby/object:Gem::Requirement
|
127
|
+
none: false
|
128
|
+
requirements:
|
129
|
+
- - ! '>='
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: *70156424751900
|
135
|
+
description: Rocket Pants adds JSON API love to Rails and ActionController, making
|
136
|
+
it simpler to build resource-oriented controllers.
|
137
|
+
email:
|
138
|
+
- sutto@sutto.net
|
139
|
+
executables: []
|
140
|
+
extensions: []
|
141
|
+
extra_rdoc_files: []
|
142
|
+
files:
|
143
|
+
- lib/rocket_pants/base.rb
|
144
|
+
- lib/rocket_pants/cache_middleware.rb
|
145
|
+
- lib/rocket_pants/cacheable.rb
|
146
|
+
- lib/rocket_pants/client.rb
|
147
|
+
- lib/rocket_pants/controller/caching.rb
|
148
|
+
- lib/rocket_pants/controller/error_handling.rb
|
149
|
+
- lib/rocket_pants/controller/instrumentation.rb
|
150
|
+
- lib/rocket_pants/controller/rescuable.rb
|
151
|
+
- lib/rocket_pants/controller/respondable.rb
|
152
|
+
- lib/rocket_pants/controller/url_for.rb
|
153
|
+
- lib/rocket_pants/controller/versioning.rb
|
154
|
+
- lib/rocket_pants/exceptions.rb
|
155
|
+
- lib/rocket_pants/locale/en.yml
|
156
|
+
- lib/rocket_pants/railtie.rb
|
157
|
+
- lib/rocket_pants/routing.rb
|
158
|
+
- lib/rocket_pants/rspec_matchers.rb
|
159
|
+
- lib/rocket_pants/tasks/rocket_pants.rake
|
160
|
+
- lib/rocket_pants/test_helper.rb
|
161
|
+
- lib/rocket_pants.rb
|
162
|
+
homepage: http://github.com/filtersquad
|
163
|
+
licenses: []
|
164
|
+
post_install_message:
|
165
|
+
rdoc_options: []
|
166
|
+
require_paths:
|
167
|
+
- lib
|
168
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
169
|
+
none: false
|
170
|
+
requirements:
|
171
|
+
- - ! '>='
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: '0'
|
174
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
175
|
+
none: false
|
176
|
+
requirements:
|
177
|
+
- - ! '>='
|
178
|
+
- !ruby/object:Gem::Version
|
179
|
+
version: 1.3.6
|
180
|
+
requirements: []
|
181
|
+
rubyforge_project:
|
182
|
+
rubygems_version: 1.8.10
|
183
|
+
signing_key:
|
184
|
+
specification_version: 3
|
185
|
+
summary: JSON API love for Rails and ActionController
|
186
|
+
test_files: []
|