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.
@@ -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: []