apipie-rails 0.0.13 → 0.0.14

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. data/Gemfile.lock +5 -3
  2. data/README.rst +688 -0
  3. data/apipie-rails.gemspec +2 -4
  4. data/app/controllers/apipie/apipies_controller.rb +73 -41
  5. data/app/views/apipie/apipies/index.html.erb +10 -3
  6. data/app/views/apipie/apipies/method.html.erb +5 -2
  7. data/app/views/apipie/apipies/resource.html.erb +10 -3
  8. data/lib/apipie-rails.rb +1 -0
  9. data/lib/apipie/apipie_module.rb +28 -79
  10. data/lib/apipie/application.rb +194 -97
  11. data/lib/apipie/client/generator.rb +5 -4
  12. data/lib/apipie/configuration.rb +112 -0
  13. data/lib/apipie/dsl_definition.rb +93 -16
  14. data/lib/apipie/extractor.rb +12 -5
  15. data/lib/apipie/extractor/collector.rb +1 -1
  16. data/lib/apipie/extractor/recorder.rb +2 -1
  17. data/lib/apipie/extractor/writer.rb +11 -4
  18. data/lib/apipie/helpers.rb +15 -4
  19. data/lib/apipie/markup.rb +3 -4
  20. data/lib/apipie/method_description.rb +28 -22
  21. data/lib/apipie/resource_description.rb +44 -42
  22. data/lib/apipie/routing.rb +3 -1
  23. data/lib/apipie/static_dispatcher.rb +0 -1
  24. data/lib/apipie/version.rb +1 -1
  25. data/lib/generators/apipie/install/README +6 -0
  26. data/lib/generators/apipie/install/install_generator.rb +25 -0
  27. data/lib/generators/apipie/install/templates/initializer.rb.erb +7 -0
  28. data/lib/tasks/apipie.rake +31 -39
  29. data/spec/controllers/api/v1/architectures_controller_spec.rb +30 -0
  30. data/spec/controllers/api/v2/architectures_controller_spec.rb +12 -0
  31. data/spec/controllers/apipies_controller_spec.rb +18 -12
  32. data/spec/controllers/users_controller_spec.rb +56 -19
  33. data/spec/dummy/app/controllers/api/base_controller.rb +4 -0
  34. data/spec/dummy/app/controllers/api/v1/architectures_controller.rb +32 -0
  35. data/spec/dummy/app/controllers/api/v1/base_controller.rb +11 -0
  36. data/spec/dummy/app/controllers/api/v2/architectures_controller.rb +32 -0
  37. data/spec/dummy/app/controllers/api/v2/base_controller.rb +11 -0
  38. data/spec/dummy/app/controllers/twitter_example_controller.rb +1 -1
  39. data/spec/dummy/app/controllers/users_controller.rb +2 -3
  40. data/spec/dummy/config/initializers/apipie.rb +29 -6
  41. data/spec/lib/method_description_spec.rb +29 -0
  42. data/spec/lib/param_description_spec.rb +41 -0
  43. data/spec/lib/resource_description_spec.rb +28 -0
  44. data/spec/spec_helper.rb +1 -1
  45. metadata +99 -164
  46. data/README.rdoc +0 -367
  47. data/lib/apipie/client/base.rb +0 -133
  48. data/lib/apipie/client/cli_command.rb +0 -130
  49. data/lib/apipie/client/main.rb +0 -101
  50. data/lib/apipie/client/rest_client_oauth.rb +0 -19
  51. data/lib/apipie/client/template/Gemfile.tt +0 -3
  52. data/lib/apipie/client/template/README.tt +0 -3
  53. data/lib/apipie/client/template/Rakefile.tt +0 -2
  54. data/lib/apipie/client/template/a_name.gemspec.tt +0 -23
  55. data/lib/apipie/client/template/bin/bin.rb.tt +0 -30
  56. data/lib/apipie/client/template/lib/a_name.rb.tt +0 -27
  57. data/lib/apipie/client/template/lib/a_name/commands/cli.rb.tt +0 -22
  58. data/lib/apipie/client/template/lib/a_name/config.yml +0 -6
  59. data/lib/apipie/client/template/lib/a_name/resources/resource.rb.tt +0 -22
  60. data/lib/apipie/client/template/lib/a_name/version.rb.tt +0 -3
  61. data/lib/apipie/client/thor.rb +0 -21
  62. data/rubygem-apipie-rails.spec +0 -122
  63. data/spec/lib/parameter_description_spec.rb +0 -41
