Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Remove rocket pants vendored in.

  • Loading branch information...
commit b832cc0f9dfa8f4bb14ad92463da22c05a36b825 1 parent 29f1912
@Sutto authored
Showing with 2 additions and 2,554 deletions.
  1. +1 −0  .gitignore
  2. +1 −1  Gemfile
  3. +0 −3  vendor/rocket_pants/.gitignore
  4. +0 −2  vendor/rocket_pants/.rspec
  5. +0 −1  vendor/rocket_pants/.rvmrc
  6. +0 −6 vendor/rocket_pants/Gemfile
  7. +0 −108 vendor/rocket_pants/Gemfile.lock
  8. +0 −15 vendor/rocket_pants/README.md
  9. +0 −23 vendor/rocket_pants/Rakefile
  10. +0 −41 vendor/rocket_pants/lib/rocket_pants.rb
  11. +0 −60 vendor/rocket_pants/lib/rocket_pants/base.rb
  12. +0 −54 vendor/rocket_pants/lib/rocket_pants/cache_middleware.rb
  13. +0 −23 vendor/rocket_pants/lib/rocket_pants/cacheable.rb
  14. +0 −135 vendor/rocket_pants/lib/rocket_pants/client.rb
  15. +0 −136 vendor/rocket_pants/lib/rocket_pants/controller/caching.rb
  16. +0 −112 vendor/rocket_pants/lib/rocket_pants/controller/error_handling.rb
  17. +0 −30 vendor/rocket_pants/lib/rocket_pants/controller/instrumentation.rb
  18. +0 −70 vendor/rocket_pants/lib/rocket_pants/controller/rescuable.rb
  19. +0 −118 vendor/rocket_pants/lib/rocket_pants/controller/respondable.rb
  20. +0 −11 vendor/rocket_pants/lib/rocket_pants/controller/url_for.rb
  21. +0 −39 vendor/rocket_pants/lib/rocket_pants/controller/versioning.rb
  22. +0 −112 vendor/rocket_pants/lib/rocket_pants/exceptions.rb
  23. +0 −8 vendor/rocket_pants/lib/rocket_pants/locale/en.yml
  24. +0 −44 vendor/rocket_pants/lib/rocket_pants/railtie.rb
  25. +0 −26 vendor/rocket_pants/lib/rocket_pants/routing.rb
  26. +0 −117 vendor/rocket_pants/lib/rocket_pants/rspec_matchers.rb
  27. +0 −32 vendor/rocket_pants/lib/rocket_pants/tasks/rocket_pants.rake
  28. +0 −98 vendor/rocket_pants/lib/rocket_pants/test_helper.rb
  29. +0 −30 vendor/rocket_pants/rocket_pants.gemspec
  30. +0 −9 vendor/rocket_pants/spec/fixtures/collection.json
  31. +0 −17 vendor/rocket_pants/spec/fixtures/paginated.json
  32. +0 −4 vendor/rocket_pants/spec/fixtures/simple_error.json
  33. +0 −5 vendor/rocket_pants/spec/fixtures/simple_object.json
  34. +0 −3  vendor/rocket_pants/spec/fixtures/simple_string.json
  35. +0 −4 vendor/rocket_pants/spec/fixtures/unknown_error.json
  36. +0 −101 vendor/rocket_pants/spec/rocket_pants/caching_spec.rb
  37. +0 −194 vendor/rocket_pants/spec/rocket_pants/client_spec.rb
  38. +0 −345 vendor/rocket_pants/spec/rocket_pants/controller_spec.rb
  39. +0 −84 vendor/rocket_pants/spec/rocket_pants/error_spec.rb
  40. +0 −94 vendor/rocket_pants/spec/rocket_pants/errors_spec.rb
  41. +0 −79 vendor/rocket_pants/spec/rocket_pants/routing_spec.rb
  42. +0 −18 vendor/rocket_pants/spec/spec_helper.rb
  43. +0 −48 vendor/rocket_pants/spec/support/controller.rb
  44. +0 −42 vendor/rocket_pants/spec/support/controller_helpers.rb
  45. +0 −12 vendor/rocket_pants/spec/support/i18n_spec_helper.rb
  46. +0 −40 vendor/rocket_pants/spec/support/webmock.rb
