Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Instead of manually redefine the route for the action, retrieve it from the routes of the application. #187

Merged
merged 22 commits into from
Dec 18, 2014
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,14 @@ api
You can use this +api+ method more than once for one method. It could
be useful when there are more routes mapped to it.

When providing just one argument (description) or not argument at all,
the paths will be loaded from routes.rb file.

api!
Provide short description and additional option.
The last parameter is methods short description.
The paths will be loaded from routes.rb file.

api_versions (also api_version)
What version(s) does the action belong to. (See `Versioning`_ for details.)

Expand Down
34 changes: 33 additions & 1 deletion lib/apipie/application.rb
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
require 'apipie/static_dispatcher'
require 'apipie/routes_formatter'
require 'yaml'
require 'digest/md5'
require 'json'

module Apipie

class Application

# we need engine just for serving static assets
class Engine < Rails::Engine
initializer "static assets" do |app|
Expand All @@ -29,6 +29,38 @@ def set_resource_id(controller, resource_id)
@controller_to_resource_id[controller] = resource_id
end

def apipie_routes
unless @apipie_api_routes
# ensure routes are loaded
Rails.application.reload_routes! unless Rails.application.routes.routes.any?

regex = Regexp.new("\\A#{Apipie.configuration.api_base_url.values.join('|')}")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the reason for filtering the routes based on the api_base_url? Just optimization or there are other reasons? I causes me some troubles when I want to use this to document some engines that add their specific API

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed the value added is not enough in comparison of problems it may cause.
(I implemented just because I tried to follow current driven behaviors by api_base_url)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will remove that then, we can add that back later in some cusomizable fashion later, if the need occurs

@apipie_api_routes = Rails.application.routes.routes.select do |x|
regex =~ x.path.spec.to_s
end
end
@apipie_api_routes
end

# the app might be nested when using contraints, namespaces etc.
# this method does in depth search for the route controller
def route_app_controller(app, route)
if app.respond_to?(:controller)
return app.controller(route.defaults)
elsif app.respond_to?(:app)
return route_app_controller(app.app, route)
end
end

def routes_for_action(controller, method)
routes = apipie_routes.select do |route|
controller == route_app_controller(route.app, route) &&
method.to_s == route.defaults[:action]
end

RoutesFormater.new.format_paths(routes)
end

# create new method api description
def define_method_description(controller, method_name, dsl_data)
return if ignored?(controller, method_name)
Expand Down
62 changes: 37 additions & 25 deletions lib/apipie/dsl_definition.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ def _apipie_dsl_data_clear

