biceps 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/lib/biceps.rb CHANGED
@@ -1,5 +1,11 @@
1
- require 'biceps/routing'
1
+ require 'biceps/core_ext/action_dispatch/routing/mapper'
2
+ require 'biceps/core_ext/action_controller/base'
2
3
 
3
4
  module Biceps
4
- autoload :ApiVersion, 'biceps/api_version'
5
+ autoload :ApiVersion, 'biceps/api_version'
6
+ autoload :ModelVersioning, 'biceps/model_versioning'
7
+ autoload :Jsonp, 'biceps/jsonp'
8
+ autoload :Serializer, 'biceps/serializer'
9
+ autoload :Parser, 'biceps/parser'
10
+ autoload :Builder, 'biceps/builder'
5
11
  end
@@ -1,36 +1,13 @@
1
1
  module Biceps
2
2
  class ApiVersion
3
- attr_accessor :version, :accept
3
+ attr_accessor :version
4
4
 
5
5
  def initialize(version)
6
6
  @version = version
7
7
  end
8
8
 
9
9
  def matches?(request)
10
- @accept = request.accept
11
- valid_api_version?
12
- end
13
-
14
-
15
- private
16
- def valid_api_version?
17
- request_version == version
18
- end
19
-
20
- def request_version
21
- is_api_call?[1].to_i if is_api_call?
22
- end
23
-
24
- def is_api_call?
25
- @is_api_call ||= accept.match(regex)
26
- end
27
-
28
- def regex
29
- Regexp.new("application/vnd.#{app_name};ver=([0-9]+)")
30
- end
31
-
32
- def app_name
33
- Rails.application.class.to_s.split('::').first.underscore
10
+ Biceps::Parser.new(request, version).valid?
34
11
  end
35
12
  end
36
13
  end
@@ -0,0 +1,32 @@
1
+ module Biceps
2
+ module Builder
3
+
4
+ # instiantiate_for_api(User, params[:user]) == Constructor.new(User,params[:user], 1)
5
+ def self.new(resource, params, version)
6
+ begin
7
+ constant = "Builders::#{resource}::V#{version}".constantize
8
+ instance = constant.new(resource.new, params).resource
9
+ rescue NameError
10
+ #
11
+ # The builder for that version is not defined
12
+ # Use the model's internal initialize
13
+ instance = resource.new params
14
+ end
15
+
16
+ instance
17
+ end
18
+
19
+ class Base
20
+ attr_reader :resource, :params
21
+
22
+ extend ActiveModel::Callbacks
23
+ define_model_callbacks :initialize, :only => :after
24
+
25
+ def initialize(resource, params)
26
+ run_callbacks :initialize do
27
+ @resource, @params = resource, params
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,7 @@
1
+ ActionController::Base.class_eval do
2
+
3
+ def initialize_for_api(resource, params)
4
+ api_version = Biceps::Parser.new(request).version
5
+ Biceps::Builder.new(resource, params, api_version)
6
+ end
7
+ end
@@ -1,3 +1,4 @@
1
+ require 'action_controller'
1
2
  #
2
3
  # Monkey Patching routes to add the `api_version` method
3
4
  # This allows us to define both the namespace and the constraint all together
@@ -5,9 +6,14 @@
5
6
  module ActionDispatch::Routing
6
7
  class Mapper
7
8
 
8
- def api_version(version)
9
+ def api_version(version, opts={})
10
+ if opts[:module] && opts[:module].empty?
11
+ scope_module = nil
12
+ else
13
+ scope_module = opts[:module] || "v#{version}"
14
+ end
9
15
 
10
- scope :module => "v#{version}" do
16
+ scope :module => scope_module do
11
17
  constraints Biceps::ApiVersion.new(version) do
12
18
  yield if block_given?
13
19
  end
