apish 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.ruby-gemset ADDED
@@ -0,0 +1 @@
1
+ gemdev
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 1.9.3-p392
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in apish.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 C. Jason Harrelson (midas)
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,32 @@
1
+ # Apish
2
+
3
+ Apish provides a set of tools to aid in API creation and management. These tools include but are not limited
4
+ to version maangement, responders and generators.
5
+
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ gem 'apish'
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install apish
20
+
21
+
22
+ ## Usage
23
+
24
+ ### Generators
25
+
26
+ Generate endpoint with controller, routing, and request specs (default view engine is Rabl):
27
+
28
+ rails g apish:endpoint api/v1/things index show create update
29
+
30
+ Generate endpoint with controller, routing, request and view specs (specifying view engine):
31
+
32
+ rails g apish:endpoint api/v1/things index show create update -t rabl --view_specs
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
3
+
4
+ require 'rspec/core/rake_task'
5
+ RSpec::Core::RakeTask.new('spec')
6
+ task :default => :spec
data/apish.gemspec ADDED
@@ -0,0 +1,25 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'apish/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.authors = ["C. Jason Harrelson (midas)"]
8
+ gem.email = ["jason@lookforwardenterprises.com"]
9
+ gem.description = %q{Apish provides a set of tools to aid in API creation and management.}
10
+ gem.summary = %q{Apish provides a set of tools to aid in API creation and management. These tools include but are not limited to version maangement and responders.}
11
+ gem.homepage = ""
12
+
13
+ gem.files = `git ls-files`.split($\)
14
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
15
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
16
+ gem.name = "apish"
17
+ gem.require_paths = ["lib"]
18
+ gem.version = Apish::VERSION
19
+
20
+ gem.add_development_dependency(%q<rspec>, ["~> 2"])
21
+
22
+ gem.add_dependency(%q<activesupport>, [">= 2"])
23
+ gem.add_dependency(%q<actionpack>, [">= 2"])
24
+ gem.add_dependency(%q<mime-types>, [">= 1"])
25
+ end
data/lib/apish.rb ADDED
@@ -0,0 +1,37 @@
1
+ #require 'apish/railtie' if defined?( Rails )
2
+
3
+ module Apish
4
+
5
+ autoload :ApiVersion, "apish/api_version"
6
+ autoload :Configuration, "apish/configuration"
7
+ autoload :Controller, "apish/controller"
8
+ autoload :Error, "apish/error"
9
+ autoload :FormatResolver, "apish/format_resolver"
10
+ autoload :Responder, "apish/responder"
11
+ autoload :VERSION, "apish/version"
12
+
13
+ def self.configuration
14
+ @configuration ||= Configuration.new
15
+ end
16
+
17
+ def self.configure
18
+ yield(configuration) if block_given?
19
+ end
20
+
21
+ def self.register_custom_mime_types
22
+ dir = File.join( Rails.root, 'app', 'controllers', 'api' )
23
+ Dir.glob( "#{dir}/v*" ) do |d|
24
+ version = d[/(v\d+)$/, 1]
25
+ types = %w( json xml ).collect do |wire_format|
26
+ "api_#{version}_#{wire_format}"
27
+ end
28
+ types.each do |type|
29
+ mime_type = ["application/vnd.#{Apish.configuration.mime_types_base}",
30
+ type.split( '_' )[1..2].join( '+' )].join( '-' )
31
+ next if Mime.const_defined?( type.upcase )
32
+ Mime::Type.register mime_type, type.to_sym
33
+ end
34
+ end
35
+ end
36
+
37
+ end
@@ -0,0 +1,42 @@
1
+ require 'active_support/core_ext/object/try'
2
+
3
+ class Apish::ApiVersion
4
+
5
+ attr_reader :accepts
6
+
7
+ def initialize( accepts )
8
+ @accepts = accepts
9
+ end
10
+
11
+ def to_s
12
+ parse
13
+ end
14
+
15
+ def to_i
16
+ parse.try( :to_i ) || 0
17
+ end
18
+
19
+ def max
20
+ 1
21
+ end
22
+
23
+ def exists?
24
+ to_i <= max
25
+ end
26
+
27
+ def most_recent?
28
+ to_i == max
29
+ end
30
+
31
+ def self.version_regex
32
+ /application\/vnd.#{Apish.configuration.mime_types_base}-v(\d+)\+\w*/
33
+ end
34
+
35
+ private
36
+
37
+ def parse
38
+ matches = accepts.match( self.class.version_regex )
39
+ matches.try :[], 1
40
+ end
41
+
42
+ end
@@ -0,0 +1,10 @@
1
+ module Apish
2
+
3
+ class Configuration
4
+
5
+ attr_accessor :mime_types_base,
6
+ :representation_framework
7
+
8
+ end
9
+
10
+ end
@@ -0,0 +1,74 @@
1
+ require 'active_support/concern'
2
+
3
+ module Apish::Controller
4
+
5
+ autoload :CheckFormat, 'apish/controller/check_format'
6
+ autoload :Rescue, 'apish/controller/rescue'
7
+
8
+ extend ActiveSupport::Concern
9
+
10
+ module ClassMethods
11
+
12
+ # Adds common API controller functionality.
13
+ #
14
+ def acts_as_api_controller
15
+ base = self.name.split( '::' )[0..1].join( '_' ).downcase
16
+ json = "#{base}_json"
17
+ xml = "#{base}_xml"
18
+ types = [json, xml]
19
+
20
+ formats = { Mime::const_get( json.upcase ) => :json,
21
+ Mime::const_get( xml.upcase ) => :xml }
22
+
23
+ class_eval do
24
+ def self.supported_formats
25
+ Hash[*(self.mimes_for_respond_to.keys.map { |f| [Mime::Type.lookup_by_extension(f), f.to_s[/^api_v\d+_(.+)$/, 1]] }.flatten)]
26
+ end
27
+ end
28
+
29
+ # Set up custom mime types that this controller will respond to
30
+ respond_to( *formats.keys.map( &:to_sym ) )
31
+
32
+ # Define renderers for each mime type
33
+ formats.each do |mime_type, format|
34
+ if Apish.configuration.representation_framework == :rabl
35
+ ActionController.add_renderer mime_type.to_sym do |object, options|
36
+ templates = options[:prefixes].map { |path| File.join( path, options[:template] ) }.select { |path| File.exists?( File.join( Rails.root, 'app', 'views', "#{path}.rabl" )) }
37
+ # Do not try to render if no templates were found
38
+ unless templates.empty?
39
+ self.content_type ||= mime_type
40
+ self.response_body = Rabl::Renderer.send( format, object, templates.first, :view_path => 'app/views', :scope => self )
41
+ end
42
+ end
43
+ end
44
+ end
45
+
46
+ # Define #to_* methods for each custom mime type (in order to get free
47
+ # format.custom_mime_type { render :custom_mime_type => @object } functionality)
48
+ types.each do |type|
49
+ method_name = "to_#{type}"
50
+
51
+ unless ActionController::Responder.method_defined?( method_name )
52
+ ActionController::Responder.class_eval do
53
+ define_method method_name do
54
+ # if the default_response Proc is from the current controller, then it is an override
55
+ #controller_provides_response = @default_response.binding.eval( "self.class" ) == controller.class
56
+ controller_provides_response = @default_response.file_colon_line.split( ':' ).first.gsub( /app\/controllers\/|.rb/, '' ).classify == controller.class.name
57
+ if controller_provides_response
58
+ # allow overridden responses in respond_with blocks
59
+ @default_response.call( options )
60
+ else
61
+ collection_name = controller.class.name.gsub( /Controller/, '' ).gsub( /Api::V\d+::/, '' ).tableize
62
+ object_name = collection_name.singularize
63
+ object = controller.instance_variable_get( "@#{collection_name}" ) || controller.instance_variable_get( "@#{object_name}" )
64
+ controller.render type.to_sym => object
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
71
+
72
+ end
73
+
74
+ end
@@ -0,0 +1,27 @@
1
+ module Apish::Controller::CheckFormat
2
+
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ before_filter :check_format
7
+ end
8
+
9
+ protected
10
+
11
+ def check_format
12
+ unless self.class.supported_formats.key?( request.format )
13
+ respond_with_unsupported_format
14
+ end
15
+ end
16
+
17
+ def respond_with_unsupported_format
18
+ render :status => :not_found,
19
+ :text => unsupported_text
20
+ end
21
+
22
+ def unsupported_text
23
+ return "You must provide a format for #{request.path.split( '.' ).first.inspect}" if params[:format].blank?
24
+ "Format #{params[:format].inspect} not supported for #{request.path.split( '.' ).first.inspect}"
25
+ end
26
+
27
+ end
@@ -0,0 +1,64 @@
1
+ module Apish::Controller::Rescue
2
+
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ rescue_from Exception, :with => :handle_exception
7
+ end
8
+
9
+ module ClassMethods
10
+
11
+ def respond_with_detail_exceptions
12
+ @@respond_with_detail_exceptions ||= {}
13
+ end
14
+
15
+ def rescue_from_with_details( exception_class, status_code_or_symbol )
16
+ respond_with_detail_exceptions[exception_class] = status_code_or_symbol
17
+ end
18
+
19
+ end
20
+
21
+ protected
22
+
23
+ def handle_exception( ex )
24
+ respond_to do |format|
25
+ format.html do
26
+ rescue_action_without_handler( ex )
27
+ end
28
+ format.any do
29
+ log_error( ex )
30
+ erase_results if performed?
31
+ if ex.respond_to?( :handle_response! )
32
+ ex.handle_response!( response )
33
+ end
34
+
35
+ if self.class.respond_with_detail_exceptions.key?( ex.class )
36
+ render :status => self.class.respond_with_detail_exceptions[ex.class],
37
+ :text => Api::Error.new( ex ).send( to_method_name )
38
+ else
39
+ render :status => :internal_server_error,
40
+ :text => Api::Error.new( 'Internal server error' ).send( to_method_name )
41
+ end
42
+ end
43
+ end
44
+ end
45
+
46
+ def log_error( ex )
47
+ return unless Rails.logger
48
+ Rails.logger.error "\n#{ex.class} (#{ex.message}):\n #{Rails.backtrace_cleaner.clean(ex.backtrace).join("\n ")}"
49
+ end
50
+
51
+ def to_method_name
52
+ return 'to_json' if json_variant?
53
+ return 'to_xml' if xml_variant?
54
+ end
55
+
56
+ def json_variant?
57
+ request.format.to_s.include? 'json'
58
+ end
59
+
60
+ def xml_variant?
61
+ request.format.to_s.include? 'xml'
62
+ end
63
+
64
+ end
@@ -0,0 +1,36 @@
1
+ module Apish::Error
2
+
3
+ attr_reader :message, :type
4
+
5
+ def initialize( message_or_error )
6
+ if message_or_error.is_a?( Exception )
7
+ @message = message_or_error.message
8
+ @type = message_or_error.class.name
9
+ else
10
+ @message = message_or_error
11
+ @type = 'Exception'
12
+ end
13
+ end
14
+
15
+ def to_json
16
+ { root => body }.to_json
17
+ end
18
+
19
+ def to_xml
20
+ body.to_xml :root => root
21
+ end
22
+
23
+ protected
24
+
25
+ def body
26
+ {
27
+ :message => message,
28
+ :type => type
29
+ }
30
+ end
31
+
32
+ def root
33
+ :error
34
+ end
35
+
36
+ end
@@ -0,0 +1,35 @@
1
+ require 'active_support/core_ext/object/try'
2
+ require 'action_dispatch/http/mime_type'
3
+ require 'action_dispatch/http/mime_types'
4
+
5
+ class Apish::FormatResolver
6
+
7
+ attr_reader :accept,
8
+ :current_format
9
+
10
+ def initialize( accept, current_format=nil )
11
+ @accept = accept || ''
12
+ @current_format = current_format
13
+ end
14
+
15
+ def format
16
+ if current_format == Mime::ALL || current_format.to_s.include?( "application/vnd.#{Apish.configuration.mime_types_base}-v" )
17
+ matches = accept.match( self.class.format_regex )
18
+ mime_type_str = matches.nil? ? current_format : matches[1]
19
+ mime_type = Mime[mime_type_str]
20
+ return mime_type.symbol.to_s unless mime_type.blank?
21
+ elsif current_format.nil?
22
+ matches = accept.match( self.class.format_regex )
23
+ mime_type_str = matches.nil? ? current_format : matches[1]
24
+ mime_type = Mime[mime_type_str]
25
+ return mime_type.symbol.to_s unless mime_type.blank?
26
+ end
27
+
28
+ current_format.try( :symbol ).try :to_s
29
+ end
30
+
31
+ def self.format_regex
32
+ /application\/vnd.#{Apish.configuration.mime_types_base}-v\d+\+(\w*)/
33
+ end
34
+
35
+ end
@@ -0,0 +1,6 @@
1
+ module Apish::Responder
2
+
3
+ autoload :Pagination, "apish/responder/pagination"
4
+ autoload :Version, "apish/responder/version"
5
+
6
+ end
@@ -0,0 +1,24 @@
1
+ module Apish::Responder::Pagination
2
+
3
+ def respond(*args)
4
+ if paginated?
5
+ controller.headers['X-Total-Entries'] = resource.total_entries.to_s
6
+ controller.headers['X-Total-Pages'] = resource.total_pages.to_s
7
+ controller.headers['X-Current-Page'] = resource.current_page.to_s
8
+ controller.headers['X-Next-Page'] = resource.next_page.to_s
9
+ controller.headers['X-Previous-Page'] = resource.previous_page.to_s
10
+ controller.headers['X-Per-Page'] = resource.per_page.to_s
11
+ end
12
+
13
+ super( *args )
14
+ end
15
+
16
+ private
17
+
18
+ def paginated?
19
+ resource.respond_to?(:total_entries) &&
20
+ resource.respond_to?(:total_pages) &&
21
+ resource.respond_to?(:per_page)
22
+ end
23
+
24
+ end
@@ -0,0 +1,15 @@
1
+ module Apish::Responder::Version
2
+
3
+ def respond( *args )
4
+ controller.headers['X-Api-Version'] = controller.class.name.match( /^Api::V(\d*)::.*$/ ).try( :[], 1 )
5
+
6
+ super( *args )
7
+ end
8
+
9
+ protected
10
+
11
+ def accept
12
+ request.headers['Accept']
13
+ end
14
+
15
+ end
@@ -0,0 +1,3 @@
1
+ module Apish
2
+ VERSION = "0.9.0"
3
+ end
@@ -0,0 +1,131 @@
1
+ require 'rails/generators'
2
+
3
+ module Apish
4
+
5
+ class EndpointGenerator < Rails::Generators::Base
6
+
7
+ attr_reader :action_name,
8
+ :controller_super_class_name
9
+
10
+ desc "Description:\n Creates the specified API endpoint with all spec files, etc."
11
+
12
+ argument :endpoint_name, :type => :string, :required => true
13
+ argument :action_names, :type => :array, :required => false
14
+
15
+ class_option :view_engine, :type => :string, :aliases => "-t", :desc => "Template engine for the views. Available options are 'rabl'.", :default => "rabl"
16
+ class_option :view_specs, :type => :boolean, :default => false
17
+
18
+ def self.source_root
19
+ File.join File.dirname(__FILE__),
20
+ 'templates'
21
+ end
22
+
23
+ def install_controller
24
+ @controller_super_class_name = ask( 'What is the class name (without namespace) of the versioned super class for this API endpoints controller (press enter for ApiController)?' )
25
+ @controller_super_class_name = default_controller_super_class_name if @controller_super_class_name.blank?
26
+ template "controller.erb", File.join( 'app', 'controllers', "#{controller_file_name}.rb" )
27
+ end
28
+
29
+ def install_controller_spec
30
+ template "controller_spec.erb", File.join( 'spec', 'controllers', "#{controller_spec_file_name}.rb" )
31
+ end
32
+
33
+ def install_routing_spec
34
+ template "routing_spec.erb", File.join( 'spec', 'routing', "#{routing_spec_file_name}.rb" )
35
+ end
36
+
37
+ def install_request_specs
38
+ return if action_names.blank?
39
+
40
+ basename = ask( 'What base name would you like for the request_spec(s) (press enter for authenticated_request)?' )
41
+ basename = default_request_spec_base_name if basename.blank?
42
+
43
+ action_names.each do |action_name|
44
+ @action_name = action_name
45
+ template "request_spec.erb", File.join( 'spec', 'requests', "#{request_spec_file_name( action_name, "#{basename}_spec" )}.rb" )
46
+ end
47
+ end
48
+
49
+ def install_views
50
+ return if action_names.blank?
51
+
52
+ action_names.each do |action_name|
53
+ create_file File.join( 'app', 'views', view_file_name( action_name ))
54
+ end
55
+ end
56
+
57
+ def install_view_specs
58
+ return if action_names.blank? || options[:view_specs] == false
59
+
60
+ action_names.each do |action_name|
61
+ template "view_spec.erb", File.join( 'spec', 'views', "#{view_spec_file_name( action_name )}.rb" )
62
+ end
63
+ end
64
+
65
+ private
66
+
67
+ def default_controller_super_class_name
68
+ 'ApiController'
69
+ end
70
+
71
+ def default_request_spec_base_name
72
+ 'authenticated_request'
73
+ end
74
+
75
+ def view_file_name( action_name )
76
+ [versioned_endpoint_human_name, "#{action_name}.#{view_file_extension}"].join( '/' )
77
+ end
78
+
79
+ def view_spec_file_name( action_name )
80
+ [versioned_endpoint_human_name, "#{action_name}.#{view_file_extension}_spec"].join( '/' )
81
+ end
82
+
83
+ def view_file_extension
84
+ options[:view_engine]
85
+ end
86
+
87
+ # api/v1/blocks
88
+ def versioned_endpoint_human_name
89
+ endpoint_name.underscore
90
+ end
91
+
92
+ # blocks
93
+ def endpoint_human_name
94
+ human_name.split( '/' ).last
95
+ end
96
+
97
+ # api/v1/blocks_controller
98
+ def controller_file_name
99
+ [endpoint_name.underscore, '_controller'].join
100
+ end
101
+
102
+ # api/v1/blocks_controller_spec
103
+ def controller_spec_file_name
104
+ [controller_file_name, '_spec'].join
105
+ end
106
+
107
+ # api/v1/blocks_routing_spec
108
+ def routing_spec_file_name
109
+ [endpoint_name.underscore, '_routing_spec'].join
110
+ end
111
+
112
+ # api/v1/blocks/create/something_spec
113
+ def request_spec_file_name( action_name, filename )
114
+ [endpoint_name.underscore, action_name, filename].join( '/' )
115
+ end
116
+
117
+ def endpoint_controller_class
118
+ [endpoint_name.camelcase, 'Controller'].join
119
+ end
120
+
121
+ def endpoint_namespace
122
+ endpoint_controller_class.split( '::' )[0...-1].join( '::' )
123
+ end
124
+
125
+ def endpoint_versioned_base_controller_name( class_name='ApiController' )
126
+ [endpoint_namespace, class_name].join( '::' )
127
+ end
128
+
129
+ end
130
+
131
+ end
@@ -0,0 +1,3 @@
1
+ class <%= endpoint_controller_class %> < <%= endpoint_versioned_base_controller_name( controller_super_class_name ) %>
2
+
3
+ end
@@ -0,0 +1,5 @@
1
+ require 'spec_helper'
2
+
3
+ describe <%= endpoint_controller_class %> do
4
+
5
+ end
@@ -0,0 +1,5 @@
1
+ require 'spec_helper'
2
+
3
+ describe "<%= versioned_endpoint_human_name %> API <%= action_name %>", :type =>:api do
4
+
5
+ end
@@ -0,0 +1,5 @@
1
+ require 'spec_helper'
2
+
3
+ describe '<%= versioned_endpoint_human_name %> routing' do
4
+
5
+ end
@@ -0,0 +1,5 @@
1
+ require 'spec_helper'
2
+
3
+ describe '<%= view_file_name( action_name )%>' do
4
+
5
+ end
@@ -0,0 +1,103 @@
1
+ require 'spec_helper'
2
+
3
+ describe Apish::ApiVersion do
4
+
5
+ before :each do
6
+ configure_apish
7
+ end
8
+
9
+ let :v1_accepts do
10
+ 'application/vnd.some-app-v1+json'
11
+ end
12
+
13
+ let :v2_accepts do
14
+ 'application/vnd.some-app-v2+json'
15
+ end
16
+
17
+ describe "#max" do
18
+
19
+ subject do
20
+ described_class.new( v1_accepts ).max
21
+ end
22
+
23
+ it { should == 1 }
24
+
25
+ end
26
+
27
+ describe "#to_s" do
28
+
29
+ subject do
30
+ described_class.new( v1_accepts ).to_s
31
+ end
32
+
33
+ it { should == '1' }
34
+
35
+ end
36
+
37
+ describe "#to_i" do
38
+
39
+ subject do
40
+ described_class.new( v2_accepts ).to_i
41
+ end
42
+
43
+ it { should == 2 }
44
+
45
+ end
46
+
47
+ describe "#exists?" do
48
+
49
+ context 'when the verison is 1' do
50
+
51
+ subject do
52
+ described_class.new( v1_accepts ).exists?
53
+ end
54
+
55
+ it { should be_true }
56
+
57
+ end
58
+
59
+ context 'when the verison is 2' do
60
+
61
+ subject do
62
+ described_class.new( v2_accepts ).exists?
63
+ end
64
+
65
+ it { should be_false }
66
+
67
+ end
68
+
69
+ end
70
+
71
+ describe "#most_recent?" do
72
+
73
+ before :each do
74
+ version.stub!( :max ).and_return 2
75
+ end
76
+
77
+ subject do
78
+ version.most_recent?
79
+ end
80
+
81
+ context 'when the verison is 1' do
82
+
83
+ let :version do
84
+ described_class.new v1_accepts
85
+ end
86
+
87
+ it { should be_false }
88
+
89
+ end
90
+
91
+ context 'when the verison is 2' do
92
+
93
+ let :version do
94
+ described_class.new v2_accepts
95
+ end
96
+
97
+ it { should be_true }
98
+
99
+ end
100
+
101
+ end
102
+
103
+ end
@@ -0,0 +1,106 @@
1
+ require 'spec_helper'
2
+
3
+ describe Apish::FormatResolver do
4
+
5
+ before :each do
6
+ configure_apish
7
+ end
8
+
9
+ let :no_format_accept do
10
+ nil
11
+ end
12
+
13
+ let :json_format_accept do
14
+ 'application/vnd.some-app-v1+json'
15
+ end
16
+
17
+ describe "#format" do
18
+
19
+ context 'when a format is provided but no current format is provided' do
20
+
21
+ subject do
22
+ described_class.new( json_format_accept ).format
23
+ end
24
+
25
+ it { should be_a( String ) }
26
+
27
+ it { should == Mime::JSON.symbol.to_s }
28
+
29
+ end
30
+
31
+ context 'when the format is not set from the URL extension' do
32
+
33
+ context 'and the format is provided in the accept header' do
34
+
35
+ subject do
36
+ described_class.new( json_format_accept,
37
+ Mime::ALL ).format
38
+ end
39
+
40
+ it { should be_a( String ) }
41
+
42
+ it { should == Mime::JSON.symbol.to_s }
43
+
44
+ end
45
+
46
+ context 'and the format is not provided in the accept header' do
47
+
48
+ subject do
49
+ described_class.new( no_format_accept,
50
+ Mime::ALL ).format
51
+ end
52
+
53
+ it { should be_a( String ) }
54
+
55
+ it { should == Mime::ALL.symbol.to_s }
56
+
57
+ end
58
+
59
+ end
60
+
61
+ context 'when the format is set from the URL extension' do
62
+
63
+ context 'and the format is provided in the accept header' do
64
+
65
+ subject do
66
+ described_class.new( json_format_accept,
67
+ Mime::XML ).format
68
+ end
69
+
70
+ it { should be_a( String ) }
71
+
72
+ it { should == Mime::XML.symbol.to_s }
73
+
74
+ end
75
+
76
+ context 'and the format is not provided in the accept header' do
77
+
78
+ subject do
79
+ described_class.new( no_format_accept,
80
+ Mime::XML ).format
81
+ end
82
+
83
+ it { should be_a( String ) }
84
+
85
+ it { should == Mime::XML.symbol.to_s }
86
+
87
+ end
88
+
89
+ end
90
+
91
+ context 'when the format is incorrectly set from the vnd.* header' do
92
+
93
+ subject do
94
+ described_class.new( json_format_accept,
95
+ Mime::Type.new( 'application/vnd.some-app-v1+json' ) ).format
96
+ end
97
+
98
+ it { should be_a( String ) }
99
+
100
+ it { should == Mime::JSON.symbol.to_s }
101
+
102
+ end
103
+
104
+ end
105
+
106
+ end
@@ -0,0 +1,17 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+ require 'apish'
4
+
5
+ RSpec.configure do |config|
6
+
7
+ end
8
+
9
+ def mime_types_base
10
+ 'some-app'
11
+ end
12
+
13
+ def configure_apish
14
+ Apish.configure do |config|
15
+ config.mime_types_base = mime_types_base
16
+ end
17
+ end
metadata ADDED
@@ -0,0 +1,142 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: apish
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.9.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - C. Jason Harrelson (midas)
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-08-08 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rspec
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: '2'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: '2'
30
+ - !ruby/object:Gem::Dependency
31
+ name: activesupport
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '2'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '2'
46
+ - !ruby/object:Gem::Dependency
47
+ name: actionpack
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '2'
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '2'
62
+ - !ruby/object:Gem::Dependency
63
+ name: mime-types
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '1'
70
+ type: :runtime
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '1'
78
+ description: Apish provides a set of tools to aid in API creation and management.
79
+ email:
80
+ - jason@lookforwardenterprises.com
81
+ executables: []
82
+ extensions: []
83
+ extra_rdoc_files: []
84
+ files:
85
+ - .gitignore
86
+ - .ruby-gemset
87
+ - .ruby-version
88
+ - Gemfile
89
+ - LICENSE
90
+ - README.md
91
+ - Rakefile
92
+ - apish.gemspec
93
+ - lib/apish.rb
94
+ - lib/apish/api_version.rb
95
+ - lib/apish/configuration.rb
96
+ - lib/apish/controller.rb
97
+ - lib/apish/controller/check_format.rb
98
+ - lib/apish/controller/rescue.rb
99
+ - lib/apish/error.rb
100
+ - lib/apish/format_resolver.rb
101
+ - lib/apish/responder.rb
102
+ - lib/apish/responder/pagination.rb
103
+ - lib/apish/responder/version.rb
104
+ - lib/apish/version.rb
105
+ - lib/generators/apish/endpoint/endpoint_generator.rb
106
+ - lib/generators/apish/endpoint/templates/controller.erb
107
+ - lib/generators/apish/endpoint/templates/controller_spec.erb
108
+ - lib/generators/apish/endpoint/templates/request_spec.erb
109
+ - lib/generators/apish/endpoint/templates/routing_spec.erb
110
+ - lib/generators/apish/endpoint/templates/view_spec.erb
111
+ - spec/lib/apish/api_version_spec.rb
112
+ - spec/lib/apish/format_resolver_spec.rb
113
+ - spec/spec_helper.rb
114
+ homepage: ''
115
+ licenses: []
116
+ post_install_message:
117
+ rdoc_options: []
118
+ require_paths:
119
+ - lib
120
+ required_ruby_version: !ruby/object:Gem::Requirement
121
+ none: false
122
+ requirements:
123
+ - - ! '>='
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
126
+ required_rubygems_version: !ruby/object:Gem::Requirement
127
+ none: false
128
+ requirements:
129
+ - - ! '>='
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ requirements: []
133
+ rubyforge_project:
134
+ rubygems_version: 1.8.25
135
+ signing_key:
136
+ specification_version: 3
137
+ summary: Apish provides a set of tools to aid in API creation and management. These
138
+ tools include but are not limited to version maangement and responders.
139
+ test_files:
140
+ - spec/lib/apish/api_version_spec.rb
141
+ - spec/lib/apish/format_resolver_spec.rb
142
+ - spec/spec_helper.rb