def _apipie_dsl_data_init
@_apipie_dsl_data = {
:api => false,
:api_args => [],
:api_from_routes => nil,
:errors => [],
:params => [],
:resouce_id => nil,
Expand Down Expand Up @@ -72,16 +74,25 @@ def def_param_group(name, &block)
Apipie.add_param_group(self, name, &block)
end

# Declare an api.
#
# Example:
# api :GET, "/resource_route", "short description",
# # load paths from routes and don't provide description
# api
#
def api(method, path, desc = nil, options={}) #:doc:
return unless Apipie.active_dsl?
_apipie_dsl_data[:api] = true
_apipie_dsl_data[:api_args] << [method, path, desc, options]
end

# # load paths from routes
# api! "short description",
#
def api!(desc = nil, options={}) #:doc:
return unless Apipie.active_dsl?
_apipie_dsl_data[:api] = true
_apipie_dsl_data[:api_from_routes] = { :desc => desc, :options =>options }
end

# Reference other similar method
#
# api :PUT, '/articles/:id'
Expand Down Expand Up @@ -363,22 +374,29 @@ def apipie_concern?
# create method api and redefine newly added method
def method_added(method_name) #:doc:
super
return if !Apipie.active_dsl? || !_apipie_dsl_data[:api]

if ! Apipie.active_dsl? || _apipie_dsl_data[:api_args].blank?
_apipie_dsl_data_clear
return
end
if _apipie_dsl_data[:api_from_routes]
desc = _apipie_dsl_data[:api_from_routes][:desc]
options = _apipie_dsl_data[:api_from_routes][:options]

begin
# remove method description if exists and create new one
Apipie.remove_method_description(self, _apipie_dsl_data[:api_versions], method_name)
description = Apipie.define_method_description(self, method_name, _apipie_dsl_data)
ensure
_apipie_dsl_data_clear
api_from_routes = Apipie.routes_for_action(self, method_name).map do |route_info|
[route_info[:verb], route_info[:path], desc, options]
end
_apipie_dsl_data[:api_args].concat(api_from_routes)
end

return if _apipie_dsl_data[:api_args].blank?

# remove method description if exists and create new one
Apipie.remove_method_description(self, _apipie_dsl_data[:api_versions], method_name)
description = Apipie.define_method_description(self, method_name, _apipie_dsl_data)

_apipie_dsl_data_clear
_apipie_define_validators(description)
end # def method_added
ensure
_apipie_dsl_data_clear
end
end

module Concern
Expand Down Expand Up @@ -409,18 +427,12 @@ def apipie_concern?
def method_added(method_name) #:doc:
super

if ! Apipie.active_dsl? || _apipie_dsl_data[:api_args].blank?
_apipie_dsl_data_clear
return
end

begin
_apipie_concern_data << [method_name, _apipie_dsl_data.merge(:from_concern => true)]
ensure
_apipie_dsl_data_clear
end
return if ! Apipie.active_dsl? || !_apipie_dsl_data[:api]

end # def method_added
_apipie_concern_data << [method_name, _apipie_dsl_data.merge(:from_concern => true)]
ensure
_apipie_dsl_data_clear
end

end

Expand Down
39 changes: 39 additions & 0 deletions lib/apipie/routes_formatter.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
class RoutesFormater
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be namespaced under Apipie module

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mtparet no action needed: I will fix that with some other updates I will probably have when testing against my apps

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, I forgot to encapsulate it under the module.

API_METHODS = %w{GET POST PUT PATCH OPTIONS DELETE}

class Path
def format(rails_path_spec)
rails_path_spec.gsub!('(.:format)', '')
rails_path_spec.gsub!(/[()]/, '')
Apipie.configuration.api_base_url.values.each do |values|
rails_path_spec.gsub!("#{values}/", '/')
end
rails_path_spec
end
end

def initialize
@path_formatter = Path.new
end

def format_paths(rails_paths)
rails_paths.map { |rails_path| format_path(rails_path) }
end

def format_path(rails_path)
path = @path_formatter.format(rails_path.path.spec.to_s)

{ path: path, verb: human_verb(rails_path) }
end

def human_verb(route)
verb = API_METHODS.select{|defined_verb| defined_verb =~ /\A#{route.verb}\z/}
if verb.count != 1
verb = API_METHODS.select{|defined_verb| defined_verb == route.constraints[:method]}
if verb.blank?
raise "Unknow verb #{route.path.spec.to_s}"
end
end
verb.first
end
end
13 changes: 13 additions & 0 deletions spec/controllers/users_controller_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ def compare_hashes(h1, h2)
it "should contain all resource methods" do
methods = subject._methods
methods.keys.should include(:show)
methods.keys.should include(:create_route)
methods.keys.should include(:index)
methods.keys.should include(:create)
methods.keys.should include(:update)
Expand Down Expand Up @@ -382,6 +383,18 @@ def reload_controllers
b.full_description.length.should be > 400
end

context "Usign routes.rb" do
it "should contain basic info about method" do
a = Apipie[UsersController, :create_route]
a.apis.count.should == 1
a.formats.should eq(['json'])
api = a.apis.first
api.short_description.should eq("Create user")
api.path.should eq("/users/create_route")
api.http_method.should eq("POST")
end
end

context "contain :see option" do

context "the key is valid" do
Expand Down
10 changes: 10 additions & 0 deletions spec/dummy/app/controllers/users_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -268,4 +268,14 @@ def see_another
def desc_from_file
render :text => 'document from file action'
end

api! 'Create user'
param_group :user
param :user, Hash do
param :permalink, String
end
param :facts, Hash, :desc => "Additional optional facts about the user", :allow_nil => true
def create_route
end

end
1 change: 0 additions & 1 deletion spec/dummy/config/initializers/apipie.rb
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,6 @@
# config.link_extension = ""
end


# integer validator
class Apipie::Validator::IntegerValidator < Apipie::Validator::BaseValidator

Expand Down
6 changes: 5 additions & 1 deletion spec/dummy/config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@
scope ENV['RAILS_RELATIVE_URL_ROOT'] || '/' do

scope '/api' do
resources :users
resources :users do
collection do
post :create_route
end
end
resources :concerns, :only => [:index, :show]
resources :twitter_example do
collection do
Expand Down