@@ -16,13 +16,11 @@ Gem::Specification.new do |s|
16
16
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
17
17
  s.require_paths = ["lib"]
18
18
 
19
+ s.add_dependency "rails", ">= 3.0.10"
19
20
  s.add_development_dependency "rspec-rails"
20
- s.add_development_dependency "rails", ">= 3.0.10"
21
21
  s.add_development_dependency "sqlite3"
22
22
  s.add_development_dependency "minitest"
23
- s.add_development_dependency "redcarpet"
23
+ s.add_development_dependency "maruku"
24
24
  s.add_development_dependency "RedCloth"
25
25
  s.add_development_dependency "rake"
26
- s.add_development_dependency "rest-client"
27
- s.add_development_dependency "oauth"
28
26
  end
@@ -2,61 +2,39 @@ module Apipie
2
2
  class ApipiesController < ActionController::Base
3
3
  layout Apipie.configuration.layout
4
4
 
5
+ around_filter :set_script_name
6
+
5
7
  def index
8
+
9
+ params[:version] ||= Apipie.configuration.default_version
10
+
11
+ get_format
12
+
6
13
  respond_to do |format|
7
14
 
8
15
  if Apipie.configuration.use_cache?
9
- path = Apipie.configuration.doc_base_url.dup
10
- if [:resource, :method, :format].any? { |p| params[p].to_s =~ /\W/ }
11
- head :bad_request and return
12
- end
13
-
14
- path << "/" << params[:resource] if params[:resource].present?
15
- path << "/" << params[:method] if params[:method].present?
16
- if params[:format].present?
17
- path << ".#{params[:format]}"
18
- else
19
- path << ".html"
20
- end
21
- cache_file = File.join(Apipie.configuration.cache_dir, path)
22
- if File.exists?(cache_file)
23
- content_type = case params[:format]
24
- when "json" then "application/json"
25
- else "text/html"
26
- end
27
- send_file cache_file, :type => content_type, :disposition => "inline"
28
- else
29
- Rails.logger.error("API doc cache not found for '#{path}'. Perhaps you have forgot to run `rake apipie:cache`")
30
- head :not_found
31
- end
32
- return
16
+ render_from_cache and return
33
17
  end
34
18
 
35
19
  Apipie.reload_documentation if Apipie.configuration.reload_controllers?
36
- @doc = Apipie.to_json(params[:resource], params[:method])
20
+ @doc = Apipie.to_json(params[:version], params[:resource], params[:method])
37
21
 
38
22
  format.json do
39
23
  render :json => @doc
40
24
  end
41
25
 
42
26
  format.html do
43
-
27
+ @versions = Apipie.available_versions
44
28
  @doc = @doc[:docs]
45
- if params[:resource].present? && params[:method].present?
46
- @resource = @doc[:resources].first
47
- @method = @resource[:methods].first unless @resource == 'null'
48
- if @resource == 'null' || @method == 'null'
49
- render 'apipie_404', :status => 404
50
- else
51
- render 'method'
52
- end
53
- elsif params[:resource].present?
54
- @resource = @doc[:resources].first
55
- if @resource == 'null'
56
- render 'apipie_404', :status => 404
57
- else
58
- render 'resource'
59
- end
29
+ @resource = @doc[:resources].first if params[:resource].present?
30
+ @method = @resource[:methods].first if params[:method].present?
31
+
32
+ if @resource && @method
33
+ render 'method'
34
+ elsif @resource
35
+ render 'resource'
36
+ elsif params[:resource].present? || params[:method].present?
37
+ render 'apipie_404', :status => 404
60
38
  else
61
39
  render 'index'
62
40
  end
@@ -64,5 +42,59 @@ module Apipie
64
42
  end
65
43
  end
66
44
 