@@ -0,0 +1,67 @@
1
+ module Biceps
2
+ class Jsonp
3
+ attr_reader :app, :callback_param
4
+
5
+ def initialize(app, options = {})
6
+ @app = app
7
+ @callback_param = options[:callback_param] || 'callback'
8
+ end
9
+
10
+ # Proxies the request to the application, stripping out the JSON-P callback
11
+ # method and padding the response with the appropriate callback format.
12
+ #
13
+ # Changes nothing if no <tt>callback</tt> param is specified.
14
+ #
15
+ def call(env)
16
+ Parser.new(self, env).perform
17
+ end
18
+
19
+ class Parser
20
+ attr_reader :jsonp, :env, :request
21
+ attr_accessor :status, :headers, :response
22
+
23
+ delegate :callback_param, :to => :jsonp
24
+ delegate :app, :to => :jsonp
25
+
26
+ def initialize(jsonp, env)
27
+ @jsonp, @env = jsonp, env
28
+ @request = Rack::Request.new(@env)
29
+ @status, @headers, @response = app.call(env)
30
+ end
31
+
32
+ def perform
33
+ if callback && headers['Content-Type'] =~ /json/i
34
+ @response = pad
35
+ headers['Content-Length'] = response.first.length.to_s
36
+ headers['Content-Type'] = 'application/javascript'
37
+ end
38
+ [status, headers, response]
39
+ end
40
+
41
+ private
42
+ def callback
43
+ @callback ||= request.params.delete(callback_param)
44
+ end
45
+
46
+ # Pads the response with the appropriate callback format according to the
47
+ # JSON-P spec/requirements.
48
+ #
49
+ # The Rack response spec indicates that it should be enumerable. The method
50
+ # of combining all of the data into a single string makes sense since JSON
51
+ # is returned as a full string.
52
+ #
53
+ def pad(body = "")
54
+ response.each {|s| body << s }
55
+ ["#{callback}({'metadata': #{metadata}, 'data': #{body}})"]
56
+ end
57
+
58
+ def metadata
59
+ # TODO : add all the non-core HTTP headers
60
+ {
61
+ "status" => status
62
+ }.to_json
63
+ end
64
+
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,33 @@
1
+ module Biceps
2
+ module ModelVersioning
3
+
4
+ def self.included(klass)
5
+ klass.class_eval do
6
+
7
+ protected
8
+ alias :old_api_behavior :api_behavior
9
+ def api_behavior(error)
10
+ if api_version
11
+ begin
12
+ constant = "Serializers::#{resource.class}::V#{api_version}".constantize
13
+
14
+ @resource = constant.new(resource)
15
+ rescue NameError
16
+ #
17
+ # The serializer for that version is not defined
18
+ # Use the model's internal serialize
19
+ #
20
+ end
21
+ end
22
+
23
+ old_api_behavior(error)
24
+ end
25
+
26
+ private
27
+ def api_version
28
+ Biceps::Parser.new(request).version
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,31 @@
1
+ module Biceps
2
+ class Parser
3
+ attr_accessor :request, :valid_versions
4
+
5
+ def initialize(request, valid_versions=nil)
6
+ @request = request
7
+ @valid_versions = [valid_versions].flatten
8
+ end
9
+
10
+ def valid?
11
+ valid_versions.include?(version)
12
+ end
13
+
14
+ def version
15
+ is_api_call?[1].to_i if is_api_call?
16
+ end
17
+
18
+ private
19
+ def is_api_call?
20
+ request.accept.match(regex) if request.accept
21
+ end
22
+
23
+ def regex
24
+ Regexp.new("application/vnd.#{app_name};ver=([0-9]+)")
25
+ end
26
+
27
+ def app_name
28
+ Rails.application.class.to_s.split('::').first.underscore
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,22 @@
1
+ module Biceps
2
+ class Serializer
3
+ include ActiveModel::Serialization
4
+ include ActiveModel::Serializers::JSON
5
+ include ActiveModel::Serializers::Xml
6
+
7
+ attr_accessor :parent
8
+ delegate :respond_to?, :to => :parent
9
+
10
+ def initialize(parent)
11
+ @parent = parent
12
+ end
13
+
14
+ def method_missing(method, *args, &block)
15
+ if parent.respond_to?(method)
16
+ parent.send(method, *args, &block)
17
+ else
18
+ super
19
+ end
20
+ end
21
+ end
22
+ end
@@ -1,3 +1,3 @@
1
1
  module Biceps
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
metadata CHANGED
@@ -1,31 +1,33 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: biceps
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
8
- - Evome
9
- - Damien MATHIEU
8
+ - Damien Mathieu
10
9
  autorequire:
11
10
  bindir: bin
12
11
  cert_chain: []
13
- date: 2011-09-30 00:00:00.000000000Z
12
+ date: 2011-10-27 00:00:00.000000000Z
14
13
  dependencies: []
15
- description: Create a versionned API with rails
16
- email:
14
+ description: ! '[description]'
15
+ email: 42@dmathieu.com
17
16
  executables: []
18
17
  extensions: []
19
18
  extra_rdoc_files: []
20
19
  files:
20
+ - lib/biceps.rb
21
21
  - lib/biceps/api_version.rb
22
- - lib/biceps/routing.rb
22
+ - lib/biceps/builder.rb
23
+ - lib/biceps/core_ext/action_controller/base.rb
24
+ - lib/biceps/core_ext/action_dispatch/routing/mapper.rb
25
+ - lib/biceps/jsonp.rb
26
+ - lib/biceps/model_versioning.rb
27
+ - lib/biceps/parser.rb
28
+ - lib/biceps/serializer.rb
23
29
  - lib/biceps/version.rb
24
- - lib/biceps.rb
25
- - MIT-LICENSE
26
- - Rakefile
27
- - README.md
28
- homepage:
30
+ homepage: https://github.com//biceps
29
31
  licenses: []
30
32
  post_install_message:
31
33
  rdoc_options: []
@@ -44,9 +46,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
44
46
  - !ruby/object:Gem::Version
45
47
  version: '0'
46
48
  requirements: []
