rocket_pants 1.0.0.rc.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: []