45
+ private
46
+
47
+ def get_format
48
+ params[:format] = :html unless params[:version].sub!('.html', '').nil?
49
+ params[:format] = :json unless params[:version].sub!('.json', '').nil?
50
+ request.format = params[:format] if params[:format]
51
+ end
52
+
53
+ def render_from_cache
54
+ path = Apipie.configuration.doc_base_url.dup
55
+ if [:resource, :method, :format].any? { |p| params[p].to_s =~ /\W/ }
56
+ head :bad_request and return
57
+ end
58
+ # version can contain dot, but only one in row
59
+ if params[:version].to_s.gsub(".", "") =~ /\W/ ||
60
+ params[:version].to_s =~ /\.\./
61
+ head :bad_request and return
62
+ end
63
+
64
+ path << "/" << params[:version] if params[:version].present?
65
+ path << "/" << params[:resource] if params[:resource].present?
66
+ path << "/" << params[:method] if params[:method].present?
67
+ if params[:format].present?
68
+ path << ".#{params[:format]}"
69
+ else
70
+ path << ".html"
71
+ end
72
+
73
+ # we sanitize the params before so in ideal case, this condition
74
+ # will be never satisfied. It's here for cases somebody adds new
75
+ # param into the path later and forgets about sanitation.
76
+ if path =~ /\.\./
77
+ head :bad_request and return
78
+ end
79
+
80
+ cache_file = File.join(Apipie.configuration.cache_dir, path)
81
+ if File.exists?(cache_file)
82
+ content_type = case params[:format]
83
+ when "json" then "application/json"
84
+ else "text/html"
85
+ end
86
+ send_file cache_file, :type => content_type, :disposition => "inline"
87
+ else
88
+ Rails.logger.error("API doc cache not found for '#{path}'. Perhaps you have forgot to run `rake apipie:cache`")
89
+ head :not_found
90
+ end
91
+ end
92
+
93
+ def set_script_name
94
+ Apipie.request_script_name = request.env["SCRIPT_NAME"]
95
+ yield
96
+ ensure
97
+ Apipie.request_script_name = nil
98
+ end
67
99
  end
68
100
  end
@@ -1,14 +1,21 @@
1
1
  <ul class='breadcrumb'>
2
- <li class='active'><a href='<%= @doc[:doc_url] %>.html'><%= @doc[:name] %></a></li>
2
+ <li class='active'><a href='<%= @doc[:doc_url] %>.html'><%= @doc[:name] %> <%= @doc[:resources].values.first[:version] %></a></li>
3
+ <% if @versions && @versions.size > 1 %>
4
+ <li class='pull-right'>
5
+ <%= @versions.collect { |v| link_to v, Apipie.full_url(v) }.join(' / ').html_safe %>
6
+ </li>
7
+ <% end %>
3
8
  </ul>
4
9
 
5
- <div><%= @doc[:info].html_safe %></div>
10
+ <div><%= raw @doc[:info] %></div>
6
11
 
7
12
  <h1 class='page-header'>Resources</h1>
8
13
 
9
14
  <% @doc[:resources].sort_by(&:first).each do |key, api| %>
10
15
  <h2>
11
- <a href='<%= api[:doc_url] %>.html'><%= api[:name] %></a><br>
16
+ <a href='<%= api[:doc_url] %>.html'>
17
+ <%= api[:name] %>
18
+ </a><br>
12
19
  <small><%= api[:short_description] %></small>
13
20
  </h2>
14
21
  <table class='table'>
@@ -1,10 +1,13 @@
1
1
  <ul class='breadcrumb'>
2
2
  <li>
3
- <a href='<%= @doc[:doc_url] %>.html'><%= @doc[:name] %></a>
3
+ <a href='<%= @doc[:doc_url] %>.html'><%= @doc[:name] %> <%= @resource[:version] %></a>
4
4
  <span class='divider'>/</span>
5
5
  </li>
6
6
  <li>
7
- <a href='<%= @resource[:doc_url] %>.html'><%= @resource[:name] %></a>
7
+ <a href='<%= @resource[:doc_url] %>.html'>
8
+ <%= @resource[:name] %>
9
+ <% if @resource[:version] %><% end %>
10
+ </a>
8
11
  <span class='divider'>/</span>
