raisin 0.0.2 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.md +8 -0
- data/lib/raisin/api/dsl.rb +177 -0
- data/lib/raisin/api.rb +29 -202
- data/lib/raisin/base.rb +5 -169
- data/lib/raisin/exposable.rb +7 -4
- data/lib/raisin/middleware.rb +31 -0
- data/lib/raisin/namespace.rb +2 -0
- data/lib/raisin/rails/request.rb +12 -1
- data/lib/raisin/rails/routes.rb +10 -2
- data/lib/raisin/railtie.rb +0 -3
- data/lib/raisin/router.rb +16 -51
- data/lib/raisin/version.rb +1 -1
- data/lib/raisin/version_constraint.rb +12 -0
- data/lib/raisin.rb +3 -3
- metadata +5 -4
- data/lib/raisin/middleware/base.rb +0 -13
- data/lib/raisin/middleware/header.rb +0 -39
    
        data/README.md
    CHANGED
    
    | @@ -150,6 +150,14 @@ Raisin.configure do |c| | |
| 150 150 | 
             
            end
         | 
| 151 151 | 
             
            ```
         | 
| 152 152 |  | 
| 153 | 
            +
            If you are using versionning via header, you also need to add a middleware to your application stack
         | 
| 154 | 
            +
             | 
| 155 | 
            +
            ```ruby
         | 
| 156 | 
            +
            #config/application.rb
         | 
| 157 | 
            +
             | 
| 158 | 
            +
            config.middleware.use Raisin::Middleware
         | 
