apipie-rails 0.0.13 → 0.0.14

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.
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