9
12
  </li>
10
13
  <li class='active'><%= @method[:name] %></li>
@@ -1,11 +1,18 @@
1
1
  <ul class='breadcrumb'>
2
- <li><a href='<%= @doc[:doc_url] %>.html'><%= @doc[:name] %></a><span class='divider'>/</span></li>
3
- <li class='active'><%= @resource[:name] %></li>
2
+ <li>
3
+ <a href='<%= @doc[:doc_url] %>.html'><%= @doc[:name] %> <%= @resource[:version] %></a>
4
+ <span class='divider'>/</span>
5
+ </li>
6
+ <li class='active'>
7
+ <%= @resource[:name] %>
8
+ <% if @resource[:version] %><% end %>
9
+ </li>
4
10
  </ul>
5
11
 
6
12
  <div class='page-header'>
7
13
  <h1>
8
- <%= @resource[:name] %><br>
14
+ <%= @resource[:name] %>
15
+ <br>
9
16
  <small><%= raw @resource[:short_description] %></small>
10
17
  </h1>
11
18
  </div>
@@ -1,6 +1,7 @@
1
1
  require "apipie/routing"
2
2
  require "apipie/markup"
3
3
  require "apipie/apipie_module"
4
+ require "apipie/configuration"
4
5
  require "apipie/method_description"
5
6
  require "apipie/resource_description"
6
7
  require "apipie/param_description"
@@ -8,8 +8,9 @@ module Apipie
8
8
  @application ||= Apipie::Application.new
9
9
  end
10
10
 
11
- def self.to_json(resource_name = nil, method_name = nil)
12
- app.to_json(resource_name, method_name)
11
+ def self.to_json(version = nil, resource_name = nil, method_name = nil)
12
+ version ||= Apipie.configuration.default_version
13
+ app.to_json(version, resource_name, method_name)
13
14
  end
14
15
 
15
16
  # all calls delegated to Apipie::Application instance
@@ -25,89 +26,37 @@ module Apipie
25
26
  @configuration ||= Configuration.new
26
27
  end
27
28
 
28
- class Configuration
29
- attr_accessor :app_name, :app_info, :copyright, :markup, :disqus_shortname,
30
- :validate, :api_base_url, :doc_base_url, :required_by_default, :layout
31
-
32
- alias_method :validate?, :validate
33
- alias_method :required_by_default?, :required_by_default
34
-
35
- # matcher to be used in Dir.glob to find controllers to be reloaded e.g.
36
- #
37
- # "#{Rails.root}/app/controllers/api/*.rb"
38
- attr_accessor :api_controllers_matcher
39
-
40
- # set to true if you want to reload the controllers at each refresh of the
41
- # documentation. It requires +:api_controllers_matcher+ to be set to work
42
- # properly.
43
- attr_writer :reload_controllers
44
-
45
- def reload_controllers?
46
- @reload_controllers = Rails.env.development? unless defined? @reload_controllers
47
- return @reload_controllers && @api_controllers_matcher
48
- end
49
-
50
- # set to true if you want to use pregenerated documentation cache and avoid
51
- # generating the documentation on runtime (usefull for production
52
- # environment).
53
- # You can generate the cache by running
54
- #
55
- # rake apipie:cache
56
- attr_accessor :use_cache
57
- alias_method :use_cache?, :use_cache
58
-
59
- attr_writer :cache_dir
60
- def cache_dir
61
- @cache_dir ||= File.join(Rails.root, "public", "apipie-cache")
62
- end
63
-
64
- # if there is not obvious reason why the DSL should be turned on (no
65
- # validations, cache turned on etc.), it's disabled to avoid unneeded
66
- # allocation. It you need the DSL for other reasons, you can force the
67
- # activation.
68
- attr_writer :force_dsl
69
- def force_dsl?
70
- @force_dsl
71
- end
72
-
73
- # array of controller names (strings) (might include actions as well)
74
- # to be ignored # when extracting description form calls.
75
- # e.g. %w[Api::CommentsController Api::PostsController#post]
76
- attr_writer :ignored_by_recorder
77
- def ignored_by_recorder
78
- @ignored_by_recorder ||= []
79
- @ignored_by_recorder.map(&:to_s)
80
- end
29
+ def self.debug(message)
30
+ puts message if Apipie.configuration.debug
31
+ end
81
32
 