| 159 | 
            +
            ```
         | 
| 160 | 
            +
             | 
| 153 161 | 
             
            ## Contributing
         | 
| 154 162 |  | 
| 155 163 | 
             
            1. Fork it
         | 
| @@ -0,0 +1,177 @@ | |
| 1 | 
            +
            module Raisin
         | 
| 2 | 
            +
              module DSL
         | 
| 3 | 
            +
                %w(get head post put delete).each do |via|
         | 
| 4 | 
            +
                  class_eval <<-EOF, __FILE__, __LINE__ + 1
         | 
| 5 | 
            +
                    def #{via}(path = '/', options = {}, &block)
         | 
| 6 | 
            +
                      path = normalize_path(path)
         | 
| 7 | 
            +
                      method_name = options.key?(:as) ? options[:as].to_s : extract_method_name(path, :#{via})
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                      klass = self.const_set method_name.camelize, Class.new(@_klass, &block)
         | 
| 10 | 
            +
                      
         | 
| 11 | 
            +
                      if current_namespace && current_namespace.expose?
         | 
| 12 | 
            +
                        current_namespace.exposures.each do |name, b|
         | 
| 13 | 
            +
                          klass.send(:expose, name, &b)
         | 
| 14 | 
            +
                        end
         | 
| 15 | 
            +
                      end
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                      current_namespace.add(method_name) if current_namespace
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                      routes << [:#{via}, path, default_route(method_name)]
         | 
| 20 | 
            +
                    end
         | 
| 21 | 
            +
                  EOF
         | 
| 22 | 
            +
                end
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                def included(&block)
         | 
| 25 | 
            +
                  self.action_klass.class_eval(&block) if block_given?
         | 
| 26 | 
            +
                end
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                def member(&block)
         | 
| 29 | 
            +
                  namespace(':id') do
         | 
| 30 | 
            +
                    resource = self.api_name.singularize
         | 
| 31 | 
            +
                    expose(resource) { resource.camelize.constantize.send :find, params[:id] }
         | 
| 32 | 
            +
                    instance_eval(&block)
         | 
| 33 | 
            +
                  end
         | 
| 34 | 
            +
                end
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                def nested_into_resource(parent)
         | 
| 37 | 
            +
                  parent = parent.to_s
         | 
| 38 | 
            +
                  sing = parent.singularize
         | 
| 39 | 
            +
                  id = "#{sing}_id"
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                  @_namespaces << Namespace.new("#{parent}/:#{id}")
         | 
| 42 | 
            +
                  current_namespace.expose(sing) { sing.camelize.constantize.send :find, params[id.to_sym]}
         | 
| 43 | 
            +
                  @_namespaces << Namespace.new(@_prefix)
         | 
| 44 | 
            +
                  @_prefix = nil
         | 
| 45 | 
            +
                end
         | 
| 46 | 
            +
             | 
| 47 | 
            +
                def single_resource
         | 
| 48 | 
            +
                  @_single_resource = true
         | 
| 49 | 
            +
                  @_prefix = @_prefix.singularize if prefix?
         | 
| 50 | 
            +
                end
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                def prefix(prefix)
         | 
| 53 | 
            +
                  @_prefix = prefix
         | 
| 54 | 
            +
                end
         | 
| 55 | 
            +
             | 
| 56 | 
            +
                def description(desc)
         | 
| 57 | 
            +
                  # noop
         | 
| 58 | 
            +
                end
         | 
| 59 | 
            +
             | 
| 60 | 
            +
                def expose(*args, &block)
         | 
| 61 | 
            +
                  current_namespace.expose(*args, &block)
         | 
| 62 | 
            +
                end
         | 
| 63 | 
            +
             | 
| 64 | 
            +
                def namespace(path, &block)
         | 
| 65 | 
            +
                  path = path.sub(%r(\A/?#{@_prefix}), '') if prefix?
         | 
| 66 | 
            +
                  @_namespaces.push Namespace.new(path)
         | 
| 67 | 
            +
                  yield
         | 
| 68 | 
            +
                  process_filters
         | 
| 69 | 
            +
                  @_namespaces.pop
         | 
| 70 | 
            +
                end
         | 
| 71 | 
            +
             | 
| 72 | 
            +
                %w(before around after).each do |type|
         | 
| 73 | 
            +
                  class_eval <<-EOF, __FILE__, __LINE__ + 1
         | 
| 74 | 
            +
                    def #{type}(*args, &block)
         | 
| 75 | 
            +
                      return unless current_namespace
         | 
| 76 | 
            +
                      current_namespace.filter(:#{type}, args, &block)
         | 
| 77 | 
            +
                    end
         | 
| 78 | 
            +
                  EOF
         | 
| 79 | 
            +
                end
         | 
| 80 | 
            +
             | 
| 81 | 
            +
                protected
         | 
| 82 | 
            +
             | 
| 83 | 
            +
                def prefix?
         | 
| 84 | 
            +
                  !!@_prefix
         | 
| 85 | 
            +
                end
         | 
| 86 | 
            +
             | 
| 87 | 
            +
                def single_resource?
         | 
| 88 | 
            +
                  !!@_single_resource
         | 
| 89 | 
            +
                end
         | 
| 90 | 
            +
             | 
| 91 | 
            +
                def current_namespace
         | 
| 92 | 
            +
                  @_namespaces.at(0)
         | 
| 93 | 
            +
                end
         | 
| 94 | 
            +
             | 
| 95 | 
            +
                def current_namespace?
         | 
| 96 | 
            +
                  @_namespaces.length > 0
         | 
| 97 | 
            +
                end
         | 
| 98 | 
            +
             | 
| 99 | 
            +
                def process_filters
         | 
| 100 | 
            +
                  current_namespace.filters.each_pair { |type, filters|
         | 
| 101 | 
            +
                    filters.each do |name, block|
         | 
| 102 | 
            +
                      superclass.send("#{type}_filter", name, only: current_namespace.methods, &block)
         | 
| 103 | 
            +
                    end
         | 
| 104 | 
            +
                  }
         | 
| 105 | 
            +
                end
         | 
| 106 | 
            +
             | 
| 107 | 
            +
                def default_route(method)
         | 
| 108 | 
            +
                  "#{modules_prefix}#{self.api_name}##{method}"
         | 
| 109 | 
            +
                end
         | 
| 110 | 
            +
             | 
| 111 | 
            +
                def modules_prefix
         | 
| 112 | 
            +
                  @modules_prefix ||= begin
         | 
| 113 | 
            +
                    modules = self.name.split('::').slice(0..-2)
         | 
| 114 | 
            +
                    modules.empty? ? '' : "#{modules.map!(&:downcase).join('/')}/"
         | 
| 115 | 
            +
                  end
         | 
| 116 | 
            +
                end
         | 
| 117 | 
            +
             | 
| 118 | 
            +
                #
         | 
| 119 | 
            +
                # Get method name from path
         | 
| 120 | 
            +
                # Example:
         | 
| 121 | 
            +
                #   / => :index
         | 
| 122 | 
            +
                #   /users/:id => :users
         | 
| 123 | 
            +
                #   /users/:id/addresses => :addresses
         | 
| 124 | 
            +
                #
         | 
| 125 | 
            +
                def extract_method_name(path, via)
         | 
| 126 | 
            +
                  return method_name_for_single_resource(path, via) if single_resource?
         | 
| 127 | 
            +
             | 
| 128 | 
            +
                  if path =~ %r(\A/?#{@_prefix}\z)
         | 
| 129 | 
            +
                    return via == :get ? 'index' : 'create'
         | 
| 130 | 
            +
                  end
         | 
| 131 | 
            +
             | 
| 132 | 
            +
                  parts = path.split('/').reverse!
         | 
| 133 | 
            +
             | 
| 134 | 
            +
                  return parts.find { |part| !part.start_with?(':') } if parts.first != ':id'
         | 
| 135 | 
            +
             | 
| 136 | 
            +
                  case via
         | 
| 137 | 
            +
                  when :get
         | 
| 138 | 
            +
                    'show'
         | 
| 139 | 
            +
                  when :put
         | 
| 140 | 
            +
                    'update'
         | 
| 141 | 
            +
                  when :delete
         | 
| 142 | 
            +
                    'destroy'
         | 
| 143 | 
            +
                  else
         | 
| 144 | 
            +
                    raise "Cannot extract method name from #{path}"
         | 
| 145 | 
            +
                  end
         | 
| 146 | 
            +
                end
         | 
| 147 | 
            +
             | 
| 148 | 
            +
                def method_name_for_single_resource(path, via)
         | 
| 149 | 
            +
                  if path =~ %r(\A/?#{@_prefix}\z)
         | 
| 150 | 
            +
                    case via
         | 
| 151 | 
            +
                    when :get
         | 
| 152 | 
            +
                      'show'
         | 
| 153 | 
            +
                    when :post
         | 
| 154 | 
            +
                      'create'
         | 
| 155 | 
            +
                    when :put
         | 
| 156 | 
            +
                      'update'
         | 
| 157 | 
            +
                    when :delete
         | 
| 158 | 
            +
                      'destroy'
         | 
| 159 | 
            +
                    end
         | 
| 160 | 
            +
                  else
         | 
| 161 | 
            +
                    path.split('/').reverse!.last
         | 
| 162 | 
            +
                  end
         | 
| 163 | 
            +
                end
         | 
| 164 | 
            +
             | 
| 165 | 
            +
                #
         | 
| 166 | 
            +
                # Creates path with version, namespace and
         | 
| 167 | 
            +
                # given path, then normalizes it
         | 
| 168 | 
            +
                #
         | 
| 169 | 
            +
                def normalize_path(path)
         | 
| 170 | 
            +
                  parts = []
         | 
| 171 | 
            +
                  parts << @_prefix unless !prefix? || path =~ %r(\A/?#{@_prefix})
         | 
| 172 | 
            +
                  parts.concat @_namespaces.reject { |n| path =~ %r(/#{n.path}) }.map!(&:path) if current_namespace?
         | 
| 173 | 
            +
                  parts << path.to_s unless path == '/'
         | 
| 174 | 
            +
                  parts.join('/')
         | 
| 175 | 
            +
                end
         | 
| 176 | 
            +
              end
         | 
| 177 | 
            +
            end
         | 
    
        data/lib/raisin/api.rb
    CHANGED
    
    | @@ -1,26 +1,22 @@ | |
| 1 | 
            -
             | 
| 2 | 
            -
              class MiddlewareStack < ActionDispatch::MiddlewareStack
         | 
| 3 | 
            -
                class Middleware < ActionDispatch::MiddlewareStack::Middleware
         | 
| 4 | 
            -
                  def update(args)
         | 
| 5 | 
            -
                    @args = args
         | 
| 6 | 
            -
                  end
         | 
| 7 | 
            -
                end
         | 
| 8 | 
            -
              end
         | 
| 1 | 
            +
            require 'raisin/api/dsl'
         | 
| 9 2 |  | 
| 3 | 
            +
            module Raisin
         | 
| 10 4 | 
             
              class API
         | 
| 11 | 
            -
                cattr_accessor :middleware_stack
         | 
| 12 | 
            -
                @@middleware_stack = Raisin::MiddlewareStack.new
         | 
| 13 5 |  | 
| 6 | 
            +
                #
         | 
| 7 | 
            +
                # Returns a proc representing our action to be called in the given
         | 
| 8 | 
            +
                # environment.
         | 
| 9 | 
            +
                #
         | 
| 14 10 | 
             
                def self.action(name, klass = ActionDispatch::Request)
         | 
| 15 | 
            -
                   | 
| 16 | 
            -
                    self.const_get(name.camelize).new.dispatch(:call, klass.new(env))
         | 
| 17 | 
            -
                  end
         | 
| 18 | 
            -
                end
         | 
| 19 | 
            -
             | 
| 20 | 
            -
                def self.use(*args, &block)
         | 
| 21 | 
            -
                  middleware_stack.use(*args, &block)
         | 
| 11 | 
            +
                  ->(env) { self.const_get(name.camelize).new.dispatch(:call, klass.new(env)) }
         | 
| 22 12 | 
             
                end
         | 
| 23 13 |  | 
| 14 | 
            +
                #
         | 
| 15 | 
            +
                # The abstract class that is inherited by all actions.
         | 
| 16 | 
            +
                #
         | 
| 17 | 
            +
                # It includes actions DSL, default call method, global authentication
         | 
| 18 | 
            +
                # filter and global respond_to
         | 
| 19 | 
            +
                #
         | 
| 24 20 | 
             
                def self.action_klass
         | 
| 25 21 | 
             
                  @_klass ||= begin
         | 
| 26 22 | 
             
                    klass = Class.new(::Raisin::Base)
         | 
| @@ -35,10 +31,14 @@ module Raisin | |
| 35 31 | 
             
                  end
         | 
| 36 32 | 
             
                end
         | 
| 37 33 |  | 
| 38 | 
            -
                def self.action_klass=(klass)
         | 
| 34 | 
            +
                def self.action_klass=(klass) # :nodoc:
         | 
| 39 35 | 
             
                  @_klass = klass
         | 
| 40 36 | 
             
                end
         | 
| 41 37 |  | 
| 38 | 
            +
                #
         | 
| 39 | 
            +
                # Resets routes and namespaces.
         | 
| 40 | 
            +
                # Sets default prefix to api_name (e.g PostsAPI => 'api')
         | 
| 41 | 
            +
                #
         | 
| 42 42 | 
             
                def self.reset
         | 
| 43 43 | 
             
                  @_routes = []
         | 
| 44 44 | 
             
                  @_prefix = self.api_name
         | 
| @@ -46,202 +46,29 @@ module Raisin | |
| 46 46 | 
             
                  @_single_resource = false
         | 
| 47 47 | 
             
                end
         | 
| 48 48 |  | 
| 49 | 
            +
                #
         | 
| 50 | 
            +
                # Resets routes and namespaces for each new API class.
         | 
| 51 | 
            +
                # The action class is copied for some reasons (??)
         | 
| 52 | 
            +
                #
         | 
| 49 53 | 
             
                def self.inherited(subclass)
         | 
| 50 54 | 
             
                  super
         | 
| 51 55 | 
             
                  subclass.reset
         | 
| 52 | 
            -
                  subclass.middleware_stack = self.middleware_stack.dup
         | 
| 53 56 | 
             
                  subclass.action_klass = self.action_klass.dup
         | 
| 54 57 | 
             
                end
         | 
| 55 58 |  | 
| 59 | 
            +
                #
         | 
| 60 | 
            +
                # Returns the last part of the api's name, underscored, without the ending
         | 
| 61 | 
            +
                # <tt>API</tt>. For instance, PostsAPI returns <tt>posts</tt>.
         | 
| 62 | 
            +
                # Namespaces are left out, so Admin::PostsAPI returns <tt>posts</tt> as well.
         | 
| 63 | 
            +
                #
         | 
| 56 64 | 
             
                def self.api_name
         | 
| 57 65 | 
             
                  @api_name ||= self.name.demodulize.sub(/api/i, '').underscore
         | 
| 58 66 | 
             
                end
         | 
| 59 67 |  | 
| 60 | 
            -
                def self. | 
| 61 | 
            -
                  m = middleware_stack.find { |m| m == klass }
         | 
| 62 | 
            -
                  if m
         | 
| 63 | 
            -
                    m.update klass.merge(m.args, args)
         | 
| 64 | 
            -
                  else
         | 
| 65 | 
            -
                    self.use(klass, *args)
         | 
| 66 | 
            -
                  end
         | 
| 67 | 
            -
                end
         | 
| 68 | 
            -
             | 
| 69 | 
            -
                def self.routes
         | 
| 68 | 
            +
                def self.routes # :nodoc:
         | 
| 70 69 | 
             
                  @_routes
         | 
| 71 70 | 
             
                end
         | 
| 72 71 |  | 
| 73 | 
            -
                 | 
| 74 | 
            -
                  %w(get head post put delete).each do |via|
         | 
| 75 | 
            -
                    class_eval <<-EOF, __FILE__, __LINE__ + 1
         | 
| 76 | 
            -
                      def #{via}(path = '/', options = {}, &block)
         | 
| 77 | 
            -
                        path = normalize_path(path)
         | 
| 78 | 
            -
                        method_name = options.key?(:as) ? options[:as].to_s : extract_method_name(path, :#{via})
         | 
| 79 | 
            -
             | 
| 80 | 
            -
                        klass = self.const_set method_name.camelize, Class.new(@_klass, &block)
         | 
| 81 | 
            -
                        klass.send(:expose, current_namespace.exposure, &(current_namespace.lazy_expose)) if current_namespace.try(:expose?)
         | 
| 82 | 
            -
             | 
| 83 | 
            -
                        current_namespace.add(method_name) if current_namespace
         | 
| 84 | 
            -
             | 
| 85 | 
            -
                        routes << [:#{via}, path, default_route(method_name)]
         | 
| 86 | 
            -
                      end
         | 
| 87 | 
            -
                    EOF
         | 
| 88 | 
            -
                  end
         | 
| 89 | 
            -
             | 
| 90 | 
            -
                  def included(&block)
         | 
| 91 | 
            -
                    self.action_klass.class_eval(&block) if block_given?
         | 
| 92 | 
            -
                  end
         | 
| 93 | 
            -
             | 
| 94 | 
            -
                  def member(&block)
         | 
| 95 | 
            -
                    namespace(':id') do
         | 
| 96 | 
            -
                      resource = self.api_name.singularize
         | 
| 97 | 
            -
                      expose(resource) { resource.camelize.constantize.send :find, params[:id] }
         | 
| 98 | 
            -
                      instance_eval(&block)
         | 
| 99 | 
            -
                    end
         | 
| 100 | 
            -
                  end
         | 
| 101 | 
            -
             | 
| 102 | 
            -
                  def nested_into_resource(parent)
         | 
| 103 | 
            -
                    parent = parent.to_s
         | 
| 104 | 
            -
                    sing = parent.singularize
         | 
| 105 | 
            -
                    id = "#{sing}_id"
         | 
| 106 | 
            -
             | 
| 107 | 
            -
                    @_namespaces << Namespace.new("#{parent}/:#{id}")
         | 
| 108 | 
            -
                    current_namespace.expose(sing) { sing.camelize.constantize.send :find, params[id.to_sym]}
         | 
| 109 | 
            -
                    @_namespaces << Namespace.new(@_prefix)
         | 
| 110 | 
            -
                    @_prefix = nil
         | 
| 111 | 
            -
                  end
         | 
| 112 | 
            -
             | 
| 113 | 
            -
                  def single_resource
         | 
| 114 | 
            -
                    @_single_resource = true
         | 
| 115 | 
            -
                    @_prefix = @_prefix.singularize if prefix?
         | 
| 116 | 
            -
                  end
         | 
| 117 | 
            -
             | 
| 118 | 
            -
                  def prefix(prefix)
         | 
| 119 | 
            -
                    @_prefix = prefix
         | 
| 120 | 
            -
                  end
         | 
| 121 | 
            -
             | 
| 122 | 
            -
                  def description(desc)
         | 
| 123 | 
            -
                    # noop
         | 
| 124 | 
            -
                  end
         | 
| 125 | 
            -
             | 
| 126 | 
            -
                  def expose(*args, &block)
         | 
| 127 | 
            -
                    current_namespace.expose(*args, &block)
         | 
| 128 | 
            -
                  end
         | 
| 129 | 
            -
             | 
| 130 | 
            -
                  def namespace(path, &block)
         | 
| 131 | 
            -
                    path = path.sub(%r(\A/?#{@_prefix}), '') if prefix?
         | 
| 132 | 
            -
                    @_namespaces.push Namespace.new(path)
         | 
| 133 | 
            -
                    yield
         | 
| 134 | 
            -
                    process_filters
         | 
| 135 | 
            -
                    @_namespaces.pop
         | 
| 136 | 
            -
                  end
         | 
| 137 | 
            -
             | 
| 138 | 
            -
                  %w(before around after).each do |type|
         | 
| 139 | 
            -
                    class_eval <<-EOF, __FILE__, __LINE__ + 1
         | 
| 140 | 
            -
                      def #{type}(*args, &block)
         | 
| 141 | 
            -
                        return unless current_namespace
         | 
| 142 | 
            -
                        current_namespace.filter(:#{type}, args, &block)
         | 
| 143 | 
            -
                      end
         | 
| 144 | 
            -
                    EOF
         | 
| 145 | 
            -
                  end
         | 
| 146 | 
            -
             | 
| 147 | 
            -
                  protected
         | 
| 148 | 
            -
             | 
| 149 | 
            -
                  def prefix?
         | 
| 150 | 
            -
                    !!@_prefix
         | 
| 151 | 
            -
                  end
         | 
| 152 | 
            -
             | 
| 153 | 
            -
                  def single_resource?
         | 
| 154 | 
            -
                    !!@_single_resource
         | 
| 155 | 
            -
                  end
         | 
| 156 | 
            -
             | 
| 157 | 
            -
                  def current_namespace
         | 
| 158 | 
            -
                    @_namespaces.at(0)
         | 
| 159 | 
            -
                  end
         | 
| 160 | 
            -
             | 
| 161 | 
            -
                  def current_namespace?
         | 
| 162 | 
            -
                    @_namespaces.length > 0
         | 
| 163 | 
            -
                  end
         | 
| 164 | 
            -
             | 
| 165 | 
            -
                  def process_filters
         | 
| 166 | 
            -
                    current_namespace.filters.each_pair { |type, filters|
         | 
| 167 | 
            -
                      filters.each do |name, block|
         | 
| 168 | 
            -
                        superclass.send("#{type}_filter", name, only: current_namespace.methods, &block)
         | 
| 169 | 
            -
                      end
         | 
| 170 | 
            -
                    }
         | 
| 171 | 
            -
                  end
         | 
| 172 | 
            -
             | 
| 173 | 
            -
                  def default_route(method)
         | 
| 174 | 
            -
                    "#{modules_prefix}#{self.api_name}##{method}"
         | 
| 175 | 
            -
                  end
         | 
| 176 | 
            -
             | 
| 177 | 
            -
                  def modules_prefix
         | 
| 178 | 
            -
                    @modules_prefix ||= begin
         | 
| 179 | 
            -
                      modules = self.name.split('::').slice(0..-2)
         | 
| 180 | 
            -
                      modules.empty? ? '' : "#{modules.map!(&:downcase).join('/')}/"
         | 
| 181 | 
            -
                    end
         | 
| 182 | 
            -
                  end
         | 
| 183 | 
            -
             | 
| 184 | 
            -
                  #
         | 
| 185 | 
            -
                  # Get method name from path
         | 
| 186 | 
            -
                  # Example:
         | 
| 187 | 
            -
                  #   / => :index
         | 
| 188 | 
            -
                  #   /users/:id => :users
         | 
| 189 | 
            -
                  #   /users/:id/addresses => :addresses
         | 
| 190 | 
            -
                  #
         | 
| 191 | 
            -
                  def extract_method_name(path, via)
         | 
| 192 | 
            -
                    return method_name_for_single_resource(path, via) if single_resource?
         | 
| 193 | 
            -
             | 
| 194 | 
            -
                    if path =~ %r(\A/?#{@_prefix}\z)
         | 
| 195 | 
            -
                      return via == :get ? 'index' : 'create'
         | 
| 196 | 
            -
                    end
         | 
| 197 | 
            -
             | 
| 198 | 
            -
                    parts = path.split('/').reverse!
         | 
| 199 | 
            -
             | 
| 200 | 
            -
                    return parts.find { |part| !part.start_with?(':') } if parts.first != ':id'
         | 
| 201 | 
            -
             | 
| 202 | 
            -
                    case via
         | 
| 203 | 
            -
                    when :get
         | 
| 204 | 
            -
                      'show'
         | 
| 205 | 
            -
                    when :put
         | 
| 206 | 
            -
                      'update'
         | 
| 207 | 
            -
                    when :delete
         | 
| 208 | 
            -
                      'destroy'
         | 
| 209 | 
            -
                    else
         | 
| 210 | 
            -
                      raise "Cannot extract method name from #{path}"
         | 
| 211 | 
            -
                    end
         | 
| 212 | 
            -
                  end
         | 
| 213 | 
            -
             | 
| 214 | 
            -
                  def method_name_for_single_resource(path, via)
         | 
| 215 | 
            -
                    if path =~ %r(\A/?#{@_prefix}\z)
         | 
| 216 | 
            -
                      case via
         | 
| 217 | 
            -
                      when :get
         | 
| 218 | 
            -
                        'show'
         | 
| 219 | 
            -
                      when :post
         | 
| 220 | 
            -
                        'create'
         | 
| 221 | 
            -
                      when :put
         | 
| 222 | 
            -
                        'update'
         | 
| 223 | 
            -
                      when :delete
         | 
| 224 | 
            -
                        'destroy'
         | 
| 225 | 
            -
                      end
         | 
| 226 | 
            -
                    else
         | 
| 227 | 
            -
                      path.split('/').reverse!.last
         | 
| 228 | 
            -
                    end
         | 
| 229 | 
            -
                  end
         | 
| 230 | 
            -
             | 
| 231 | 
            -
                  #
         | 
| 232 | 
            -
                  # Creates path with version, namespace and
         | 
| 233 | 
            -
                  # given path, then normalizes it
         | 
| 234 | 
            -
                  #
         | 
| 235 | 
            -
                  def normalize_path(path)
         | 
| 236 | 
            -
                    parts = []
         | 
| 237 | 
            -
                    parts << @_prefix unless !prefix? || path =~ %r(\A/?#{@_prefix})
         | 
| 238 | 
            -
                    parts.concat @_namespaces.reject { |n| path =~ %r(/#{n.path}) }.map!(&:path) if current_namespace?
         | 
| 239 | 
            -
                    parts << path.to_s unless path == '/'
         | 
| 240 | 
            -
                    parts.join('/')
         | 
| 241 | 
            -
                  end
         | 
| 242 | 
            -
                end
         | 
| 243 | 
            -
             | 
| 244 | 
            -
                extend DSL
         | 
| 245 | 
            -
             | 
| 72 | 
            +
                extend Raisin::DSL
         | 
| 246 73 | 
             
              end
         | 
| 247 74 | 
             
            end
         | 
    
        data/lib/raisin/base.rb
    CHANGED
    
    | @@ -1,4 +1,8 @@ | |
| 1 1 | 
             
            module Raisin
         | 
| 2 | 
            +
             | 
| 3 | 
            +
              #
         | 
| 4 | 
            +
              # Abstract class for all actions.
         | 
| 5 | 
            +
              #
         | 
| 2 6 | 
             
              class Base < ActionController::Metal
         | 
| 3 7 | 
             
                abstract!
         | 
| 4 8 |  | 
| @@ -15,8 +19,6 @@ module Raisin | |
| 15 19 | 
             
                  def perform_caching=(*); end
         | 
| 16 20 | 
             
                  def helpers_path=(*); end
         | 
| 17 21 | 
             
                  def allow_forgery_protection=(*); end
         | 
| 18 | 
            -
                  # def helper_method(*); end
         | 
| 19 | 
            -
                  # def helper(*); end
         | 
| 20 22 | 
             
                end
         | 
| 21 23 |  | 
| 22 24 | 
             
                extend Compatibility
         | 
| @@ -44,17 +46,10 @@ module Raisin | |
| 44 46 | 
             
                  include mod
         | 
| 45 47 | 
             
                }
         | 
| 46 48 |  | 
| 47 | 
            -
                def self._expose(name, &block)
         | 
| 48 | 
            -
             | 
| 49 | 
            -
                end
         | 
| 50 | 
            -
             | 
| 51 49 | 
             
                def self.controller_path
         | 
| 52 50 | 
             
                  @controller_path ||= name && name.sub(/\:\:[^\:]+$/, '').sub(/api$/i, '').underscore
         | 
| 53 51 | 
             
                end
         | 
| 54 52 |  | 
| 55 | 
            -
                #
         | 
| 56 | 
            -
                # Remove nil prefixes from our anonymous classes
         | 
| 57 | 
            -
                #
         | 
| 58 53 | 
             
                def _prefixes
         | 
| 59 54 | 
             
                  @_prefixes ||= begin
         | 
| 60 55 | 
             
                    parent_prefixes = self.class.parent_prefixes
         | 
| @@ -67,171 +62,12 @@ module Raisin | |
| 67 62 | 
             
                end
         | 
| 68 63 |  | 
| 69 64 | 
             
                #
         | 
| 70 | 
            -
                #  | 
| 71 | 
            -
                # called in the normal process only in tests
         | 
| 65 | 
            +
                # In test env, action is not :call. This is a bit of a hack
         | 
| 72 66 | 
             
                #
         | 
| 73 67 | 
             
                def process(action, *args)
         | 
| 74 68 | 
             
                  super(:call, *args)
         | 
| 75 69 | 
             
                end
         | 
| 76 70 |  | 
| 77 | 
            -
                # class << self
         | 
| 78 | 
            -
                #   attr_internal_reader :routes, :current_namespace
         | 
| 79 | 
            -
             | 
| 80 | 
            -
                #   alias :current_namespace? :current_namespace
         | 
| 81 | 
            -
             | 
| 82 | 
            -
                #   %w(get head post put delete).each do |via|
         | 
| 83 | 
            -
                #     class_eval <<-EOF, __FILE__, __LINE__ + 1
         | 
| 84 | 
            -
                #       def #{via}(path, options = nil, &block)
         | 
| 85 | 
            -
                #         path = normalize_path(path)
         | 
| 86 | 
            -
                #         method_name = extract_method_name(path, :#{via})
         | 
| 87 | 
            -
             | 
| 88 | 
            -
                #         endpoint = Endpoint.new
         | 
| 89 | 
            -
                #         endpoint.instance_eval(&block)
         | 
| 90 | 
            -
             | 
| 91 | 
            -
                #         Rails.logger.warn("WARNING: redefinition of method " << method_name) if method_defined?(method_name)
         | 
| 92 | 
            -
                #         define_method(method_name, &(endpoint.response_body))
         | 
| 93 | 
            -
             | 
| 94 | 
            -
                #         current_namespace.add(method_name) if current_namespace?
         | 
| 95 | 
            -
             | 
| 96 | 
            -
                #         routes << [:#{via}, path, default_route(method_name)]
         | 
| 97 | 
            -
                #       end
         | 
| 98 | 
            -
                #     EOF
         | 
| 99 | 
            -
                #   end
         | 
| 100 | 
            -
             | 
| 101 | 
            -
                #   def prefix(prefix)
         | 
| 102 | 
            -
                #     @_prefix = prefix
         | 
| 103 | 
            -
                #   end
         | 
| 104 | 
            -
             | 
| 105 | 
            -
                #   def prefix?
         | 
| 106 | 
            -
                #     @_prefix
         | 
| 107 | 
            -
                #   end
         | 
| 108 | 
            -
             | 
| 109 | 
            -
                #   def description(desc)
         | 
| 110 | 
            -
                #     # noop
         | 
| 111 | 
            -
                #   end
         | 
| 112 | 
            -
             | 
| 113 | 
            -
                #   # def get(path, options = nil, &block)
         | 
| 114 | 
            -
                #   #   path = normalize_path(path)
         | 
| 115 | 
            -
                #   #   method_name = extract_method_name(path, :get)
         | 
| 116 | 
            -
             | 
| 117 | 
            -
                #   #   endpoint = Endpoint.new
         | 
| 118 | 
            -
                #   #   endpoint.instance_eval(&block)
         | 
| 119 | 
            -
             | 
| 120 | 
            -
                #   #   define_method(method_name, &(endpoint.response_body))
         | 
| 121 | 
            -
             | 
| 122 | 
            -
                #   #   current_namespace.add(method_name) if current_namespace?
         | 
| 123 | 
            -
             | 
| 124 | 
            -
                #   #   routes << [:get, path, default_route(method_name)]
         | 
| 125 | 
            -
                #   # end
         | 
| 126 | 
            -
                #   # alias_method :head, :get
         | 
| 127 | 
            -
             | 
| 128 | 
            -
                #   # def post(path, options = nil, &block)
         | 
| 129 | 
            -
                #   #   path = normalize_path(path)
         | 
| 130 | 
            -
                #   #   method_name = extract_method_name(path)
         | 
| 131 | 
            -
             | 
| 132 | 
            -
                #   #   define_method(method_name, &block)
         | 
| 133 | 
            -
             | 
| 134 | 
            -
                #   #   routes << [:post, path, default_route(method_name)]
         | 
| 135 | 
            -
                #   # end
         | 
| 136 | 
            -
             | 
| 137 | 
            -
                #   # def put(path, options = nil, &block)
         | 
| 138 | 
            -
                #   #   path = normalize_path(path)
         | 
| 139 | 
            -
                #   #   method_name = extract_method_name(path)
         | 
| 140 | 
            -
             | 
| 141 | 
            -
                #   #   define_method(method_name, &block)
         | 
| 142 | 
            -
             | 
| 143 | 
            -
                #   #   routes << [:put, path, default_route(method_name)]
         | 
| 144 | 
            -
                #   # end
         | 
| 145 | 
            -
                #   # alias_method :patch, :put
         | 
| 146 | 
            -
             | 
| 147 | 
            -
                #   # def delete(path, options = nil, &block)
         | 
| 148 | 
            -
                #   #   path = normalize_path(path)
         | 
| 149 | 
            -
                #   #   method_name = extract_method_name(path)
         | 
| 150 | 
            -
             | 
| 151 | 
            -
                #   #   define_method(method_name, &block)
         | 
| 152 | 
            -
             | 
| 153 | 
            -
                #   #   routes << [:delete, path, default_route(method_name)]
         | 
| 154 | 
            -
                #   # end
         | 
| 155 | 
            -
             | 
| 156 | 
            -
                #   def namespace(path, &block)
         | 
| 157 | 
            -
                #     path = path.sub(%r(\A/?#{@_prefix}), '') if prefix?
         | 
| 158 | 
            -
                #     old_namespace, @_current_namespace = current_namespace, Namespace.new(path)
         | 
| 159 | 
            -
                #     yield
         | 
| 160 | 
            -
                #     process_filters
         | 
| 161 | 
            -
                #     @_current_namespace = old_namespace
         | 
| 162 | 
            -
                #   end
         | 
| 163 | 
            -
             | 
| 164 | 
            -
                #   %w(before around after).each do |type|
         | 
| 165 | 
            -
                #     class_eval <<-EOF, __FILE__, __LINE__ + 1
         | 
| 166 | 
            -
                #       def #{type}(*args, &block)
         | 
| 167 | 
            -
                #         return unless current_namespace?
         | 
| 168 | 
            -
                #         current_namespace.filter(:#{type}, args, &block)
         | 
| 169 | 
            -
                #       end
         | 
| 170 | 
            -
                #     EOF
         | 
| 171 | 
            -
                #   end
         | 
| 172 | 
            -
             | 
| 173 | 
            -
                #   protected
         | 
| 174 | 
            -
             | 
| 175 | 
            -
                #   def process_filters
         | 
| 176 | 
            -
                #     current_namespace.filters.each_pair { |type, filters|
         | 
| 177 | 
            -
                #       filters.each do |name, block|
         | 
| 178 | 
            -
                #         superclass.send("#{type}_filter", name, only: current_namespace.methods, &block)
         | 
| 179 | 
            -
                #       end
         | 
| 180 | 
            -
                #     }
         | 
| 181 | 
            -
                #   end
         | 
| 182 | 
            -
             | 
| 183 | 
            -
                #   def default_route(method)
         | 
| 184 | 
            -
                #     "#{modules_prefix}#{self.api_name}##{method}"
         | 
| 185 | 
            -
                #   end
         | 
| 186 | 
            -
             | 
| 187 | 
            -
                #   def modules_prefix
         | 
| 188 | 
            -
                #     @modules_prefix ||= begin
         | 
| 189 | 
            -
                #       modules = self.name.split('::').slice(0..-2)
         | 
| 190 | 
            -
                #       modules.empty? ? '' : "#{modules.join('/')}/"
         | 
| 191 | 
            -
                #     end
         | 
| 192 | 
            -
                #   end
         | 
| 193 | 
            -
             | 
| 194 | 
            -
                #   #
         | 
| 195 | 
            -
                #   # Get method name from path
         | 
| 196 | 
            -
                #   # Example:
         | 
| 197 | 
            -
                #   #   / => :index
         | 
| 198 | 
            -
                #   #   /users/:id => :users
         | 
| 199 | 
            -
                #   #   /users/:id/addresses => :addresses
         | 
| 200 | 
            -
                #   #
         | 
| 201 | 
            -
                #   def extract_method_name(path, via)
         | 
| 202 | 
            -
                #     return :index if path =~ %r(\A/?#{@_prefix}\z)
         | 
| 203 | 
            -
             | 
| 204 | 
            -
                #     parts = path.split('/').reverse!
         | 
| 205 | 
            -
             | 
| 206 | 
            -
                #     return parts.find { |part| !part.start_with?(':') } if parts.first != ':id'
         | 
| 207 | 
            -
             | 
| 208 | 
            -
                #     case via
         | 
| 209 | 
            -
                #     when :get
         | 
| 210 | 
            -
                #       :show
         | 
| 211 | 
            -
                #     when :post
         | 
| 212 | 
            -
                #       :create
         | 
| 213 | 
            -
                #     when :put
         | 
| 214 | 
            -
                #       :update
         | 
| 215 | 
            -
                #     when :delete
         | 
| 216 | 
            -
                #       :destroy
         | 
| 217 | 
            -
                #     else
         | 
| 218 | 
            -
                #       raise "Cannot extract method name from #{path}"
         | 
| 219 | 
            -
                #     end
         | 
| 220 | 
            -
                #   end
         | 
| 221 | 
            -
             | 
| 222 | 
            -
                #   #
         | 
| 223 | 
            -
                #   # Creates path with version, namespace and
         | 
| 224 | 
            -
                #   # given path, then normalizes it
         | 
| 225 | 
            -
                #   #
         | 
| 226 | 
            -
                #   def normalize_path(path)
         | 
| 227 | 
            -
                #     parts = []
         | 
| 228 | 
            -
                #     parts << @_prefix unless !@_prefix || path =~ %r(\A/?#{@_prefix})
         | 
| 229 | 
            -
                #     parts << current_namespace.path unless !current_namespace? || path =~ %r(/#{current_namespace.path})
         | 
| 230 | 
            -
                #     parts << path.to_s unless path == '/'
         | 
| 231 | 
            -
                #     parts.join('/')
         | 
| 232 | 
            -
                #   end
         | 
| 233 | 
            -
                # end
         | 
| 234 | 
            -
             | 
| 235 71 | 
             
                ActiveSupport.run_load_hooks(:action_controller, self)
         | 
| 236 72 | 
             
              end
         | 
| 237 73 | 
             
            end
         | 
    
        data/lib/raisin/exposable.rb
    CHANGED
    
    | @@ -3,16 +3,19 @@ module Raisin | |
| 3 3 | 
             
                extend ActiveSupport::Concern
         | 
| 4 4 |  | 
| 5 5 | 
             
                included do
         | 
| 6 | 
            -
                  attr_reader : | 
| 6 | 
            +
                  attr_reader :exposures
         | 
| 7 | 
            +
                end
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                def initialize(*args)
         | 
| 10 | 
            +
                  @exposures = []
         | 
| 7 11 | 
             
                end
         | 
| 8 12 |  | 
| 9 13 | 
             
                def expose(name, &block)
         | 
| 10 | 
            -
                  @ | 
| 11 | 
            -
                  @lazy_expose = block
         | 
| 14 | 
            +
                  @exposures << [name, block]
         | 
| 12 15 | 
             
                end
         | 
| 13 16 |  | 
| 14 17 | 
             
                def expose?
         | 
| 15 | 
            -
                   | 
| 18 | 
            +
                  !exposures.empty?
         | 
| 16 19 | 
             
                end
         | 
| 17 20 | 
             
              end
         | 
| 18 21 | 
             
            end
         | 
| @@ -0,0 +1,31 @@ | |
| 1 | 
            +
            module Raisin
         | 
| 2 | 
            +
              class Middleware
         | 
| 3 | 
            +
                ACCEPT_REGEXP = /application\/vnd\.(?<vendor>[a-z]+)-(?<version>v[0-9]+)\+(?<format>[a-z]+)?/
         | 
| 4 | 
            +
             | 
| 5 | 
            +
                def initialize(app)
         | 
| 6 | 
            +
                  @app = app
         | 
| 7 | 
            +
                  @vendor = Configuration.version.vendor
         | 
| 8 | 
            +
                end
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                def call(env)
         | 
| 11 | 
            +
                  @env = env
         | 
| 12 | 
            +
                  if verify_accept_header
         | 
| 13 | 
            +
                    @app.call(@env)
         | 
| 14 | 
            +
                  else
         | 
| 15 | 
            +
                    [406, {}, []]
         | 
| 16 | 
            +
                  end
         | 
| 17 | 
            +
                end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                private
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                def verify_accept_header
         | 
| 22 | 
            +
                  if (matches = ACCEPT_REGEXP.match(@env['HTTP_ACCEPT'])) && @vendor == matches[:vendor]
         | 
| 23 | 
            +
                    @env['raisin.version']  = matches[:version]
         | 
| 24 | 
            +
                    @env['raisin.format']   = "application/#{matches[:format]}"
         | 
| 25 | 
            +
                    true
         | 
| 26 | 
            +
                  else
         | 
| 27 | 
            +
                    false
         | 
| 28 | 
            +
                  end
         | 
| 29 | 
            +
                end
         | 
| 30 | 
            +
              end
         | 
| 31 | 
            +
            end
         | 
    
        data/lib/raisin/namespace.rb
    CHANGED
    
    
    
        data/lib/raisin/rails/request.rb
    CHANGED
    
    | @@ -1,7 +1,18 @@ | |
| 1 1 | 
             
            module Raisin
         | 
| 2 2 | 
             
              module ApiFormat
         | 
| 3 3 | 
             
                def formats
         | 
| 4 | 
            -
                  @env["action_dispatch.request.formats"] ||=  | 
| 4 | 
            +
                  @env["action_dispatch.request.formats"] ||= 
         | 
| 5 | 
            +
                    if @env.key?('raisin.format')
         | 
| 6 | 
            +
                      Array(Mime::Type.lookup(@env['raisin.format']))
         | 
| 7 | 
            +
                    elsif parameters[:format]
         | 
| 8 | 
            +
                      Array(Mime[parameters[:format]])
         | 
| 9 | 
            +
                    elsif use_accept_header && valid_accept_header
         | 
| 10 | 
            +
                      accepts
         | 
| 11 | 
            +
                    elsif xhr?
         | 
| 12 | 
            +
                      [Mime::JS]
         | 
| 13 | 
            +
                    else
         | 
| 14 | 
            +
                      [Mime::HTML]
         | 
| 15 | 
            +
                    end
         | 
| 5 16 | 
             
                end
         | 
| 6 17 | 
             
              end
         | 
| 7 18 | 
             
            end
         | 
    
        data/lib/raisin/rails/routes.rb
    CHANGED
    
    | @@ -1,3 +1,5 @@ | |
| 1 | 
            +
            require 'raisin/version_constraint'
         | 
| 2 | 
            +
             | 
| 1 3 | 
             
            module ActionDispatch::Routing
         | 
| 2 4 | 
             
              class Mapper
         | 
| 3 5 |  | 
| @@ -5,8 +7,14 @@ module ActionDispatch::Routing | |
| 5 7 | 
             
                #
         | 
| 6 8 | 
             
                #
         | 
| 7 9 | 
             
                def mount_api(raisin_api)
         | 
| 8 | 
            -
                  raisin_api. | 
| 9 | 
            -
                     | 
| 10 | 
            +
                  raisin_api.versions.each do |version, routes|
         | 
| 11 | 
            +
                    next if routes.empty?
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                    send(:constraints, Raisin::VersionConstraint.new(version)) {
         | 
| 14 | 
            +
                      routes.each do |method, path, endpoint|
         | 
| 15 | 
            +
                        send(method, path, to: endpoint)
         | 
| 16 | 
            +
                      end
         | 
| 17 | 
            +
                    }
         | 
| 10 18 | 
             
                  end
         | 
| 11 19 | 
             
                end
         | 
| 12 20 | 
             
              end
         | 
    
        data/lib/raisin/railtie.rb
    CHANGED
    
    
    
        data/lib/raisin/router.rb
    CHANGED
    
    | @@ -1,36 +1,6 @@ | |
| 1 1 | 
             
            module Raisin
         | 
| 2 2 | 
             
              class Router
         | 
| 3 | 
            -
                 | 
| 4 | 
            -
                  ALL = 'all'
         | 
| 5 | 
            -
             | 
| 6 | 
            -
                  attr_reader :version, :type, :options
         | 
| 7 | 
            -
             | 
| 8 | 
            -
                  def initialize(version, options = {})
         | 
| 9 | 
            -
                    @version = version.to_s
         | 
| 10 | 
            -
                    @type = options.delete(:using).try(:to_sym) || Configuration.version.using
         | 
| 11 | 
            -
                    @options = { vendor: Configuration.version.vendor }.merge!(options)
         | 
| 12 | 
            -
             | 
| 13 | 
            -
                    validate!
         | 
| 14 | 
            -
                  end
         | 
| 15 | 
            -
             | 
| 16 | 
            -
                  private
         | 
| 17 | 
            -
             | 
| 18 | 
            -
                  def validate!
         | 
| 19 | 
            -
                    raise 'Missing :using options for version' unless type
         | 
| 20 | 
            -
             | 
| 21 | 
            -
                    case type
         | 
| 22 | 
            -
                    when :header
         | 
| 23 | 
            -
                      raise 'Missing :vendor options when using header versionning' unless options[:vendor]
         | 
| 24 | 
            -
                    when :path
         | 
| 25 | 
            -
                      raise ':all cannot be used with path versionning' if version == ALL
         | 
| 26 | 
            -
                    end
         | 
| 27 | 
            -
                  end
         | 
| 28 | 
            -
                end
         | 
| 29 | 
            -
             | 
| 30 | 
            -
                def self.reset
         | 
| 31 | 
            -
                  @_current_version = nil
         | 
| 32 | 
            -
                  @_routes = []
         | 
| 33 | 
            -
                end
         | 
| 3 | 
            +
                ALL_VERSIONS = :all
         | 
| 34 4 |  | 
| 35 5 | 
             
                #
         | 
| 36 6 | 
             
                # Reset class variables on the subclass when inherited
         | 
| @@ -39,31 +9,37 @@ module Raisin | |
| 39 9 | 
             
                  subclass.reset
         | 
| 40 10 | 
             
                end
         | 
| 41 11 |  | 
| 12 | 
            +
                def self.reset
         | 
| 13 | 
            +
                  @_current_version = nil
         | 
| 14 | 
            +
                  @_versions = { ALL_VERSIONS => [] }
         | 
| 15 | 
            +
                end
         | 
| 16 | 
            +
             | 
| 42 17 | 
             
                class << self
         | 
| 43 | 
            -
                  attr_internal_accessor : | 
| 18 | 
            +
                  attr_internal_accessor :versions, :current_version
         | 
| 44 19 |  | 
| 45 20 | 
             
                  def mount(api)
         | 
| 46 | 
            -
                     | 
| 47 | 
            -
             | 
| 21 | 
            +
                    if version?(:header)
         | 
| 22 | 
            +
                      @_versions[self.current_version].concat(api.routes)
         | 
| 23 | 
            +
                    else
         | 
| 24 | 
            +
                      @_versions[ALL_VERSIONS].concat(version?(:path) ? pathed_routes(api.routes) : api.routes)
         | 
| 25 | 
            +
                    end
         | 
| 48 26 | 
             
                  end
         | 
| 49 27 |  | 
| 50 28 | 
             
                  #
         | 
| 51 29 | 
             
                  # Set version for current block
         | 
| 52 30 | 
             
                  #
         | 
| 53 31 | 
             
                  def version(version, options = {}, &block)
         | 
| 54 | 
            -
                     | 
| 32 | 
            +
                    version = version.to_s
         | 
| 33 | 
            +
                    self.current_version = version
         | 
| 34 | 
            +
                    @_versions[version] = [] if version?(:header)
         | 
| 55 35 | 
             
                    yield
         | 
| 56 36 | 
             
                    self.current_version = nil
         | 
| 57 37 | 
             
                  end
         | 
| 58 38 |  | 
| 59 39 | 
             
                  private
         | 
| 60 40 |  | 
| 61 | 
            -
                  def mount_version_middleware(api)
         | 
| 62 | 
            -
                    api.use_or_update Middleware::Header, self.current_version.version, self.current_version.options
         | 
| 63 | 
            -
                  end
         | 
| 64 | 
            -
             | 
| 65 41 | 
             
                  def version?(type)
         | 
| 66 | 
            -
                    self.current_version &&  | 
| 42 | 
            +
                    self.current_version && Configuration.version.using == type
         | 
| 67 43 | 
             
                  end
         | 
| 68 44 |  | 
| 69 45 | 
             
                  def pathed_routes(routes)
         | 
| @@ -75,16 +51,5 @@ module Raisin | |
| 75 51 | 
             
                  end
         | 
| 76 52 | 
             
                end
         | 
| 77 53 |  | 
| 78 | 
            -
                # #
         | 
| 79 | 
            -
                # # Make the API a rack endpoint
         | 
| 80 | 
            -
                # #
         | 
| 81 | 
            -
                # def self.call(env)
         | 
| 82 | 
            -
                #   @_route_set.freeze unless @_route_set.frozen?
         | 
| 83 | 
            -
                #   @_route_set.call(env)
         | 
| 84 | 
            -
                # end
         | 
| 85 | 
            -
             | 
| 86 | 
            -
                # Mount Raisin::Base into the api
         | 
| 87 | 
            -
                #
         | 
| 88 | 
            -
             | 
| 89 54 | 
             
              end
         | 
| 90 55 | 
             
            end
         | 
    
        data/lib/raisin/version.rb
    CHANGED
    
    
    
        data/lib/raisin.rb
    CHANGED
    
    | @@ -1,8 +1,5 @@ | |
| 1 1 | 
             
            require 'raisin/version'
         | 
| 2 2 |  | 
| 3 | 
            -
            require 'raisin/middleware/base'
         | 
| 4 | 
            -
            require 'raisin/middleware/header'
         | 
| 5 | 
            -
             | 
| 6 3 | 
             
            require 'raisin/configuration'
         | 
| 7 4 |  | 
| 8 5 | 
             
            require 'raisin/exposable'
         | 
| @@ -13,6 +10,9 @@ require 'raisin/router' | |
| 13 10 | 
             
            require 'raisin/base'
         | 
| 14 11 | 
             
            require 'raisin/api'
         | 
| 15 12 |  | 
| 13 | 
            +
            require 'raisin/middleware'
         | 
| 14 | 
            +
            require 'raisin/version_constraint'
         | 
| 15 | 
            +
             | 
| 16 16 | 
             
            module Raisin
         | 
| 17 17 | 
             
              def self.configure
         | 
| 18 18 | 
             
                yield Configuration if block_given?
         | 
    
        metadata
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: raisin
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 0.0. | 
| 4 | 
            +
              version: 0.0.4
         | 
| 5 5 | 
             
              prerelease: 
         | 
| 6 6 | 
             
            platform: ruby
         | 
| 7 7 | 
             
            authors:
         | 
| @@ -9,7 +9,7 @@ authors: | |
| 9 9 | 
             
            autorequire: 
         | 
| 10 10 | 
             
            bindir: bin
         | 
| 11 11 | 
             
            cert_chain: []
         | 
| 12 | 
            -
            date: 2012-12- | 
| 12 | 
            +
            date: 2012-12-20 00:00:00.000000000 Z
         | 
| 13 13 | 
             
            dependencies: []
         | 
| 14 14 | 
             
            description: An opiniated micro-framework to easily build elegant API on top of Rails
         | 
| 15 15 | 
             
            email:
         | 
| @@ -25,12 +25,12 @@ files: | |
| 25 25 | 
             
            - Rakefile
         | 
| 26 26 | 
             
            - lib/raisin.rb
         | 
| 27 27 | 
             
            - lib/raisin/api.rb
         | 
| 28 | 
            +
            - lib/raisin/api/dsl.rb
         | 
| 28 29 | 
             
            - lib/raisin/base.rb
         | 
| 29 30 | 
             
            - lib/raisin/configuration.rb
         | 
| 30 31 | 
             
            - lib/raisin/endpoint.rb
         | 
| 31 32 | 
             
            - lib/raisin/exposable.rb
         | 
| 32 | 
            -
            - lib/raisin/middleware | 
| 33 | 
            -
            - lib/raisin/middleware/header.rb
         | 
| 33 | 
            +
            - lib/raisin/middleware.rb
         | 
| 34 34 | 
             
            - lib/raisin/mixin.rb
         | 
| 35 35 | 
             
            - lib/raisin/namespace.rb
         | 
| 36 36 | 
             
            - lib/raisin/rails/request.rb
         | 
| @@ -45,6 +45,7 @@ files: | |
| 45 45 | 
             
            - lib/raisin/testing/rspec/unit_helper.rb
         | 
| 46 46 | 
             
            - lib/raisin/testing/rspec/unit_test.rb
         | 
| 47 47 | 
             
            - lib/raisin/version.rb
         | 
| 48 | 
            +
            - lib/raisin/version_constraint.rb
         | 
| 48 49 | 
             
            - raisin.gemspec
         | 
| 49 50 | 
             
            homepage: https://github.com/ccocchi/raisin
         | 
| 50 51 | 
             
            licenses: []
         | 
| @@ -1,39 +0,0 @@ | |
| 1 | 
            -
            module Raisin
         | 
| 2 | 
            -
              module Middleware
         | 
| 3 | 
            -
                class Header < Base
         | 
| 4 | 
            -
             | 
| 5 | 
            -
                  def self.merge(base, other)
         | 
| 6 | 
            -
                    base_options, other_options = base.pop, other.pop
         | 
| 7 | 
            -
                    [base.concat(other), base_options.merge!(other_options)]
         | 
| 8 | 
            -
                  end
         | 
| 9 | 
            -
             | 
| 10 | 
            -
                  attr_reader :options, :versions
         | 
| 11 | 
            -
             | 
| 12 | 
            -
                  def initialize(app, versions, options = {})
         | 
| 13 | 
            -
                    super
         | 
| 14 | 
            -
                    @options = options
         | 
| 15 | 
            -
                    @versions = Array(versions)
         | 
| 16 | 
            -
                  end
         | 
| 17 | 
            -
             | 
| 18 | 
            -
                  def call(env)
         | 
| 19 | 
            -
                    @env = env
         | 
| 20 | 
            -
                    return [406, {}, ["You shall not pass!"]] unless verify_http_accept_header
         | 
| 21 | 
            -
                    super
         | 
| 22 | 
            -
                  end
         | 
| 23 | 
            -
             | 
| 24 | 
            -
                  private
         | 
| 25 | 
            -
             | 
| 26 | 
            -
                  def verify_http_accept_header
         | 
| 27 | 
            -
                    header = @env['HTTP_ACCEPT']
         | 
| 28 | 
            -
                    if (matches = %r{application/vnd\.(?<vendor>[a-z]+)-(?<version>v[0-9]+)\+(?<format>[a-z]+)?}.match(header)) &&
         | 
| 29 | 
            -
                       (versions.include?(matches[:version]) || versions.include?(Raisin::Router::Version::ALL)) &&
         | 
| 30 | 
            -
                       (!options.key?(:vendor) || options[:vendor] == matches[:vendor])
         | 
| 31 | 
            -
             | 
| 32 | 
            -
                      @env['raisin.request.formats'] = [Mime::Type.lookup("application/#{matches[:format]}")]
         | 
| 33 | 
            -
                      return true
         | 
| 34 | 
            -
                    end
         | 
| 35 | 
            -
                    false
         | 
| 36 | 
            -
                  end
         | 
| 37 | 
            -
                end
         | 
| 38 | 
            -
              end
         | 
| 39 | 
            -
            end
         |