47
- rubyforge_project:
49
+ rubyforge_project: ! '[none]'
48
50
  rubygems_version: 1.8.10
49
51
  signing_key:
50
52
  specification_version: 3
51
- summary: Easy versionned API routing
53
+ summary: ! '[summary]'
52
54
  test_files: []
data/MIT-LICENSE DELETED
@@ -1,20 +0,0 @@
1
- Copyright 2011 Evome
2
-
3
- Permission is hereby granted, free of charge, to any person obtaining
4
- a copy of this software and associated documentation files (the
5
- "Software"), to deal in the Software without restriction, including
6
- without limitation the rights to use, copy, modify, merge, publish,
7
- distribute, sublicense, and/or sell copies of the Software, and to
8
- permit persons to whom the Software is furnished to do so, subject to
9
- the following conditions:
10
-
11
- The above copyright notice and this permission notice shall be
12
- included in all copies or substantial portions of the Software.
13
-
14
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
- LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
- OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
- WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md DELETED
@@ -1,89 +0,0 @@
1
- # Biceps
2
-
3
- Easily route your rails-based versionned API
4
- [![Travis](https://secure.travis-ci.org/evome/biceps.png)](http://travis-ci.org/evome/biceps)
5
-
6
- ## Installation
7
-
8
- Biceps heavily uses the convention over configuration principle.
9
- To install it, you just need to add it to your Gemfile.
10
-
11
- gem 'biceps'
12
-
13
- ## Defining routes
14
-
15
- Once Biceps is installed, you can start adding api-versionned routes.
16
- Your `config/routes.rb` file could look like the following :
17
-
18
- MyApp:Application.routes.draw do
19
- root :to => "home#index"
20
-
21
- api_version(1) do
22
- get '/me' => "users#show"
23
- end
24
-
25
- api_version(2) do
26
- get '/user' => "users#show"
27
- end
28
- end
29
-
30
- This will create two routes :
31
-
32
- GET /me(.:format) {:controller=>"v1/users", :action=>"show"}
33
- GET /user(.:format) {:controller=>"v2/users", :action=>"show"}
34
-
35
- As you can see in the routing, both are leading to different namespaces
36
- : v1 and v2.
37
- Both namespaces are the version of your API.
38
-
39
- ## Calling the API
40
-
41
- When you want to call the API, you need to specify the Accept header
42
- like this :
43
-
44
- application/json,application/vnd.my_app;ver=1
45
-
46
- When my_app is your application's name (based on the module name at
47
- `MyApp::Application`).
48
-
49
- Here is, for example, how you could do it with [faraday](https://github.com/technoweenie/faraday)
50
-
51
- connexion = Faraday.new(:url => 'http://api.yourapplication')
52
- connexion.get do |req|
53
- req.url '/me'
54
- req.headers['HTTP_ACCEPT'] = 'application/json, application/vnd.my_app;ver=1'
55
- req.params['access_token'] = 'xxx'
56
- end
57
-
58
- Or, with jQuery, we do it like this :
59
-
60
- $.ajaxSetup({
61
- accepts: {
62
- my_app: "application/json,application/vnd.my_app;ver=1"
63
- }
64
- });
65
-
66
- $.ajax({
67
- url: '/me'
68
- dataType: 'my_app'
69
- }).always(function(response) {
70
- json = JSON.parse(response.responseText)
71
- });
72
-
73
-
74
- ## Contributing
75
-
76
- We're open to any contribution. It has to be tested properly though.
77
-
78
- * [Fork](http://help.github.com/forking/) the project
79
- * Do your changes and commit them to your repository
80
- * Test your changes. We won't accept any untested contributions (except if they're not testable).
81
- * Create an [issue](https://github.com/evome/biceps/issues) with a link to your commits.
82
-
83
- ## Maintainers
84
-
85
- * Evome ([evome.fr](http://evome.fr))
86
- * Damien MATHIEU ([github/dmathieu](http://github.com/dmathieu), [dmathieu.com](http://dmathieu.com))
87
-
88
- ## License
89
- MIT License. Copyright 2010 Evome. http://evome.fr
data/Rakefile DELETED
@@ -1,24 +0,0 @@
1
- # encoding: UTF-8
2
- require 'rubygems'
3
- begin
4
- require 'bundler/setup'
5
- rescue LoadError
6
- puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
7
- end
8
-
9
- require 'rake'
10
- require 'rake/rdoctask'
11
-
12
-
13
- require 'rspec/core'
14
- require 'rspec/core/rake_task'
15
- task :default => :spec
16
- RSpec::Core::RakeTask.new(:spec)
17
-
18
- Rake::RDocTask.new(:rdoc) do |rdoc|
19
- rdoc.rdoc_dir = 'rdoc'
20
- rdoc.title = 'Oauned'
21
- rdoc.options << '--line-numbers' << '--inline-source'
22
- rdoc.rdoc_files.include('README.rdoc')
23
- rdoc.rdoc_files.include('lib/**/*.rb')
24
- end