82
- # comment to put before docs that was generated automatically. It's used to
83
- # determine if the description should be overwritten next recording.
84
- # If you want to keep the documentation (prevent from overriding), remove
85
- # the line above the docs.
86
- attr_writer :generated_doc_disclaimer
87
- def generated_doc_disclaimer
88
- @generated_doc_disclaimer ||= "# DOC GENERATED AUTOMATICALLY: REMOVE THIS LINE TO PREVENT REGENARATING NEXT TIME"
33
+ # get application description for given or default version
34
+ def self.app_info(version = nil)
35
+ if app_info_version_valid? version
36
+ Apipie.markup_to_html(self.configuration.app_info[version])
37
+ elsif app_info_version_valid? Apipie.configuration.default_version
38
+ Apipie.markup_to_html(self.configuration.app_info[Apipie.configuration.default_version])
39
+ else
40
+ "Another API description"
89
41
  end
42
+ end
90
43
 
91
- def use_disqus?
92
- !@disqus_shortname.blank?
44
+ def self.api_base_url(version = nil)
45
+ if api_base_url_version_valid? version
46
+ self.configuration.api_base_url[version]
47
+ elsif api_base_url_version_valid? Apipie.configuration.default_version
48
+ self.configuration.api_base_url[Apipie.configuration.default_version]
49
+ else
50
+ "/api"
93
51
  end
52
+ end
94
53
 
95
- def app_info
96
- Apipie.markup_to_html(@app_info)
97
- end
54
+ def self.app_info_version_valid?(version)
55
+ version && self.configuration.app_info.has_key?(version)
56
+ end
98
57
 
99
- def initialize
100
- @markup = Apipie::Markup::RDoc.new
101
- @app_name = "Another API"
102
- @app_info = "Another API description"
103
- @copyright = nil
104
- @validate = true
105
- @required_by_default = false
106
- @api_base_url = ""
107
- @doc_base_url = "/apipie"
108
- @layout = "apipie/apipie"
109
- @disqus_shortname = nil
110
- end
58
+ def self.api_base_url_version_valid?(version)
59
+ version && self.configuration.api_base_url.has_key?(version)
111
60
  end
112
61
 
113
62
  end
@@ -12,147 +12,210 @@ module Apipie
12
12
  end
13
13
  end
14
14
 
15
- attr_accessor :last_api_args, :last_errors, :last_params, :last_description, :last_examples, :last_see, :last_formats
16
- attr_reader :method_descriptions, :resource_descriptions
15
+ attr_accessor :last_dsl_data
16
+
17
+ attr_reader :resource_descriptions
17
18
 
18
19
  def initialize
19
20
  super
20
- @method_descriptions = Hash.new
21
- @resource_descriptions = Hash.new
21
+ init_env
22
22
  clear_last
23
23
  end
24
24
 
25
+ def available_versions
26
+ @resource_descriptions.keys.sort
27
+ end
28
+
29
+ def set_resource_id(controller, resource_id)
30
+ @controller_to_resource_id[controller] = resource_id
31
+ end
32
+
25
33
  # create new method api description
26
- def define_method_description(controller, method_name)
27
- # create new or get existing api
28
- resource_name = get_resource_name(controller)
29
- key = [resource_name, method_name].join('#')
30
- # add method description key to resource description
31
- resource = define_resource_description(controller)
34
+ def define_method_description(controller, method_name, versions = [])
35
+ return if ignored?(controller, method_name)
36
+ ret_method_description = nil
37
+
38
+ versions = controller_versions(controller) if versions.empty?
39
+
40
+ versions.each do |version|
41
+ resource_name_with_version = "#{version}##{get_resource_name(controller)}"
42
+ resource_description = get_resource_description(resource_name_with_version)
32
43
 