View
1  .gitignore
@@ -3,3 +3,4 @@ db/*.sqlite3
log/*.log
tmp/
.sass-cache/
+coverage/
View
2  Gemfile
@@ -5,7 +5,7 @@ gem 'pg'
gem 'nokogiri'
gem 'api_smith'
-gem 'rocket_pants', :path => 'vendor/rocket_pants'
+gem 'rocket_pants', '~> 1.0'
gem 'gcoder'
gem 'awesome_print', :require => nil
View
3  vendor/rocket_pants/.gitignore
@@ -1,3 +0,0 @@
-doc/
-.yardoc/
-coverage/
View
2  vendor/rocket_pants/.rspec
@@ -1,2 +0,0 @@
---colour
---format=documentation
View
1  vendor/rocket_pants/.rvmrc
@@ -1 +0,0 @@
-rvm use ree@rocket_pants_lib --create
View
6 vendor/rocket_pants/Gemfile
@@ -1,6 +0,0 @@
-source :rubygems
-
-gem 'rcov', :require => nil
-gem 'ci_reporter', '~> 1.6.3', :require => nil
-
-gemspec
View
108 vendor/rocket_pants/Gemfile.lock
@@ -1,108 +0,0 @@
-PATH
- remote: .
- specs:
- rocket_pants (0.1.0)
- actionpack (~> 3.0)
- api_smith
- hashie (~> 1.0)
- moneta
- railties (~> 3.0)
- will_paginate
- will_paginate (~> 3.0)
-
-GEM
- remote: http://rubygems.org/
- specs:
- actionpack (3.1.0)
- activemodel (= 3.1.0)
- activesupport (= 3.1.0)
- builder (~> 3.0.0)
- erubis (~> 2.7.0)
- i18n (~> 0.6)
- rack (~> 1.3.2)
- rack-cache (~> 1.0.3)
- rack-mount (~> 0.8.2)
- rack-test (~> 0.6.1)
- sprockets (~> 2.0.0)
- activemodel (3.1.0)
- activesupport (= 3.1.0)
- bcrypt-ruby (~> 3.0.0)
- builder (~> 3.0.0)
- i18n (~> 0.6)
- activesupport (3.1.0)
- multi_json (~> 1.0)
- addressable (2.2.6)
- api_smith (1.0.0)
- hashie (~> 1.0)
- httparty
- bcrypt-ruby (3.0.1)
- builder (3.0.0)
- ci_reporter (1.6.5)
- builder (>= 2.1.2)
- crack (0.3.1)
- diff-lcs (1.1.3)
- erubis (2.7.0)
- hashie (1.1.0)
- hike (1.2.1)
- httparty (0.8.0)
- multi_json
- multi_xml
- i18n (0.6.0)
- moneta (0.6.0)
- multi_json (1.0.3)
- multi_xml (0.4.0)
- rack (1.3.3)
- rack-cache (1.0.3)
- rack (>= 0.4)
- rack-mount (0.8.3)
- rack (>= 1.0.0)
- rack-ssl (1.3.2)
- rack
- rack-test (0.6.1)
- rack (>= 1.0)
- railties (3.1.0)
- actionpack (= 3.1.0)
- activesupport (= 3.1.0)
- rack-ssl (~> 1.3.2)
- rake (>= 0.8.7)
- rdoc (~> 3.4)
- thor (~> 0.14.6)
- rake (0.9.2)
- rcov (0.9.10)
- rdoc (3.9.4)
- rr (1.0.4)
- rspec (2.4.0)
- rspec-core (~> 2.4.0)
- rspec-expectations (~> 2.4.0)
- rspec-mocks (~> 2.4.0)
- rspec-core (2.4.0)
- rspec-expectations (2.4.0)
- diff-lcs (~> 1.1.2)
- rspec-mocks (2.4.0)
- rspec-rails (2.4.1)
- actionpack (~> 3.0)
- activesupport (~> 3.0)
- railties (~> 3.0)
- rspec (~> 2.4.0)
- sprockets (2.0.0)
- hike (~> 1.2)
- rack (~> 1.0)
- tilt (~> 1.1, != 1.3.0)
- thor (0.14.6)
- tilt (1.3.3)
- webmock (1.7.6)
- addressable (~> 2.2, > 2.2.5)
- crack (>= 0.1.7)
- will_paginate (3.0.1)
-
-PLATFORMS
- ruby
-
-DEPENDENCIES
- ci_reporter (~> 1.6.3)
- rcov
- rocket_pants!
- rr (~> 1.0.0)
- rspec (~> 2.4.0)
- rspec-rails (~> 2.4.0)
- webmock
View
15 vendor/rocket_pants/README.md
@@ -1,15 +0,0 @@
-# Rocket Pants!
-
-This is where the readme shall go. Seriously.
-
-## Using with Rspec
-
-Simply include RocketPants::TestHelper and RocketPants::RSpecMatchers, and you'll get access to:
-
-* `be_singular_resource`
-* `be_collection_resource`
-* `be_paginated_response`
-* `be_api_error(type = any)`
-* `have_exposed(data)`
-
-and helpers for checking data.
View
23 vendor/rocket_pants/Rakefile
@@ -1,23 +0,0 @@
-require 'rubygems'
-require 'rake'
-require 'rspec/core'
-require 'rspec/core/rake_task'
-
-task :default => :spec
-
-begin
- require 'ci/reporter/rake/rspec'
-rescue LoadError
-end
-
-desc "Run all specs in spec directory (excluding plugin specs)"
-RSpec::Core::RakeTask.new(:spec)
-
-namespace :spec do
- desc "Run all specs with rcov"
- RSpec::Core::RakeTask.new(:rcov) do |t|
- t.rcov = true
- t.pattern = "./spec/**/*_spec.rb"
- t.rcov_opts = '--exclude spec/,/gems/,/Library/,/usr/,lib/tasks,.bundle,config,/lib/rspec/,/lib/rspec-'
- end
-end
View
41 vendor/rocket_pants/lib/rocket_pants.rb
@@ -1,41 +0,0 @@
-require 'active_support/all'
-require 'action_dispatch'
-require 'action_dispatch/routing'
-require 'action_controller'
-
-require 'moneta'
-require 'moneta/memory'
-
-module RocketPants
- require 'rocket_pants/exceptions'
-
- # Set up the routing in advance.
- require 'rocket_pants/routing'
- ActionDispatch::Routing::Mapper.send :include, RocketPants::Routing
-
- require 'rocket_pants/railtie' if defined?(Rails::Railtie)
-
- # Extra parts of RocketPants.
- autoload :Base, 'rocket_pants/base'
- autoload :Client, 'rocket_pants/client'
- autoload :Cacheable, 'rocket_pants/cacheable'
- autoload :CacheMiddleware, 'rocket_pants/cache_middleware'
-
- # Helpers for various testing frameworks.
- autoload :TestHelper, 'rocket_pants/test_helper'
- autoload :RSpecMatchers, 'rocket_pants/rspec_matchers'
-
- mattr_accessor :caching_enabled
- self.caching_enabled = false
-
- mattr_writer :cache
-
- class << self
- alias caching_enabled? caching_enabled
-
- def cache
- @@cache ||= Moneta::Memory.new
- end
- end
-
-end
View
60 vendor/rocket_pants/lib/rocket_pants/base.rb
@@ -1,60 +0,0 @@
-require 'rocket_pants/exceptions'
-
-module RocketPants
-
- autoload :Caching, 'rocket_pants/controller/caching'
- autoload :ErrorHandling, 'rocket_pants/controller/error_handling'
- autoload :Instrumentation, 'rocket_pants/controller/instrumentation'
- autoload :Rescuable, 'rocket_pants/controller/rescuable'
- autoload :Respondable, 'rocket_pants/controller/respondable'
- autoload :Versioning, 'rocket_pants/controller/versioning'
- autoload :UrlFor, 'rocket_pants/controller/url_for'
-
- class Base < ActionController::Metal
-
- abstract!
-
- MODULES = [
- ActionController::HideActions,
- ActionController::UrlFor,
- ActionController::Redirecting,
- ActionController::ConditionalGet,
- ActionController::RackDelegation,
- ActionController::RecordIdentifier,
- UrlFor,
- Respondable,
- Versioning,
- Instrumentation,
- Caching,
- ActionController::MimeResponds,
- # Include earliest as possible in the request.
- AbstractController::Callbacks,
- ActionController::Rescue,
- ErrorHandling,
- Rescuable
- ]
-
- # If possible, include the Rails controller methods in Airbrake to make it useful.
- begin
- require 'airbrake'
- require 'airbrake/rails/controller_methods'
- MODULES << Airbrake::Rails::ControllerMethods
- rescue LoadError => e
- end
-
- MODULES.each do |mixin|
- include mixin
- end
-
- respond_to :json
-
- # Bug fix for rails - include compatibility.
- config_accessor :protected_instance_variables
- self.protected_instance_variables = %w(@assigns @performed_redirect @performed_render
- @variables_added @request_origin @url @parent_controller @action_name
- @before_filter_chain_aborted @_headers @_params @_response)
-
- ActiveSupport.run_load_hooks(:rocket_pants, self)
-
- end
-end
View
54 vendor/rocket_pants/lib/rocket_pants/cache_middleware.rb
@@ -1,54 +0,0 @@
-module RocketPants
- class CacheMiddleware
-
- NOT_MODIFIED = [304, {}, []]
-
- def initialize(app)
- @app = app
- end
-
- def call(env)
- dup._call env
- end
-
- def _call(env)
- @env = env
- if has_valid_etag?
- debug "Cache key is valid, returning not modified response."
- NOT_MODIFIED.dup
- else
- @app.call env
- end
- end
-
- private
-
- def has_valid_etag?
- return false if (etags = request_etags).blank?
- debug ""
- etags.any? do |etag|
- cache_key, value = extract_cache_key_and_value etag
- debug "Checking cache key #{cache_key} matches the value #{value}"
- fresh? cache_key, value
- end
- end
-
- def extract_cache_key_and_value(etag)
- etag.to_s.split(":", 2)
- end
-
- def fresh?(key, value)
- RocketPants.cache[key.to_s] == value
- end
-
- def request_etags
- stored = @env['HTTP_IF_NONE_MATCH']
- stored.present? && stored.to_s.scan(/"([^"]+)"/)
- end
-
- def debug(message)
- Rails.logger.debug message if defined?(Rails.logger)
- end
-
- end
-end
View
23 vendor/rocket_pants/lib/rocket_pants/cacheable.rb
@@ -1,23 +0,0 @@
-module RocketPants
- module Cacheable
- extend ActiveSupport::Concern
-
- included do
- after_save :record_cache! if respond_to?(:after_save)
- after_destroy :remove_cache! if respond_to?(:after_destroy)
- end
-
- module InstanceMethods
-
- def record_cache!
- RocketPants::Caching.record self
- end
-
- def remove_cache!
- RocketPants::Caching.remove self
- end
-
- end
-
- end
-end
View
135 vendor/rocket_pants/lib/rocket_pants/client.rb
@@ -1,135 +0,0 @@
-require 'active_support/core_ext/class/attribute'
-require 'active_support/core_ext/object/blank'
-require 'active_support/core_ext/string/inflections'
-
-require 'rocket_pants/exceptions'
-
-require 'api_smith'
-require 'will_paginate/collection'
-
-module RocketPants
- # Implements a generalised base for building clients on top of
- # the rocket pants controller. This automatically unpacks the API
- # into the correct response types, handles errors (using the same error registry
- # as the server) and in general makes it simpler to implement api clients.
- #
- # @example A versioned api client
- # class MyApiClient < RocketPants::Client
- #
- # class Profile < APISmith::Smash
- # property :name
- # property :email
- # end
- #
- # version 1
- # base_uri "http://api.example.com/"
- #
- # def profile
- # get 'profile', :transformer => Profile
- # end
- # end
- #
- class Client < APISmith::Base
-
- class_attribute :_version, :_actual_endpoint
-
- class << self
-
- # @overload version
- # @return [Integer] the current API version number for this client
- # @overload version(number)
- # Sets a variable noting what version of the api the client uses and updates
- # the endpoint to prefix it with the given version number.
- # @param [Integer] number the version of the api to process.
- def version(number = nil)
- if number.nil?
- _version
- else
- self._version = number
- endpoint _actual_endpoint
- number
- end
- end
-
- alias _original_endpoint endpoint
-
- # Sets the endpoint url, taking into account the version number as a
- # prefix if present.
- def endpoint(path)
- self._actual_endpoint = path
- _original_endpoint File.join(*[_version, path].compact.map(&:to_s))
- end
-
- end
-
- # Initializes a new client, optionally setting up the host for this client.
- # @param [Hash] options general client options
- # @option options [String] :api_host the overriden base_uri host for this client instance.
- def initialize(options = {})
- super
- # Setup the base uri if passed in to the client.
- if options[:api_host].present?
- add_request_options! :base_uri => HTTParty.normalize_base_uri(options[:api_host])
- end
- end
-
- private
-
- # Give a response hash from the api, will transform it into
- # the correct data type.
- # @param [Hash] response the incoming response
- # @param [hash] options any options to pass through for transformation
- # @return [Object] the transformed response.
- def transform_response(response, options = {})
- # Now unpack the response into the data types.
- inner = response.delete("response")
- objects = unpack inner, options
- # Unpack pagination as a special case.
- if response.has_key?("pagination")
- paginated_response objects, response
- else
- objects
- end
- end
-
- # Returns an API response wrapped in a will_paginate collection
- # using the pagination key in the response container to set up
- # the current number of total entries and page details.
- # @param [Array<Object>] objects the actual response contents
- # @param [Hash{String => Object}] The response container
- # @option container [Hash] "pagination" the pagination data to use.
- def paginated_response(objects, container)
- pagination = container.delete("pagination")
- WillPaginate::Collection.create(pagination["current"], pagination["per_page"]) do |collection|
- collection.replace objects
- collection.total_entries = pagination["count"]
- end
- end
-
- # Finds and uses the transformer for a given incoming object to
- # unpack it into a useful data type.
- # @param [Hash, Array<Hash>] object the response object to unpack
- # @param [Hash] options the unpacking options
- # @option options [Hash] :transformer,:as the transformer to use
- # @return The transformed data or the data itself when no transformer is specified.
- def unpack(object, options = {})
- transformer = options[:transformer] || options[:as]
- transformer ? transformer.call(object) : object
- end
-
- # Processes a given response to check for the presence of an error,
- # either by the response not being a hash (e.g. it is returning HTML instead
- # JSON or HTTParty couldn't parse the response).
- # @param [Hash, Object] response the response to check errors on
- # @raise [RocketPants::Error] a generic error when the type is wrong, or a registered error subclass otherwise.
- def check_response_errors(response)
- if !response.is_a?(Hash)
- raise RocketPants::Error, "The response from the server was not in a supported format."
- elsif response.has_key?("error")
- klass = RocketPants::Errors[response["error"]] || RocketPants::Error
- raise klass.new(response["error_description"])
- end
- end
-
- end
-end
View
136 vendor/rocket_pants/lib/rocket_pants/controller/caching.rb
@@ -1,136 +0,0 @@
-require 'set'
-require 'digest/md5'
-
-module RocketPants
- # RocketPants::Caching adds unobtrusive support for automatic HTTP-level caching
- # to controllers built on Rocket Pants. It will automatically.
- module Caching
- extend ActiveSupport::Concern
-
- included do
- class_attribute :cached_actions, :caching_timeout, :caching_options
- self.caching_timeout = 5.minutes
- self.cached_actions = Set.new
- self.caching_options = {:public => true}
- end
-
- class << self
-
- # Removes the cache record for a given object, making sure
- # It isn't contained in the Rocket Pants etag cache thus
- # ignoring it in incoming responses.
- # @param [Object] object what to remove from the cache
- def remove(object)
- RocketPants.cache.delete cache_key_for(object)
- end
-
- # Takes a given object and sets the stored etag in the Rocket Pants
- # etag key to have the correct value.
- #
- # Doing this means that on subsequent requests with caching enabled,
- # the request will be automatically recorded by the middleware and not
- # actually served.
- #
- # Please note that this expects the object has a cache_key method
- # defined that will return a string, useable for caching. If not,
- # it will use inspect which is a VERY VERY bad idea (and will likely
- # be removed in the near future).
- #
- # @param [#cache_key] object what to record in the cache
- def record(object, cache_key = cache_key_for(object))
- default_etag = object.respond_to?(:cache_key) ? object.cache_key : object.inspect
- generated_etag = Digest::MD5.hexdigest(default_etag)
- RocketPants.cache[cache_key] = generated_etag
- end
-
- # Given an object, returns the etag value to be used in
- # the response / etag header (prior to any more processing).
- # If the object isn't in the cache, we'll instead record
- # it and return the etag for it.
- # @param [#cache_key] object what to look up the etag for
- def etag_for(object)
- cache_key = cache_key_for(object)
- etag_value = RocketPants.cache[cache_key].presence || record(object, cache_key)
- "#{cache_key}:#{etag_value}"
- end
-
- # Returns the default cache key for a given object.
- # Note that when the object defines a rp_object_key method, it will
- # be used as the
- #
- # @param [Object, #rp_object_key] the object to find the cache key for.
- def cache_key_for(object)
- if object.respond_to?(:rp_object_key)
- Digest::MD5.hexdigest object.rp_object_key
- else
- suffix = (object.respond_to?(:new?) && object.new?) ? "new" : object.id
- Digest::MD5.hexdigest "#{object.class.name}/#{suffix}"
- end
- end
-
- def normalise_etag(identifier_or_object)
- %("#{identifier_or_object.to_s}")
- end
-
- end
-
- module ClassMethods
-
- # Sets up automatic etag and cache control headers for api resource
- # controllers using an after filter. Note that for the middleware
- # to actually be inserted, `RocketPants.enable_caching` needs to be
- # set to true.
- # @param [Symbol*] args a collection of action names to perform caching on.
- # @param [Hash] options options to configure caching
- # @option options [Integer, ActiveSupport::Duration] :cache_for the amount of
- # time to cache timeout based actions for.
- # @example Setting up caching on a series of actions
- # caches :index, :show
- # @example Setting up caching with options
- # caches :index, :show, :cache_for => 3.minutes
- def caches(*args)
- options = args.extract_options!
- self.cached_actions += Array.wrap(args).map(&:to_s).compact
- # Setup the time based caching.
- if options.has_key?(:cache_for)
- self.caching_timeout = options.delete(:cache_for)
- end
- # Merge in any caching options for other controllers.
- caching_options.merge!(options.delete(:caching_options) || {})
- end
-
- end
-
- module InstanceMethods
-
- def cache_action?(action = params[:action])
- RocketPants.caching_enabled? && cached_actions.include?(action)
- end
-
- def cache_response(resource, single_resource)
- # Add in the default options.
- response.cache_control.merge! caching_options
- # We need to set the etag based on the object when it is singular
- # Note that the object is responsible for clearing the etag cache.
- if single_resource
- response["ETag"] = Caching.normalise_etag Caching.etag_for(resource)
- # Otherwise, it's a collection and we need to use time based caching.
- else
- response.cache_control[:max_age] = caching_timeout
- end
- end
-
- # The callback use to automatically cache the current response object, using it's
- # cache key as a guide. For collections, instead of using an etag we'll use the request
- # path as a cache key and instead use a timeout.
- def post_process_exposed_object(resource, type, singular)
- super # Make sure we invoke the old hook.
- if cache_action?
- cache_response resource, singular
- end
- end
-
- end
-
- end
-end
View
112 vendor/rocket_pants/lib/rocket_pants/controller/error_handling.rb
@@ -1,112 +0,0 @@
-module RocketPants
- # Makes it easier to both throw named exceptions (corresponding to
- # error states in the response) and to then process the response into
- # something useable in the response.
- module ErrorHandling
- extend ActiveSupport::Concern
-
- included do
- rescue_from RocketPants::Error, :with => :render_error
- class_attribute :error_mapping
- self.error_mapping = {}
- end
-
- module ClassMethods
-
- def map_error!(from, to)
- error_mapping[from] = to
- rescue_from from, :with => :render_error
- end
-
- end
-
- module InstanceMethods
-
- # Dynamically looks up and then throws the error given by a symbolic name.
- # Optionally takes a string message argument and a hash of 'context'.
- # @overload error!(name, context = {})
- # @param [Symbol] name the name of the exception, looked up using RocketPants::Errors
- # @param [Hash{Symbol => Object}] context the options passed to the error message translation.
- # @overload error!(name, message, context = {})
- # @param [Symbol] name the name of the exception, looked up using RocketPants::Errors
- # @param [String] message an optional message describing the error
- # @param [Hash{Symbol => Object}] context the options passed to the error message translation.
- # @raise [RocketPants::Error] the error from the given options
- def error!(name, *args)
- context = args.extract_options!
- klass = Errors[name] || Error
- exception = klass.new(*args).tap { |e| e.context = context }
- raise exception
- end
-
- # From a given exception, gets the corresponding error message using I18n.
- # It will use context (if defined) on the exception for the I18n context base and
- # will use either the result of Error#error_name (if present) or :system for
- # the name of the error.
- # @param [StandardError] exception the exception to get the message from
- # @return [String] the found error message.
- def lookup_error_message(exception)
- # TODO: Add in notification hooks for non-standard exceptions.
- name = lookup_error_name(exception)
- message = (exception.message == exception.class.name) ? 'An unknown error has occurred.' : exception.message
- I18n.t name, lookup_error_context(exception).reverse_merge(:scope => :"rocket_pants.errors", :default => message)
- end
-
- # Lookup error name will automatically handle translating errors to a
- # simpler, symbol representation. It's implemented like this to make it
- # possible to override to a simple format.
- # @param [StandardError] exception the exception to find the name for
- # @return [Symbol] the name of the given error
- def lookup_error_name(exception)
- if exception.respond_to?(:error_name)
- exception.error_name
- else
- :system
- end
- end
-
- # Returns the error status code for a given exception.
- # @param [StandardError] exception the exception to find the status for
- # @return [Symbol] the name of the given error
- def lookup_error_status(exception)
- exception.respond_to?(:http_status) ? exception.http_status : 500
- end
-
- # Returns the i18n context for a given exception.
- # @param [StandardError] exception the exception to find the context for
- # @param [Hash] the i18n translation context.
- def lookup_error_context(exception)
- exception.respond_to?(:context) ? exception.context : {}
- end
-
- # Returns extra error details for a given object, making it useable
- # for hooking in external exceptions.
- def lookup_error_extras(exception)
- {}
- end
-
- # Renders an exception as JSON using a nicer version of the error name and
- # error message, following the typically error standard as laid out in the JSON
- # api design.
- # @param [StandardError] exception the error to render a response for.
- def render_error(exception)
- logger.debug "Rendering error for #{exception.class.name}: #{exception.message}" if logger
- # When a normalised class is present, make sure we
- # convert it to a useable error class.
- normalised_class = exception.class.ancestors.detect do |klass|
- klass < StandardError and error_mapping.has_key?(klass)
- end
- if normalised_class
- exception = error_mapping[normalised_class].new(exception.message)
- end
- self.status = lookup_error_status(exception)
- render_json({
- :error => lookup_error_name(exception).to_s,
- :error_description => lookup_error_message(exception)
- }.merge(lookup_error_extras(exception)))
- end
-
- end
-
- end
-end
View
30 vendor/rocket_pants/lib/rocket_pants/controller/instrumentation.rb
@@ -1,30 +0,0 @@
-require 'action_controller/log_subscriber'
-require 'abstract_controller/logger'
-
-module RocketPants
- module Instrumentation
- extend ActiveSupport::Concern
- include AbstractController::Logger
-
- def process_action(action, *args)
- raw_payload = {
- :controller => self.class.name,
- :action => self.action_name,
- :params => request.filtered_parameters,
- :formats => [:json],
- :method => request.method,
- :path => (request.fullpath rescue "unknown")
- }
-
- ActiveSupport::Notifications.instrument("start_processing.rocket_pants", raw_payload.dup)
-
- ActiveSupport::Notifications.instrument("process_action.rocket_pants", raw_payload) do |payload|
- result = super
- payload[:status] = response.status
- result
- end
- end
-
- end
- ActionController::LogSubscriber.attach_to :rocket_pants
-end
View
70 vendor/rocket_pants/lib/rocket_pants/controller/rescuable.rb
@@ -1,70 +0,0 @@
-module RocketPants
- # An alternative to Rail's built in ActionController::Rescue module,
- # tailored to deeply integrate rescue notififers into the application.
- #
- # Thus, it is relatively simple to
- module Rescuable
- extend ActiveSupport::Concern
- include ActiveSupport::Rescuable
-
- DEFAULT_NOTIFIER_CALLBACK = lambda do |controller, exception, req|
- # Do nothing by default...
- end
-
- NAMED_NOTIFIER_CALLBACKS = {
- :airbrake => lambda { |c, e, r|
- unless c.send(:airbrake_local_request?)
- c.error_identifier = Airbrake.notify(e, c.send(:airbrake_request_data))
- end
- }
- }
-
- included do
- class_attribute :exception_notifier_callback
- attr_accessor :error_identifier
- self.exception_notifier_callback ||= DEFAULT_NOTIFIER_CALLBACK
- end
-
- module ClassMethods
-
- # Tells rocketpants to use the given exception handler to deal with errors.
- # E.g. use_named_exception_handler! :airbrake
- # @param [Symbol] name the name of the exception handler to use.
- def use_named_exception_notifier(name)
- handler = NAMED_NOTIFIER_CALLBACKS.fetch(name, DEFAULT_NOTIFIER_CALLBACK)
- self.exception_notifier_callback = handler
- end
-
- end
-
- module InstanceMethods
-
- # Overrides the lookup_error_extras method to also include an error_identifier field
- # to be sent back to the client.
- def lookup_error_extras(exception)
- extras = super
- extras = extras.merge(:error_identifier => error_identifier) if error_identifier
- extras
- end
-
- private
-
- # Overrides the processing internals to rescue any exceptions and handle them with the
- # registered exception rescue handler.
- def process_action(*args)
- super
- rescue Exception => exception
- # Otherwise, use the default built in handler.
- logger.error "Exception occured: #{exception.class.name} - #{exception.message}"
- logger.error "Exception backtrace:"
- exception.backtrace[0, 10].each do |backtrace_line|
- logger.error "=> #{backtrace_line}"
- end
- exception_notifier_callback.call(self, exception, request)
- render_error exception
- end
-
- end
-
- end
-end
View
118 vendor/rocket_pants/lib/rocket_pants/controller/respondable.rb
@@ -1,118 +0,0 @@
-require 'will_paginate/collection'
-
-module RocketPants
- module Respondable
- extend ActiveSupport::Concern
-
- def self.normalise_object(object, options = {})
- # Convert the object using a standard grape-like lookup chain.
- if object.is_a?(Array) || object.is_a?(Set)
- object.map { |o| normalise_object o, options }
- elsif object.respond_to?(:serializable_hash)
- object.serializable_hash options
- elsif object.respond_to?(:as_json)
- object.as_json options
- else
- object
- end
- end
-
- module InstanceMethods
-
- private
-
- def normalise_object(object, options = {})
- Respondable.normalise_object object, options
- end
-
- # Given a json object or encoded json, will encode it
- # and set it to be the output of the given page.
- def render_json(json, options = {})
- # Don't convert raw strings to JSON.
- json = ActiveSupport::JSON.encode(json) unless json.respond_to?(:to_str)
- # Encode the object to json.
- self.status ||= :ok
- self.content_type = Mime::JSON
- self.response_body = json
- end
-
- # Renders a raw object, without any wrapping etc.
- # Suitable for nicer object handling.
- def respond_with(object, options = {})
- render_json normalise_object(object, options)
- end
-
- # Renders a single resource.
- def resource(object, options = {})
- pre_process_exposed_object object, :resource, true
- render_json({
- :response => normalise_object(object, options)
- })
- post_process_exposed_object object, :resource, true
- end
-
- # Renders a normal collection to JSON.
- def collection(collection, options = {})
- pre_process_exposed_object collection, :collection, false
- options = options.reverse_merge(:compact => true)
- render_json({
- :response => normalise_object(collection, options),
- :count => collection.length
- })
- post_process_exposed_object collection, :collection, false
- end
-
- # Renders a paginated collecton to JSON.
- def paginated(collection, options = {})
- pre_process_exposed_object collection, :paginated, false
- options = options.reverse_merge(:compact => true)
- render_json({
- :response => normalise_object(collection, options),
- :count => collection.length,
- :pagination => {
- :previous => collection.previous_page.try(:to_i),
- :next => collection.next_page.try(:to_i),
- :current => collection.current_page.try(:to_i),
- :per_page => collection.per_page.try(:to_i),
- :count => collection.total_entries.try(:to_i),
- :pages => collection.total_pages.try(:to_i)
- }
- })
- post_process_exposed_object collection, :paginated, false
- end
-
- # Exposes an object to the response - Essentiall, it tells the
- # controller that this is what we need to render.
- def exposes(object, options = {})
- case object
- when WillPaginate::Collection
- paginated object, options
- when Array
- collection object, options
- else
- resource object, options
- end
- end
- alias expose exposes
-
- # Hooks in to allow you to perform pre-processing of objects
- # when they are exposed. Used for plugins to the controller.
- #
- # @param [Object] resource the exposed object.
- # @param [Symbol] type the type of object exposed, one of :resource, :collection or :paginated
- # @param [true,false] singular Whether or not the given object is singular (e.g. :resource)
- def pre_process_exposed_object(resource, type, singular)
- end
-
- # Hooks in to allow you to perform post-processing of objects
- # when they are exposed. Used for plugins to the controller.
- #
- # @param [Object] resource the exposed object.
- # @param [Symbol] type the type of object exposed, one of :resource, :collection or :paginated
- # @param [true,false] singular Whether or not the given object is singular (e.g. :resource)
- def post_process_exposed_object(resource, type, singular)
- end
-
- end
- end
-end
View
11 vendor/rocket_pants/lib/rocket_pants/controller/url_for.rb
@@ -1,11 +0,0 @@
-module RocketPants
- module UrlFor
-
- def url_options
- options = super
- options = options.merge(:version => version) if version.present?
- options
- end
-
- end
-end
View
39 vendor/rocket_pants/lib/rocket_pants/controller/versioning.rb
@@ -1,39 +0,0 @@
-module RocketPants
- module Versioning
-
- extend ActiveSupport::Concern
-
- included do
- class_attribute :_version_range
- end
-
- module ClassMethods
-
- def version(version)
- version = version..version if version.is_a?(Integer)
- self._version_range = version
- before_filter :verify_api_version
- end
-
- end
-
- module InstanceMethods
-
- protected
-
- def version
- @version ||= begin
- Integer(params[:version])
- rescue ArgumentError
- nil
- end
- end
-
- def verify_api_version
- error! :invalid_version unless version.present? && _version_range.include?(version)
- end
-
- end
-
- end
-end
View
112 vendor/rocket_pants/lib/rocket_pants/exceptions.rb
@@ -1,112 +0,0 @@
-module RocketPants
-
- # Represents the standard error type as defined by the API.
- # RocketPants::Error instances will be caught and automatically rendered as JSON
- # by the controller during processing.
- class Error < StandardError
-
- # @overload error_name
- # Returns the error name for this error class, defaulting to
- # the class name underscorized minus _error.
- # @return [Symbol] the given errors name.
- # @overload error_name(value)
- # Sets the error name for the current class.
- # @param [#to_sym] the name of this error.
- def self.error_name(value = nil)
- if value.nil?
- @name ||= name.underscore.split("/").last.sub(/_error$/, '').to_sym
- else
- @name = (value.presence && value.to_sym)
- end
- end
-
- # @overload http_status
- # Returns the http status code of this error, defaulting to 400 (Bad Request).
- # @overload http_status(value)
- # Sets the http status code for this error to a given symbol / integer.
- # @param value [String, Fixnum] value the new status code.
- def self.http_status(value = nil)
- if value.nil?
- @http_status ||= 400
- else
- @http_status = (value.presence && value)
- end
- end
-
- # Gets the name of this error from the class.
- def error_name
- self.class.error_name
- end
-
- # Gets the http status of this error from the class.
- def http_status
- self.class.http_status
- end
-
- # Setter for optional data about this error, used for translation.
- attr_writer :context
-
- # Gets the context for this error, defaulting to nil.
- # @return [Hash] the context for this param.
- def context
- @context ||= {}
- end
-
- error_name :unknown
-
- end
-
- # A simple map of data about errors that the rocket pants system can handle.
- class Errors
-
- @@errors = {}
-
- # Returns a hash of all known errors, keyed by their error name.
- # @return [Hash{Symbol => RocketPants::Error}] the hash of known errors.
- def self.all
- @@errors.dup
- end
-
- # Looks up a specific error from the given name, returning nil if none are found.
- # @param [#to_sym] name the name of the error to look up.
- # @return [Error, nil] the error class if found, otherwise nil.
- def self.[](name)
- @@errors[name.to_sym]
- end
-
- # Adds a given Error class in the list of all errors, making it suitable
- # for lookup via [].
- # @see Errors[]
- # @param [Error] error the error to register.
- def self.add(error)
- @@errors[error.error_name] = error
- end
-
- # Creates an error class to represent a given error state.
- # @param [Symbol] name the name of the given error
- # @param [Hash] options the options used to create the error class.
- # @option options [Symbol] :class_name the name of the class (under `RocketPants`), defaulting to the classified name.
- # @option options [Symbol] :error_name the name of the error, defaulting to the name parameter.
- # @option options [Symbol] :http_status the status code for the given error, doing nothing if absent.
- # @example Adding a RocketPants::NinjasUnavailable class w/ `:service_unavailable` as the status code:
- # register! :ninjas_unavailable, :http_status => :service_unavailable
- def self.register!(name, options = {})
- klass_name = (options[:class_name] || name.to_s.classify).to_sym
- klass = Class.new(Error)
- klass.error_name(options[:error_name] || name.to_s.underscore)
- klass.http_status(options[:http_status]) if options[:http_status].present?
- (options[:under] || RocketPants).const_set klass_name, klass
- add klass
- klass
- end
-
- # The default set of exceptions.
- register! :throttled, :http_status => :service_unavailable
- register! :unauthenticated, :http_status => :unauthorized
- register! :invalid_version, :http_status => :not_found
- register! :not_implemented, :http_status => :service_unavailable
- register! :not_found, :http_status => :not_found
-
- end
-
-end
View
8 vendor/rocket_pants/lib/rocket_pants/locale/en.yml
@@ -1,8 +0,0 @@
-en:
- rocket_pants:
- errors:
- throttled: "You have gone over your allotted amount of requests and have been throttled."
- unauthenticated: "This action requires authentication to continue."
- invalid_version: "This action is not available in the given version of the api."
- not_implemented: "The feature you requested has not yet been implemented and hence is currently unavailable."
- not_found: "The requested resource could not be found."
View
44 vendor/rocket_pants/lib/rocket_pants/railtie.rb
@@ -1,44 +0,0 @@
-module RocketPants
- class Railtie < Rails::Railtie
-
- config.rocket_pants = ActiveSupport::OrderedOptions.new
- config.rocket_pants.use_caching = nil
-
- config.i18n.railties_load_path << File.expand_path('../locale/en.yml', __FILE__)
-
- initializer "rocket_pants.logger" do
- ActiveSupport.on_load(:rocket_pants) { self.logger ||= Rails.logger }
- end
-
- initializer "rocket_pants.configuration" do |app|
- rp_config = app.config.rocket_pants
- rp_config.use_caching = Rails.env.production? if rp_config.use_caching.nil?
- RocketPants.caching_enabled = rp_config.use_caching
- # Set the rocket pants cache if present.
- RocketPants.cache = rp_config.cache if rp_config.cache
- end
-
- initializer "rocket_pants.url_helpers" do |app|
- ActiveSupport.on_load(:rocket_pants) do
- include app.routes.url_helpers
- end
- end
-
- initializer "rocket_pants.setup_testing" do |app|
- ActiveSupport.on_load(:rocket_pants) do
- include ActionController::Testing if Rails.env.test?
- end
- end
-
- initializer "rocket_pants.setup_caching" do |app|
- if RocketPants.caching_enabled?
- app.middleware.insert 'Rack::Runtime', RocketPants::CacheMiddleware
- end
- end
-
- rake_tasks do
- load "rocket_pants/tasks/rocket_pants.rake"
- end
-
- end
-end
View
26 vendor/rocket_pants/lib/rocket_pants/routing.rb
@@ -1,26 +0,0 @@
-module RocketPants
- module Routing
-
- # Scopes a set of given api routes, allowing for option versions.
- # @param [Hash] options options to pass through to the route e.g. `:module`.
- # @option options [Array<Integer>, Integer] :versions the versions to support
- # @option options [Array<Integer>, Integer] :version the single version to support
- # @raise [ArgumentError] raised when the version isn't provided.
- def rocket_pants(options = {}, &blk)
- versions = (Array(options.delete(:versions)) + Array(options.delete(:version))).flatten.map(&:to_s)
- versions.each do |version|
- raise ArgumentError, "Got invalid version: '#{version}'" unless version =~ /\A\d+\Z/
- end
- versions_regexp = /(#{versions.uniq.join("|")})/
- raise ArgumentError, 'please provide atleast one version' if versions.empty?
- options = options.deep_merge({
- :constraints => {:version => versions_regexp},
- :path => ':version',
- :defaults => {:format => 'json'}
- })
- scope options, &blk
- end
- alias api rocket_pants
-
- end
-end
View
117 vendor/rocket_pants/lib/rocket_pants/rspec_matchers.rb
@@ -1,117 +0,0 @@
-module RocketPants
- module RSpecMatchers
- extend RSpec::Matchers::DSL
-
- def self.normalised_error(e)
- if e.is_a?(String) || e.is_a?(Symbol)
- Errors[e]
- else
- e
- end
- end
-
- def self.normalise_urls(object)
- if object.is_a?(Array)
- object.each { |o| o['url'] = nil }
- elsif object.is_a?(Hash) || object.is_a?(APISmith::Smash)
- object['url'] = nil
- end
- object
- end
-
- # Converts it to JSON and back again.
- def self.normalise_as_json(object, options = {})
- options = options.reverse_merge(:compact => true) if object.is_a?(Array)
- object = RocketPants::Respondable.normalise_object(object, options)
- j = ActiveSupport::JSON
- j.decode(j.encode({'response' => object}))['response']
- end
-
- def self.normalise_response(response, options = {})
- normalise_urls normalise_as_json response, options
- end
-
- def self.valid_for?(response, allowed, disallowed)
- body = response.decoded_body
- return false if body.blank?
- body = body.to_hash
- return false if body.has_key?("error")
- allowed.all? { |f| body.has_key?(f) } && !disallowed.any? { |f| body.has_key?(f) }
- end
-
- def self.differ
- @_differ ||= RSpec::Expectations::Differ.new
- end
-
- matcher :_be_api_error do |error_type|
-
- match do |response|
- @error = response.decoded_body.error
- @error.present? && (error_type.blank? || RSpecMatchers.normalised_error(@error) == error_type)
- end
-
- failure_message_for_should do |response|
- if @error.blank?
- "expected #{error_type || "any error"} on response, got no error"
- else error_type.present? && (normalised = RSpecMatchers.normalised_error(@error)) != error_type
- "expected #{error_type || "any error"} but got #{normalised} instead"
- end
- end
-
- failure_message_for_should_not do |response|
- "expected response to not have an #{error_type || "error"}, but it did (#{@error})"
- end
-
- end
-
- matcher :be_singular_resource do
-
- match do |response|
- RSpecMatchers.valid_for? response, %w(), %w(count pagination)
- end
-
- end
-
- matcher :be_collection_resource do
-
- match do |response|
- RSpecMatchers.valid_for? response, %w(count), %w(pagination)
- end
-
- end
-
- matcher :be_paginated_resource do
-
- match do |response|
- RSpecMatchers.valid_for? response, %w(count pagination), %w()
- end
-
- end
-
- matcher :have_exposed do |*args|
- normalised_response = RSpecMatchers.normalise_response(*args)
-
- match do |response|
- @decoded = RSpecMatchers.normalise_urls(response.parsed_body["response"])
- normalised_response == @decoded
- end
-
- failure_message_for_should do |response|
- message = "expected api to have exposed #{normalised_response.inspect}, got #{@decoded.inspect} instead."
- message << "\n\nDiff: #{RSpecMatchers.differ.diff_as_object(@decoded, normalised_response)}"
- message
- end
-
- failure_message_for_should_not do |response|
- "expected api to not have exposed #{normalised_response.inspect}"
- end
-
- end
-
- def be_api_error(error = nil)
- _be_api_error error
- end
-
-
- end
-end
View
32 vendor/rocket_pants/lib/rocket_pants/tasks/rocket_pants.rake
@@ -1,32 +0,0 @@
-namespace :rocket_pants do
-
- desc "Generates a pretty listing of errors registered in the application"
- task :errors => :environment do
- errors = RocketPants::Errors.all
- output = [["Error Name", "HTTP Status", "Class Name"]]
- errors.keys.map(&:to_s).sort.each do |key|
- klass = errors[key.to_sym]
- http_status = klass.respond_to?(:http_status) && klass.http_status
- status_code = Rack::Utils.status_code(http_status)
- status_name = http_status.is_a?(Integer) ? Rack::Utils::HTTP_STATUS_CODES[http_status] : http_status.to_s.titleize
- output << [key, "#{status_code} #{status_name}", klass.name]
- end
- total_width = 8
- 0.upto(2) do |column|
- fields = output.map { |i| i[column] }
- length = fields.map(&:length).max + 2
- total_width += length
- fields.each_with_index do |item, idx|
- output[idx][column] = item.ljust(length)
- end
- end
- puts("+#{"-" * total_width}+")
- puts "| #{output[0] * " | "} |"
- puts("+#{"-" * total_width}+")
- output[1..-1].each do |row|
- puts "| #{row * " | "} |"
- end
- puts("+#{"-" * total_width}+")
- end
-
-end
View
98 vendor/rocket_pants/lib/rocket_pants/test_helper.rb
@@ -1,98 +0,0 @@
-require 'hashie/mash'
-
-module RocketPants
- module TestHelper
- extend ActiveSupport::Concern
-
- included do
- # Extend the response on first include.
- class_attribute :_default_version
- unless ActionController::TestResponse < ResponseHelper
- ActionController::TestResponse.send :include, ResponseHelper
- end
- end
-
- module ResponseHelper
-
- def parsed_body
- @_parsed_body ||= begin
- ActiveSupport::JSON.decode(body)
- rescue StandardError => e
- nil
- end
- end
-
- def decoded_body
- @_decoded_body ||= begin
- decoded = parsed_body
- if decoded.is_a?(Hash)
- Hashie::Mash.new(decoded)
- else
- decoded
- end
- end
- end
-
- end
-
- module ClassMethods
-
- def default_version(value)
- self._default_version = value
- end
-
- end
-
- module InstanceMethods
-
- def decoded_response
- value = response.decoded_body.try(:response)
- end
-
- def decoded_pagination
- response.decoded_body.try :pagination
- end
-
- def decoded_count
- response.decoded_body.try :count
- end
-
- def decoded_error_class
- error = response.decoded_body.try :error
- error.presence && RocketPants::Errors[error]
- end
-
- # RSpec matcher foo.
-
- def have_decoded_response(value)
- response = normalise_value(value)
- end
-
- protected
-
- # Like process, but automatically adds the api version.
- def process(action, parameters = nil, session = nil, flash = nil, http_method = 'GET')
- parameters ||= {}
- if _default_version.present? && parameters[:version].blank? && parameters['version'].blank?
- parameters[:version] = _default_version
- end
- super
- end
-
- def normalise_value(value)
- if value.is_a?(Hash)
- value.inject({}) do |acc, (k, v)|
- acc[k.to_s] = normalise_value(v)
- acc
- end
- elsif value.is_a?(Array)
- value.map { |v| normalise_value v }
- else
- value
- end
- end
-
- end
-
- end
-end
View
30 vendor/rocket_pants/rocket_pants.gemspec
@@ -1,30 +0,0 @@
-# -*- encoding: utf-8 -*-
-lib = File.expand_path('../lib/', __FILE__)
-$:.unshift lib unless $:.include?(lib)
-
-Gem::Specification.new do |s|
- s.name = "rocket_pants"
- s.version = "0.1.0"
- s.platform = Gem::Platform::RUBY
- s.authors = ["Darcy Laycock"]
- s.email = ["sutto@sutto.net"]
- s.homepage = "http://github.com/filtersquad"
- s.summary = "JSON API love for Rails and ActionController"
- s.description = "Rocket Pants adds JSON API love to Rails and ActionController, making it simpler to build resource-oriented controllers."
- s.required_rubygems_version = ">= 1.3.6"
-
- s.add_dependency 'actionpack', '~> 3.0'
- s.add_dependency 'railties', '~> 3.0'
- s.add_dependency 'will_paginate', '~> 3.0'
- s.add_dependency 'hashie', '~> 1.0'
- s.add_dependency 'api_smith'
- s.add_dependency 'will_paginate'
- s.add_dependency 'moneta'
- s.add_development_dependency 'rspec', '~> 2.4.0'
- s.add_development_dependency 'rspec-rails', '~> 2.4.0'
- s.add_development_dependency 'rr', '~> 1.0.0'
- s.add_development_dependency 'webmock'
-
- s.files = Dir.glob("{lib}/**/*")
- s.require_path = 'lib'
-end
View
9 vendor/rocket_pants/spec/fixtures/collection.json
@@ -1,9 +0,0 @@
-{
- "count": 4,
- "response": [
- {"name": "Name of Doom - 1"},
- {"name": "Name of Doom - 2"},
- {"name": "Name of Doom - 3"},
- {"name": "Name of Doom - 4"}
- ]
-}
View
17 vendor/rocket_pants/spec/fixtures/paginated.json
@@ -1,17 +0,0 @@
-{
- "count": 4,
- "pagination": {
- "next": 2,
- "current": 1,
- "previous": null,
- "per_page": 4,
- "count": 20,
- "pages": 5
- },
- "response": [
- {"name": "Name of Doom - 1"},
- {"name": "Name of Doom - 2"},
- {"name": "Name of Doom - 3"},
- {"name": "Name of Doom - 4"}
- ]
-}
View
4 vendor/rocket_pants/spec/fixtures/simple_error.json
@@ -1,4 +0,0 @@
-{
- "error": "throttled",
- "error_description": "You have gone over your allotted amount of requests and have been throttled."
-}
View
5 vendor/rocket_pants/spec/fixtures/simple_object.json
@@ -1,5 +0,0 @@
-{
- "response": {
- "name": "My Simple Object"
- }
-}
View
3  vendor/rocket_pants/spec/fixtures/simple_string.json
@@ -1,3 +0,0 @@
-{
- "response": "Hello World"
-}
View
4 vendor/rocket_pants/spec/fixtures/unknown_error.json
@@ -1,4 +0,0 @@
-{
- "error": "ninja_error",
- "error_description": "A ninja as attacked your fortress of solitude."
-}
View
101 vendor/rocket_pants/spec/rocket_pants/caching_spec.rb
@@ -1,101 +0,0 @@
-require 'spec_helper'
-
-describe RocketPants::Caching do
-
- let(:object) { Object.new.tap { |i| stub(i).id.returns(10) } }
-
- describe 'dealing with the etag cache' do
-
- it 'should let you remove an item from the cache' do
- stub(RocketPants::Caching).cache_key_for(object) { 'my-cache-key' }
- RocketPants.cache['my-cache-key'] = 'hello there'
- RocketPants::Caching.remove object
- RocketPants.cache['my-cache-key'].should be_nil
- end
-
- it 'should safely delete a non-existant item from the cache' do
- expect do
- RocketPants::Caching.remove object
- end.to_not raise_error
- end
-
- it 'should let you record an object in the cache with a cache_key method' do
- mock(RocketPants::Caching).cache_key_for(object) { 'my-cache-key' }
- mock(object).cache_key { 'hello' }
- RocketPants::Caching.record object
- RocketPants.cache['my-cache-key'].should == Digest::MD5.hexdigest('hello')
- end
-
- it 'should let you record an object in the cache with the default inspect value' do
- mock(RocketPants::Caching).cache_key_for(object) { 'my-cache-key' }
- RocketPants::Caching.record object
- RocketPants.cache['my-cache-key'].should == Digest::MD5.hexdigest(object.inspect)
- end
-
- end
-
- describe 'computing the cache key for an object' do
-
- it 'should return a md5-like string' do
- RocketPants::Caching.cache_key_for(object).should =~ /\A[a-z0-9]{32}\Z/
- end
-
- it 'should use the rp_object_key method if present' do
- mock(object).rp_object_key { 'hello' }
- RocketPants::Caching.cache_key_for(object).should == Digest::MD5.hexdigest('hello')
- end
-
- it 'should build a default cache key for records with new? that are new' do
- mock(object).new? { true }
- RocketPants::Caching.cache_key_for(object).should == Digest::MD5.hexdigest('Object/new')
- end
-
- it 'should build a default cache key for records with new? that are old' do
- mock(object).new? { false }
- RocketPants::Caching.cache_key_for(object).should == Digest::MD5.hexdigest('Object/10')
- end
-
- it 'should build a default cache key for records without new' do
- RocketPants::Caching.cache_key_for(object).should == Digest::MD5.hexdigest('Object/10')
- end
-
- end
-
- describe 'normalising an etag' do
-
- it 'should correctly convert it to the string' do
- def object.to_s; 'Hello-World'; end
- mock(object).to_s { 'Hello-World' }
- described_class.normalise_etag(object).should == '"Hello-World"'
- end
-
- it 'should correctly deal with a basic case' do
- described_class.normalise_etag('SOMETAG').should == '"SOMETAG"'
- end
-
- end
-
- describe 'fetching an object etag' do
-
- before :each do
- stub(RocketPants::Caching).cache_key_for(object) { 'my-cache-key' }
- end
-
- it 'should use the cache key as a prefix' do
- RocketPants::Caching.etag_for(object).should =~ /\Amy-cache-key\:/
- end
-
- it 'should fetch the recorded etag' do
- mock(RocketPants.cache)['my-cache-key'].returns 'hello-world'
- RocketPants::Caching.etag_for(object)
- end
-
- it 'should generate a new etag if one does not exist' do
- mock(RocketPants::Caching).record object, 'my-cache-key'
- stub(RocketPants.cache)['my-cache-key'].returns nil
- RocketPants::Caching.etag_for object
- end
-
- end
-
-end
View
194 vendor/rocket_pants/spec/rocket_pants/client_spec.rb
@@ -1,194 +0,0 @@
-require 'spec_helper'
-
-describe RocketPants::Client do
-
- let(:test_client) do
- Class.new RocketPants::Client
- end
-
- describe 'setting versioned endpoints' do
-
- it 'should default to no version' do
- RocketPants::Client._version.should be_nil
- test_client._version.should be_nil
- end
-
- it 'should let you set the version' do
- test_client.version 1
- test_client._version.should == 1
- test_client.new.send(:endpoint).should == '1'
- end
-
- it 'should let you change the version' do
- test_client.version 1
- client = test_client.new
- test_client.version 2
- test_client._version.should == 2
- client.send(:endpoint).should == '2'
- end
-
- it 'should merge the endpoint with the version' do
- test_client.endpoint 'test'
- test_client.version 1
- client = test_client.new
- client.send(:endpoint).should == '1/test'
- test_client.version 2
- client.send(:endpoint).should == '2/test'
- end
-
- end
-
- describe 'handling errors' do
-
- let(:test_client) do
- Class.new(RocketPants::Client).tap do |c|
- c.version 1
- c.base_uri "http://localhost:3000"
- end
- end
-
- let(:client) { test_client.new }
- subject { client }
-
- it 'should correctly raise an error when the response has an error' do
- stub_with_fixture :get, 'error?', 'simple_error'
- expect do
- client.get 'error'
- end.to raise_error
- end
-
- it 'should use error description for the exception' do
- begin
- stub_with_fixture :get, 'error?', 'simple_error'
- client.get 'error'
- rescue RocketPants::Error => e
- error_object = Crack::JSON.parse(api_fixture_json('simple_error'))
- e.message.should == error_object["error_description"]
- end
- end
-
- it 'should use the rocket pants error registry' do
- stub_with_fixture :get, 'error?', 'simple_error'
- expect do
- client.get 'error'
- end.to raise_error(RocketPants::Throttled)
- end
-
- it 'should default to a normal rocket pants error' do
- stub_with_fixture :get, 'error?', 'unknown_error'
- expect do
- client.get 'error'
- end.to raise_error(RocketPants::Error)
- end
-
- end
-
- describe 'handling responses' do
-
- let(:test_client) do
- Class.new(RocketPants::Client).tap do |c|
- c.version 1
- c.base_uri "http://localhost:3000"
- end
- end
-
- let(:client) { test_client.new }
- subject { client }
-
- it 'should correctly unpack normal objects' do
- stub_with_fixture :get, 'test?', 'simple_object'
- client.get('test').should == {
- 'name' => 'My Simple Object'
- }
- end
-
- it 'should correct unpack ordinary types' do
- stub_with_fixture :get, 'test?', 'simple_string'
- client.get('test').should == 'Hello World'
- end
-
- it 'should correctly unpack paginated objects' do
- stub_with_fixture :get, 'test?', 'paginated'
- response = client.get('test')
- response.should be_kind_of(WillPaginate::Collection)
- response.total_pages.should == 5
- response.total_entries.should == 20
- response.should == Crack::JSON.parse(api_fixture_json('paginated'))['response']
- end
-
- it 'should correctly unpack arrays of objects' do
- stub_with_fixture :get, 'test?', 'collection'
- response = client.get('test')
- response.should be_kind_of(Array)
- response.should == Crack::JSON.parse(api_fixture_json('collection'))['response']
- end
-
- end
-
- describe 'handling structured responses' do
-
- class Structured < APISmith::Smash
- property :name
- end
-
- let(:test_client) do
- Class.new(RocketPants::Client).tap do |c|
- c.version 1
- c.base_uri "http://localhost:3000"
- end
- end
-
- let(:client) { test_client.new }
- subject { client }
-
- it 'should default to a hash' do
- stub_with_fixture :get, 'test?', 'simple_object'
- client.get('test').should be_a Hash
- end
-
- it 'should let you specify a transformer' do
- stub_with_fixture :get, 'test?', 'simple_object'
- client.get('test', :transformer => Structured).should be_a Structured
- client.get('test', :transformer => Structured).name.should == "My Simple Object"
- client.get('test', :as => Structured).should be_a Structured
- client.get('test', :as => Structured).name.should == "My Simple Object"
- end
-
- it 'should automatically handle transforming arrays' do
- stub_with_fixture :get, 'test?', 'collection'
- response = client.get('test', :transformer => Structured)
- response.should be_an Array
- response.size.should be > 0
- response.should be_all { |v| v.is_a?(Structured) }
- end
-
- it 'should automatically handle transforming paginated' do
- stub_with_fixture :get, 'test?', 'paginated'
- response = client.get('test', :transformer => Structured)
- response.should be_an WillPaginate::Collection
- response.size.should be > 0
- response.should be_all { |v| v.is_a?(Structured) }
- end
-
- end
-
- describe 'initialisation' do
-
- let(:test_client) do
- Class.new(RocketPants::Client).tap do |c|
- c.version 1
- c.base_uri "http://localhost:3000"
- end
- end
-
- let(:client) { test_client.new :api_host => 'http://localhost:3001/' }
- subject { client }
-
- it 'should make it easy to set an api host' do
- add_response_stub stub_request(:get, 'http://localhost:3001/1/test?'), 'simple_object'
- client.get('test').should == Crack::JSON.parse(api_fixture_json('simple_object'))['response']
- end
-
- end
-
-end
View
345 vendor/rocket_pants/spec/rocket_pants/controller_spec.rb
@@ -1,345 +0,0 @@
-require 'spec_helper'
-require 'logger'
-require 'stringio'
-
-describe RocketPants::Base do
- include ControllerHelpers
-
- describe 'versioning' do
-
- it 'should ok with a valid version' do
- %w(1 2).each do |version|
- get :echo, {}, :version => version.to_s
- content[:error].should be_nil
- end
- end
-
- it 'should return an error for an invalid version number' do
- [0, 3, 10, 2.5, 2.2, '1.1'].each do |version|
- get :echo, {}, :version => version.to_s
- content[:error].should == 'invalid_version'
- end
- end
-
- it 'should return an error for no version number' do
- get :echo, {}, :version => nil
- content[:error].should == 'invalid_version'
- end
-
- end
-
- describe 'respondable' do
-
- it 'should correctly convert a will paginate collection' do
- pager = WillPaginate::Collection.create(2, 10) { |p| p.replace %w(a b c d e f g h i j); p.total_entries = 200 }
- mock(TestController).test_data { pager }
- get :test_data
- content.should have_key(:pagination)
- content[:pagination].should == {
- :next => 3,
- :current => 2,
- :previous => 1,
- :pages => 20,
- :count => 200,
- :per_page => 10,
- :count => 200
- }.stringify_keys
- content.should have_key(:count)
- content.should have_key(:pagination)
- end
-
- it 'should correctly convert a normal collection' do
- mock(TestController).test_data { %w(a b c d) }
- get :test_data
- content[:response].should == %w(a b c d)
- content[:pagination].should be_nil
- content[:count].should == 4
- end
-
- it 'should correctly convert a normal object' do
- object = {:a => 1, :b => 2}
- mock(TestController).test_data { object }
- get :test_data
- content[:count].should be_nil
- content[:pagination].should be_nil
- content[:response].should == {'a' => 1, 'b' => 2}
- end
-
- it 'should correctly convert an object with a serializable hash method' do
- object = {:a => 1, :b => 2}
- stub(object).serializable_hash(anything) { {:serialised => true}}
- mock(TestController).test_data { object }
- get :test_data
- content[:response].should == {'serialised' => true}
- end
-
- it 'should correct convert an object with as_json' do
- object = {:a => 1, :b => 2}
- stub(object).as_json(anything) { {:serialised => true}}
- mock(TestController).test_data { object }
- get :test_data
- content[:response].should == {'serialised' => true}
- end
-
- it 'should correctly hook into paginated responses' do
- pager = WillPaginate::Collection.create(2, 10) { |p| p.replace %w(a b c d e f g h i j); p.total_entries = 200 }
- mock(TestController).test_data { pager }
- hooks = []
- mock.instance_of(TestController).pre_process_exposed_object(pager, :paginated, false) { hooks << :pre }
- mock.instance_of(TestController).post_process_exposed_object(pager, :paginated, false) { hooks << :post }
- get :test_data
- hooks.should == [:pre, :post]
- end
-
- it 'should correctly hook into collection responses' do
- object = %w(a b c d)
- mock(TestController).test_data { object }
- hooks = []
- mock.instance_of(TestController).pre_process_exposed_object(object, :collection, false) { hooks << :pre }
- mock.instance_of(TestController).post_process_exposed_object(object, :collection, false) { hooks << :post }
- get :test_data
- hooks.should == [:pre, :post]
- end
-
- it 'should correctly hook into singular responses' do
- object = {:a => 1, :b => 2}
- mock(TestController).test_data { object }
- hooks = []
- mock.instance_of(TestController).pre_process_exposed_object(object, :resource, true) { hooks << :pre }
- mock.instance_of(TestController).post_process_exposed_object(object, :resource, true) { hooks << :post }
- get :test_data
- hooks.should == [:pre, :post]
- end
-
- end
-
- describe 'error handling' do
-
- it 'should throw the correct error for invalid api versions' do
- get :echo, {}, :version => '3'
- content['error'].should == 'invalid_version'
- end
-
- it 'should return the correct output for a manually thrown error' do
- get :demo_exception
- content['error'].should == 'throttled'
- content['error_description'].should be_present
- end
-
- it 'should stop the flow if you raise an exception' do
- get :premature_termination
- content['error'].should be_present
- content['error_description'].should be_present
- content['response'].should be_nil
- end
-
- it 'should use i18n for error messages' do
- with_translations :rocket_pants => {:errors => {:throttled => 'Oh noes, a puddle.'}} do
- get :demo_exception
- end
- content['error'].should == 'throttled'
- content['error_description'].should == 'Oh noes, a puddle.'
- end
-
- describe 'hooking into the process' do
-
- let(:controller_class) do
- klass = Class.new(TestController)
- klass.class_eval do
- rescue_from StandardError, :with => :render_error
- end
- klass
- end
-
- let(:error) do
- TestController::ErrorOfDoom.new("Hello there")
- end
-
- before :each do
- stub(controller_class).test_error { error }
- end
-
- it 'should let you hook into the error name lookup' do
- mock.instance_of(controller_class).lookup_error_name(error).returns(:my_test_error).times(any_times)
- get :test_error
- content['error'].should == 'my_test_error'
- end
-
- it 'should let you hook into the error message lookup' do
- mock.instance_of(controller_class).lookup_error_message(error).returns 'Oh look, pie.'
- get :test_error
- content['error_description'].should == 'Oh look, pie.'
- end
-
- it 'should let you hook into the error status lookup' do
- mock.instance_of(controller_class).lookup_error_status(error).returns 403
- get :test_error
- response.status.should == 403
- end
-
- it 'should let you add error items to the response' do
- mock.instance_of(controller_class).lookup_error_extras(error).returns(:hello => 'There')
- get :test_error
- content['hello'].should == 'There'
- end
-
- it 'should let you register an item in the error mapping' do
- controller_class.error_mapping[TestController::ErrorOfDoom] = RocketPants::Throttled
- get :test_error
- content['error'].should == 'throttled'
- end
-
- it 'should include parents when checking the mapping' do
- stub(controller_class).test_error { TestController::YetAnotherError }
- controller_class.error_mapping[TestController::ErrorOfDoom] = RocketPants::Throttled
- get :test_error
- content['error'].should == 'throttled'
- end
-
- end
-
- end
-
- describe 'caching' do
-
- let!(:controller_class) { Class.new TestController }
-
- it 'should use a set for storing the cached actions' do
- controller_class.cached_actions.should be_a Set
- controller_class.cached_actions.should == Set.new
- end
-
- it 'should default the caching timeout' do
- end
-
- it 'should let you set the caching timeout' do
- expect do
- controller_class.caches :test_data, :cache_for => 10.minutes
- controller_class.caching_timeout.should == 10.minutes
- end.to change(controller_class, :caching_timeout)
- end
-
- it 'should let you set which actions should be cached' do
- controller_class.cached_actions.should be_empty
- controller_class.caches :test_data
- controller_class.cached_actions.should == ["test_data"].to_set
- end
-
- describe 'when dealing with the controller' do
-
- it 'should invoke the caching callback with caching enabled' do
- set_caching_to true do
- mock.instance_of(controller_class).cache_response.with_any_args
- get :test_data
- end
- end
-
- it 'should not invoke the caching callback with caching disabled' do
- set_caching_to false do
- dont_allow.instance_of(controller_class).cache_response.with_any_args
- get :test_data
- end
- end
-
- before :each do
- controller_class.caches :test_data
- end
-
- around :each do |t|
- set_caching_to true, &t
- end
-
- context 'with a singular response' do
-
- let(:cached_object) { Object.new }
-
- before :each do
- stub(RocketPants::Caching).cache_key_for(cached_object) { "my-object" }
- stub(RocketPants::Caching).etag_for(cached_object) { "my-object:stored-etag" }
- stub(controller_class).test_data { cached_object }
- end
-
- it 'should invoke the caching callback correctly' do
- mock.instance_of(controller_class).cache_response cached_object, true
- get :test_data
- end
-
- it 'should not set the expires in time' do
- get :test_data
- response['Cache-Control'].to_s.should_not =~ /max-age=(\d+)/
- end
-
- it 'should set the response etag' do
- get :test_data
- response['ETag'].should == '"my-object:stored-etag"'
- end
-
- end
-
- context 'with a collection response' do
-
- let(:cached_objects) { [Object.new] }
-
- before :each do
- dont_allow(RocketPants::Caching).cache_key_for.with_any_args
- dont_allow(RocketPants::Caching).etag_for.with_any_args
- stub(controller_class).test_data { cached_objects }
- end
-
- it 'should invoke the caching callback correctly' do
- mock.instance_of(controller_class).cache_response cached_objects, false
- get :test_data
- end
-
- it 'should set the expires in time' do
- get :test_data
- response['Cache-Control'].to_s.should =~ /max-age=(\d+)/
- end
-
- it 'should not set the response etag' do
- get :test_data
- response["ETag"].should be_nil
- end
-
- end
-
- end
-
- end
-
- describe 'error handling' do
-
- let!(:controller_class) { Class.new(TestController) }
- let!(:logger) { ::Logger.new(StringIO.new) }
-
- before :each do
- controller_class.logger = logger
- end
-
- it 'should allow you to override the error handler' do
- get :test_error
- content.should have_key "error"
- content.should have_key "error_description"
- content[:error].should == "system"
- end
-
- it 'should allow you to set the error handle from a named type' do
- controller_class.exception_notifier_callback.should == controller_class::DEFAULT_NOTIFIER_CALLBACK
- controller_class.use_named_exception_notifier :airbrake
- controller_class.exception_notifier_callback.should_not == controller_class::DEFAULT_NOTIFIER_CALLBACK
- controller_class.exception_notifier_callback.should == controller_class::NAMED_NOTIFIER_CALLBACKS[:airbrake]
- controller_class.use_named_exception_notifier :nonexistant
- controller_class.exception_notifier_callback.should == controller_class::DEFAULT_NOTIFIER_CALLBACK
- end
-
- it 'should include the error identifier in the response if set' do
- controller_class.exception_notifier_callback = lambda do |controller, exception, req|
- controller.error_identifier = 'my-test-identifier'
- end
- get :test_error
- content[:error_identifier].should == 'my-test-identifier'
- end
-
- end
-
-end
View
84 vendor/rocket_pants/spec/rocket_pants/error_spec.rb
@@ -1,84 +0,0 @@
-require 'spec_helper'
-
-describe RocketPants::Error do
-
- UnchangedError = Class.new(RocketPants::Error)
- AttackedByNinjas = Class.new(RocketPants::Error) do
- http_status 422
- end
- AnotherError = Class.new(AttackedByNinjas) do
- http_status 404
- error_name :oh_look_a_panda
- end
-
- it 'should be an exception' do
- RocketPants::Error.should be < StandardError
- end
-
- describe 'working with the http status codes' do
-
- it 'should default to 400 for the status code' do
- RocketPants::Error.http_status.should == 400
- UnchangedError.http_status.should == 400
- end