33
- method_description = Apipie::MethodDescription.new(method_name, resource, self)
44
+ if resource_description.nil?
45
+ resource_description = define_resource_description(controller, version)
46
+ end
47
+
48
+ method_description = Apipie::MethodDescription.new(method_name, resource_description, self)
34
49
 
35
- @method_descriptions[key] ||= method_description
50
+ # we create separate method description for each version in
51
+ # case the method belongs to more versions. We return just one
52
+ # becuase the version doesn't matter for the purpose it's used
53
+ # (to wrap the original version with validators)
54
+ ret_method_description ||= method_description
55
+ resource_description.add_method_description(method_description)
56
+ end
36
57
 
37
- @method_descriptions[key]
58
+ return ret_method_description
38
59
  end
39
60
 
40
61
  # create new resource api description
41
- def define_resource_description(controller, &block)
62
+ def define_resource_description(controller, version, dsl_data = nil)
63
+ return if ignored?(controller)
64
+
42
65
  resource_name = get_resource_name(controller)
66
+ resource_description = @resource_descriptions[version][resource_name]
67
+ if resource_description
68
+ # we already defined the description somewhere (probably in
69
+ # some method. Updating just meta data from dsl
70
+ resource_description.update_from_dsl_data(dsl_data) if dsl_data
71
+ else
72
+ resource_description = Apipie::ResourceDescription.new(controller, resource_name, dsl_data, version)
73
+
74
+ Apipie.debug("@resource_descriptions[#{version}][#{resource_name}] = #{resource_description}")
75
+ @resource_descriptions[version][resource_name] ||= resource_description
76
+ end
77
+
78
+ return resource_description
79
+ end
43
80
 
44
- # puts "defining api for #{resource_name}"
81
+ # recursively searches what versions has the controller specified in
82
+ # resource_description? It's used to derivate the default value of
83
+ # versions for methods.
84
+ def controller_versions(controller)
85
+ ret = @controller_versions[controller]
86
+ return ret unless ret.empty?
87
+ if controller == ActionController::Base || controller.nil?
88
+ return [Apipie.configuration.default_version]
89
+ else
90
+ return controller_versions(controller.superclass)
91
+ end
92
+ end
45
93
 
46
- @resource_descriptions[resource_name] ||=
47
- Apipie::ResourceDescription.new(controller, resource_name, &block)
94
+ def set_controller_versions(controller, versions)
95
+ @controller_versions[controller] = versions
48
96
  end
49
97
 
50
98
  def add_method_description_args(method, path, desc)
51
- @last_api_args << MethodDescription::Api.new(method, path, desc)
99
+ @last_dsl_data[:api_args] << MethodDescription::Api.new(method, path, desc)
52
100
  end
53
101
 
54
102
  def add_example(example)
55
- @last_examples << example.strip_heredoc
103
+ @last_dsl_data[:examples] << example.strip_heredoc
56
104
  end
57
105
 
58
106
  # check if there is some saved description
59
107
  def apipie_provided?
60
- true unless last_api_args.blank?
108
+ true unless last_dsl_data[:api_args].blank?
61
109
  end
62
110
 
63
111
  # get api for given method
64
112
  #
65
113
  # There are two ways how this method can be used:
66
114
  # 1) Specify both parameters
67
- # resource_name: controller class or string with resource name (plural)
115
+ # resource_name:
116
+ # controller class - UsersController
117
+ # string with resource name (plural) and version - "v1#users"
68
118
  # method_name: name of the method (string or symbol)
119
+ #
69
120
  # 2) Specify only first parameter:
70
121
  # resource_name: string containing both resource and method name joined
71
- # with # (eg. "users#create")
122
+ # with '#' symbol.
123
+ # - "users#create" get default version
124
+ # - "v2#users#create" get specific version
72
125
  def get_method_description(resource_name, method_name = nil)
73
- resource_name = get_resource_name(resource_name)
74
- key = method_name.blank? ? resource_name : [resource_name, method_name].join('#')
75
- @method_descriptions[key]
126
+ if resource_name.is_a?(String)
127
+ crumbs = resource_name.split('#')
128
+ if method_name.nil?
129
+ method_name = crumbs.pop
130
+ end
131
+ resource_name = crumbs.join("#")
132
+ resource_description = get_resource_description(resource_name)
133
+ elsif resource_name.respond_to? :apipie_resource_descriptions
134
+ resource_description = get_resource_description(resource_name)
135
+ else
136
+ raise ArgumentError.new("Resource #{resource_name} does not exists.")
137
+ end
138
+ unless resource_description.nil?
139
+ resource_description.method_description(method_name.to_sym)
140
+ end
76
141
  end
77
142
  alias :[] :get_method_description
78
143
 
79
- # get api for given resource
80
- def get_resource_description(resource_name)
81
- resource_name = get_resource_name(resource_name)
82
-
83
- @resource_descriptions[resource_name]
84
- end
85
-
86
- def remove_method_description(resource_name, method_name)
87
- resource_name = get_resource_name(resource_name)
88
-
89
- @method_descriptions.delete [resource_name, method_name].join('#')
90
- end
91
-
92
- def remove_resource_description(resource_name)
93
- resource_name = get_resource_name(resource_name)
94
-
95
- @resource_descriptions.delete resource_name
96
- end
97
-
98
- # Clear all apis in this application.
99
- def clear
100
- @resource_descriptions.clear
101
- @method_descriptions.clear
102
- end
103
-
104
- # clear all saved data
105
- def clear_last
106
- @last_api_args = []
107
- @last_errors = []
108
- @last_params = []
109
- @last_description = nil
110
- @last_examples = []
111
- @last_see = nil
112
- @last_formats = []
113
- end
144
+ # options:
145
+ # => "users"
146
+ # => "v2#users"
147
+ # => V2::UsersController
148
+ def get_resource_description(resource, version = nil)
149
+ if resource.is_a?(String)
150
+ crumbs = resource.split('#')
151
+ if crumbs.size == 2
152
+ version = crumbs.first
153
+ end
154
+ version ||= Apipie.configuration.default_version
155
+ if @resource_descriptions.has_key?(version)
156
+ return @resource_descriptions[version][crumbs.last]
157
+ end
158
+ else
159
+ resource_name = get_resource_name(resource)
160
+ if version
161
+ resource_name = "#{version}##{resource_name}"
162
+ end
114
163
 
115
- # Return the current description, clearing it in the process.
116
- def get_description
117
- desc = @last_description
118
- @last_description = nil
119
- desc
164
+ if resource_name.nil?
165
+ return nil
166
+ end
167
+ resource_description = get_resource_description(resource_name)
168
+ if resource_description && resource_description.controller == resource
169
+ return resource_description
170
+ end
171
+ end
120
172
  end
121
173
 
122
- def get_errors
123
- errors = @last_errors.clone
124
- @last_errors.clear
125
- errors
174
+ # get all versions of resource description
175
+ def get_resource_descriptions(resource)
176
+ available_versions.map do |version|
177
+ get_resource_description(resource, version)
178
+ end.compact
126
179
  end
127
180
 
128
- def get_api_args
129
- api_args = @last_api_args.clone
130
- @last_api_args.clear
131
- api_args
181
+ # get all versions of method description
182
+ def get_method_descriptions(resource, method)
183
+ get_resource_descriptions(resource).map do |resource_description|
184
+ resource_description.method_description(method.to_sym)
185
+ end.compact
132
186
  end
133
187
 
134
- def get_see
135
- see = @last_see
136
- @last_see = nil
137
- see
188
+ def remove_method_description(resource, versions, method_name)
189
+ versions.each do |version|
190
+ resource = get_resource_name(resource)
191
+ if resource_description = get_resource_description("#{version}##{resource}")
192
+ resource_description.remove_method_description(method_name)
193
+ end
194
+ end
138
195
  end
139
196
 
140
- def get_formats
141
- formats = @last_formats
142
- @last_formats = nil
143
- formats
144
- end
197
+ # initialize variables for gathering dsl data
198
+ def init_env
199
+ @resource_descriptions = HashWithIndifferentAccess.new { |h, version| h[version] = {} }
200
+ @controller_to_resource_id = {}
145
201
 
146
- def get_params
147
- params = @last_params.clone
148
- @last_params.clear
149
- params
202
+ # what versions does the controller belong in (specified by resource_description)?
203
+ @controller_versions = Hash.new { |h, controller| h[controller] = [] }
150
204
  end
151
-
152
- def get_examples
153
- examples = @last_examples.clone
154
- @last_examples.clear
155
- examples
205
+ # clear all saved data
206
+ def clear_last
207
+ @last_dsl_data = {
208
+ :api_args => [],
209
+ :errors => [],
210
+ :params => [],
211
+ :resouce_id => nil,
212
+ :short_description => nil,
213
+ :description => nil,
214
+ :examples => [],
215
+ :see => nil,
216
+ :formats => nil,
217
+ :api_versions => []
218
+ }
156
219
  end
157
220
 
158
221
  def recorded_examples
@@ -170,26 +233,28 @@ module Apipie
170
233
  @recorded_examples = nil
171
234
  end
172
235
 
173
- def to_json(resource_name, method_name)
236
+ def to_json(version, resource_name, method_name)
174
237
 
175
238
  _resources = if resource_name.blank?
176
239
  # take just resources which have some methods because
177
240
  # we dont want to show eg ApplicationController as resource
178
- resource_descriptions.inject({}) do |result, (k,v)|
241
+ resource_descriptions[version].inject({}) do |result, (k,v)|
179
242
  result[k] = v.to_json unless v._methods.blank?
180
243
  result
181
244
  end
182
245
  else
183
- [@resource_descriptions[resource_name].to_json(method_name)]
246
+ [@resource_descriptions[version][resource_name].to_json(method_name)]
184
247
  end
185
248
 
249
+ url_args = Apipie.configuration.version_in_url ? version : ''
250
+
186
251
  {
187
252
  :docs => {
188
253
  :name => Apipie.configuration.app_name,
189
- :info => Apipie.configuration.app_info,
254
+ :info => Apipie.app_info(version),
190
255
  :copyright => Apipie.configuration.copyright,
191
- :doc_url => Apipie.full_url(""),
192
- :api_url => Apipie.configuration.api_base_url,
256
+ :doc_url => Apipie.full_url(url_args),
257
+ :api_url => Apipie.api_base_url(version),
193
258
  :resources => _resources
194
259
  }
195
260
  }
@@ -200,7 +265,10 @@ module Apipie
200
265
  end
201
266
 
202
267
  def reload_documentation
268
+ rails_mark_classes_for_reload
269
+ init_env
203
270
  reload_examples
271
+
204
272
  api_controllers_paths.each do |f|
205
273
  load_controller_from_file f
206
274
  end
@@ -218,8 +286,21 @@ module Apipie
218
286
  def get_resource_name(klass)
219
287
  if klass.class == String
220
288
  klass
289
+ elsif @controller_to_resource_id.has_key?(klass)
290
+ @controller_to_resource_id[klass]
221
291
  elsif klass.respond_to?(:controller_name)
292
+ return nil if klass == ActionController::Base
222
293
  klass.controller_name
294
+ else
295
+ raise "Apipie: Can not resolve resource #{klass} name."
296
+ end
297
+ end
298
+
299
+ def get_resource_version(resource_description)
300
+ if resource_description.respond_to? :_version
301
+ resource_description._version
302
+ else
303
+ Apipie.configuration.default_version
223
304
  end
224
305
  end
225
306
 
@@ -228,5 +309,21 @@ module Apipie
228
309
  controller_class_name.constantize
229
310
  end
230
311
 
312
+ def ignored?(controller, method = nil)
313
+ ignored = Apipie.configuration.ignored
314
+ return true if ignored.include?(controller.name)
315
+ return true if ignored.include?("#{controller.name}##{method}")
316
+ end
317
+
318
+ # Since Rails 3.2, the classes are reloaded only on file change.
319
+ # We need to reload all the controller classes to rebuild the
320
+ # docs, therefore we just force to reload all the code. This
321
+ # happens only when reload_controllers is set to true and only
322
+ # when showing the documentation.
323
+ def rails_mark_classes_for_reload
324
+ ActiveSupport::DescendantsTracker.clear
325
+ ActiveSupport::Dependencies.clear
326
+ end
327
+
231
328
  end
232
329
  end