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
@@ -17,12 +17,13 @@ module Apipie
17
17
  argument :name
18
18
  argument :subject
19
19
  argument :suffix
20
+ argument :version
20
21
 
21
22
  attr_reader :doc, :resource, :resource_key
22
23
 
23
24
  def initialize(*args)
24
25
  super
25
- @doc = Apipie.to_json()[:docs]
26
+ @doc = Apipie.to_json(version)[:docs]
26
27
  end
27
28
 
28
29
  def self.source_root
@@ -33,10 +34,10 @@ module Apipie
33
34
  File.join(FileUtils.pwd, "#{name}#{suffix}")
34
35
  end
35
36
 
36
- def self.start(client_name, subject = :all, suffix = '_client')
37
- name = client_name.parameterize.underscore
37
+ def self.start(client_name, subject = :all, suffix = '_client', version = nil)
38
+ name = client_name.parameterize.underscore
38
39
  suffix = suffix.parameterize.underscore
39
- super([name, subject, suffix], :destination_root => destination_root(name, suffix))
40
+ super([name, subject, suffix, version], :destination_root => destination_root(name, suffix))
40
41
  end
41
42
 
42
43
  def all?
@@ -0,0 +1,112 @@
1
+ module Apipie
2
+ class Configuration
3
+
4
+ attr_accessor :app_name, :app_info, :copyright, :markup, :disqus_shortname,
5
+ :validate, :api_base_url, :doc_base_url, :required_by_default, :layout,
6
+ :default_version, :debug, :version_in_url
7
+
8
+ alias_method :validate?, :validate
9
+ alias_method :required_by_default?, :required_by_default
10
+
11
+ # matcher to be used in Dir.glob to find controllers to be reloaded e.g.
12
+ #
13
+ # "#{Rails.root}/app/controllers/api/*.rb"
14
+ attr_accessor :api_controllers_matcher
15
+
16
+ # set to true if you want to reload the controllers at each refresh of the
17
+ # documentation. It requires +:api_controllers_matcher+ to be set to work
18
+ # properly.
19
+ attr_writer :reload_controllers
20
+
21
+ def reload_controllers?
22
+ @reload_controllers = Rails.env.development? unless defined? @reload_controllers
23
+ return @reload_controllers && @api_controllers_matcher
24
+ end
25
+
26
+ # set to true if you want to use pregenerated documentation cache and avoid
27
+ # generating the documentation on runtime (usefull for production
28
+ # environment).
29
+ # You can generate the cache by running
30
+ #
31
+ # rake apipie:cache
32
+ attr_accessor :use_cache
33
+ alias_method :use_cache?, :use_cache
34
+
35
+ attr_writer :cache_dir
36
+ def cache_dir
37
+ @cache_dir ||= File.join(Rails.root, "public", "apipie-cache")
38
+ end
39
+
40
+ # if there is not obvious reason why the DSL should be turned on (no
41
+ # validations, cache turned on etc.), it's disabled to avoid unneeded
42
+ # allocation. It you need the DSL for other reasons, you can force the
43
+ # activation.
44
+ attr_writer :force_dsl
45
+ def force_dsl?
46
+ @force_dsl
47
+ end
48
+
49
+ # array of controller names (strings) (might include actions as well)
50
+ # to be ignored # when extracting description form calls.
51
+ # e.g. %w[Api::CommentsController Api::PostsController#post]
52
+ attr_writer :ignored_by_recorder
53
+ def ignored_by_recorder
54
+ @ignored_by_recorder ||= []
55
+ @ignored_by_recorder.map(&:to_s)
56
+ end
57
+
58
+ # array of controller names (strings) (might include actions as well)
59
+ # to be ignored # when generationg the documentation
60
+ # e.g. %w[Api::CommentsController Api::PostsController#post]
61
+ attr_writer :ignored
62
+ def ignored
63
+ @ignored ||= []
64
+ @ignored.map(&:to_s)
65
+ end
66
+
67
+ # comment to put before docs that was generated automatically. It's used to
68
+ # determine if the description should be overwritten next recording.
69
+ # If you want to keep the documentation (prevent from overriding), remove
70
+ # the line above the docs.
71
+ attr_writer :generated_doc_disclaimer
72
+ def generated_doc_disclaimer
73
+ @generated_doc_disclaimer ||= "# DOC GENERATED AUTOMATICALLY: REMOVE THIS LINE TO PREVENT REGENARATING NEXT TIME"
74
+ end
75
+
76
+ def use_disqus?
77
+ !@disqus_shortname.blank?
78
+ end
79
+
80
+ # set app description for default version
81
+ # to maintain backward compatibility
82
+ # new way: config.app_info[version] = description
83
+ def app_info=(description)
84
+ version = Apipie.configuration.default_version
85
+ @app_info[version] = description
86
+ end
87
+
88
+ # set base url for default version of API
89
+ # to set it for specific version use
90
+ # config.api_base_url[version] = url
91
+ def api_base_url=(url)
92
+ version = Apipie.configuration.default_version
93
+ @api_base_url[version] = url
94
+ end
95
+
96
+ def initialize
97
+ @markup = Apipie::Markup::RDoc.new
98
+ @app_name = "Another API"
99
+ @app_info = HashWithIndifferentAccess.new
100
+ @copyright = nil
101
+ @validate = true
102
+ @required_by_default = false
103
+ @api_base_url = HashWithIndifferentAccess.new
104
+ @doc_base_url = "/apipie"
105
+ @layout = "apipie/apipie"
106
+ @disqus_shortname = nil
107
+ @default_version = "1.0"
108
+ @debug = false
109
+ @version_in_url = true
110
+ end
111
+ end
112
+ end
@@ -5,6 +5,8 @@ module Apipie
5
5
  # DSL is a module that provides #api, #error, #param, #error.
6
6
  module DSL
7
7
 
8
+ attr_reader :apipie_resource_descriptions
9
+
8
10
  private
9
11
 
10
12
  # Describe whole resource
@@ -17,10 +19,74 @@ module Apipie
17
19
  # EOS
18
20
  def resource_description(options = {}, &block) #:doc:
19
21
  return unless Apipie.active_dsl?
20
- Apipie.remove_resource_description(self)
21
- Apipie.define_resource_description(self, &block) if block_given?
22
+ raise ArgumentError, "Block expected" unless block_given?
23
+
24
+ dsl_data = ResourceDescriptionDsl.eval_dsl(self, &block)
25
+ versions = dsl_data[:api_versions]
26
+ @apipie_resource_descriptions = versions.map do |version|
27
+ Apipie.define_resource_description(self, version, dsl_data)
28
+ end
29
+ Apipie.set_controller_versions(self, versions)
22
30
  end
23
31
 
32
+ class ResourceDescriptionDsl
33
+ include Apipie::DSL
34
+
35
+ NO_RESOURCE_KEYWORDS = %w[api see example]
36
+
37
+ NO_RESOURCE_KEYWORDS.each do |method|
38
+ define_method method do
39
+ raise "#{method} is not defined for resource description"
40
+ end
41
+ end
42
+
43
+ def initialize(controller)
44
+ @controller = controller
45
+ end
46
+
47
+ # by default, the resource id is derived from controller_name
48
+ # it can be overwritten with.
49
+ #
50
+ # resource_id "my_own_resource_id"
51
+ def resource_id(resource_id)
52
+ Apipie.set_resource_id(@controller, resource_id)
53
+ end
54
+
55
+ def name(name)
56
+ Apipie.last_dsl_data[:resource_name] = name
57
+ end
58
+
59
+ def api_base_url(url)
60
+ Apipie.last_dsl_data[:api_base_url] = url
61
+ end
62
+
63
+ def short(short)
64
+ Apipie.last_dsl_data[:short_description] = short
65
+ end
66
+ alias :short_description :short
67
+
68
+ def path(path)
69
+ Apipie.last_dsl_data[:path] = path
70
+ end
71
+
72
+ def app_info(app_info)
73
+ Apipie.last_dsl_data[:app_info] = app_info
74
+ end
75
+
76
+ # evaluates resource description DSL and returns results
77
+ def self.eval_dsl(controller, &block)
78
+ self.new(controller).instance_eval(&block)
79
+ dsl_data = Apipie.last_dsl_data
80
+ if dsl_data[:api_versions].empty?
81
+ dsl_data[:api_versions] = Apipie.controller_versions(controller)
82
+ end
83
+ dsl_data
84
+ ensure
85
+ Apipie.clear_last
86
+ end
87
+ end
88
+
89
+
24
90
  # Declare an api.
25
91
  #
26
92
  # Example:
@@ -31,6 +97,11 @@ module Apipie
31
97
  Apipie.add_method_description_args(method, path, desc)
32
98
  end
33
99
 
100
+ def api_versions(*versions)
101
+ Apipie.last_dsl_data[:api_versions].concat(versions)
102
+ end
103
+ alias :api_version :api_versions
104
+
34
105
  # Describe the next method.
35
106
  #
36
107
  # Example:
@@ -41,12 +112,13 @@ module Apipie
41
112
  #
42
113
  def desc(description) #:doc:
43
114
  return unless Apipie.active_dsl?
44
- if Apipie.last_description
115
+ if Apipie.last_dsl_data[:description]
45
116
  raise "Double method description."
46
117
  end
47
- Apipie.last_description = description
118
+ Apipie.last_dsl_data[:description] = description
48
119
  end
49
120
  alias :description :desc
121
+ alias :full_description :desc
50
122
 
51
123
  # Reference other similar method
52
124
  #
@@ -55,8 +127,8 @@ module Apipie
55
127
  # def update; end
56
128
  def see(method_key)
57
129
  return unless Apipie.active_dsl?
58
- raise "'See' method called twice." if Apipie.last_see
59
- Apipie.last_see = method_key
130
+ raise "'See' method called twice." if Apipie.last_dsl_data[:see]
131
+ Apipie.last_dsl_data[:see] = method_key
60
132
  end
61
133
 
62
134
  # Show some example of what does the described
@@ -71,7 +143,7 @@ module Apipie
71
143
  # formats ['json', 'jsonp', 'xml']
72
144
  def formats(formats) #:doc:
73
145
  return unless Apipie.active_dsl?
74
- Apipie.last_formats = formats
146
+ Apipie.last_dsl_data[:formats] = formats
75
147
  end
76
148
 
77
149
  # Describe possible errors
@@ -86,7 +158,7 @@ module Apipie
86
158
  #
87
159
  def error(*args) #:doc:
88
160
  return unless Apipie.active_dsl?
89
- Apipie.last_errors << Apipie::ErrorDescription.new(args)
161
+ Apipie.last_dsl_data[:errors] << Apipie::ErrorDescription.new(args)
90
162
  end
91
163
 
92
164
  # Describe method's parameter
@@ -99,22 +171,28 @@ module Apipie
99
171
  #
100
172
  def param(param_name, validator, desc_or_options = nil, options = {}, &block) #:doc:
101
173
  return unless Apipie.active_dsl?
102
- Apipie.last_params << Apipie::ParamDescription.new(param_name, validator, desc_or_options, options, &block)
174
+ Apipie.last_dsl_data[:params] << Apipie::ParamDescription.new(param_name, validator, desc_or_options, options, &block)
103
175
  end
104
176
 
105
177
  # create method api and redefine newly added method
106
178
  def method_added(method_name) #:doc:
107
179
  super
108
180
 
109
- return unless Apipie.active_dsl?
110
- return unless Apipie.apipie_provided?
181
+ if ! Apipie.active_dsl? || ! Apipie.apipie_provided?
182
+ Apipie.clear_last
183
+ return
184
+ end
111
185
 
112
- # remove method description if exists and create new one
113
- Apipie.remove_method_description(self, method_name)
114
- description = Apipie.define_method_description(self, method_name)
186
+ begin
187
+ # remove method description if exists and create new one
188
+ Apipie.remove_method_description(self, Apipie.last_dsl_data[:api_versions], method_name)
189
+ description = Apipie.define_method_description(self, method_name, Apipie.last_dsl_data[:api_versions])
190
+ ensure
191
+ Apipie.clear_last
192
+ end
115
193
 
116
194
  # redefine method only if validation is turned on
117
- if Apipie.configuration.validate == true
195
+ if description && Apipie.configuration.validate == true
118
196
 
119
197
  old_method = instance_method(method_name)
120
198
 
@@ -141,7 +219,6 @@ module Apipie
141
219
  end
142
220
 
143
221
  end
144
-
145
222
  end # def method_added
146
223
  end # module DSL
147
224
  end # module Apipie
@@ -53,10 +53,11 @@ module Apipie
53
53
  Writer.new(@collector).write_examples if @collector
54
54
  end
55
55
 
56
+ # TODO: this is a loooooooooong method :)
56
57
  def apis_from_routes
57
58
  return @apis_from_routes if @apis_from_routes
58
59
 
59
- api_prefix = Apipie.configuration.api_base_url.sub(/\/$/,"")
60
+ api_prefix = Apipie.api_base_url.sub(/\/$/,"")
60
61
  all_routes = Rails.application.routes.routes.map do |r|
61
62
  {
62
63
  :verb => case r.verb
@@ -74,7 +75,7 @@ module Apipie
74
75
 
75
76
  end
76
77
  api_routes = all_routes.find_all do |r|
77
- r[:path].starts_with?(Apipie.configuration.api_base_url)
78
+ r[:path].starts_with?(Apipie.api_base_url)
78
79
  end
79
80
 
80
81
  @apis_from_routes = Hash.new { |h, k| h[k] = [] }
@@ -97,9 +98,12 @@ module Apipie
97
98
  end
98
99
  @apis_from_routes
99
100
 
100
- apis_from_docs = Apipie.method_descriptions.reduce({}) do |h, (method, desc)|
101
+ method_descriptions = Apipie.resource_descriptions.map(&:method_descriptions).flatten
102
+ apis_from_docs = method_descriptions.reduce({}) do |h, (method, desc)|
101
103
  apis = desc.apis.map do |api|
102
- {:method => api.http_method, :path => api.api_url, :desc => api.short_description}
104
+ { :method => api.http_method,
105
+ :path => api.api_url,
106
+ :desc => api.short_description }
103
107
  end
104
108
  h.update(method => apis)
105
109
  end
@@ -109,7 +113,10 @@ module Apipie
109
113
  old_apis = apis_from_docs[method_key] || []
110
114
  new_apis.each do |new_api|
111
115
  new_api[:path].sub!(/\(\.:format\)$/,"")
112
- if old_api = old_apis.find { |api| api[:path] == "#{api_prefix}#{new_api[:path]}" }
116
+ old_api = old_apis.find do |api|
117
+ api[:path] == "#{api_prefix}#{new_api[:path]}"
118
+ end
119
+ if old_api
113
120
  new_api[:desc] = old_api[:desc]
114
121
  end
115
122
  end
@@ -92,7 +92,7 @@ module Apipie
92
92
  end
93
93
 
94
94
  def add_routes_info(desc)
95
- api_prefix = Apipie.configuration.api_base_url.sub(/\/$/,"")
95
+ api_prefix = Apipie.api_base_url.sub(/\/$/,"")
96
96
  desc[:api] = Apipie::Extractor.apis_from_routes[[desc[:controller].name, desc[:action]]]
97
97
  if desc[:api]
98
98
  desc[:params].each do |name, param|
@@ -109,8 +109,9 @@ module Apipie
109
109
  end
110
110
 
111
111
  def process_with_api_recording(*args) # action, parameters = nil, session = nil, flash = nil, http_method = 'GET')
112
- process_without_api_recording(*args)
112
+ ret = process_without_api_recording(*args)
113
113
  Apipie::Extractor.call_recorder.analyze_functional_test(self)
114
+ ret
114
115
  ensure
115
116
  Apipie::Extractor.call_finished
116
117
  end
@@ -1,3 +1,5 @@
1
+ require 'set'
2
+
1
3
  module Apipie
2
4
  module Extractor
3
5
  class Writer
@@ -47,7 +49,7 @@ module Apipie
47
49
  def ordered_call(call)
48
50
  call = call.stringify_keys
49
51
  ordered_call = OrderedHash.new
50
- %w[verb path query request_data response_data code show_in_doc recorded].each do |k|
52
+ %w[verb path versions query request_data response_data code show_in_doc recorded].each do |k|
51
53
  next unless call.has_key?(k)
52
54
  ordered_call[k] = case call[k]
53
55
  when ActiveSupport::HashWithIndifferentAccess
@@ -76,10 +78,15 @@ module Apipie
76
78
 
77
79
  def load_new_examples
78
80
  @collector.records.reduce({}) do |h, (method, calls)|
79
- show_in_doc = nil
81
+ showed_in_versions = Set.new
82
+ # we have already shown some example
80
83
  recorded_examples = calls.map do |call|
81
- if show_in_doc.nil?
82
- show_in_doc = 1 if showable_in_doc?(call.with_indifferent_access)
84
+ method_descriptions = Apipie.get_method_descriptions(call[:controller], call[:action])
85
+ call[:versions] = method_descriptions.map(&:version)
86
+
87
+ if call[:versions].any? { |v| ! showed_in_versions.include?(v) }
88
+ call[:versions].each { |v| showed_in_versions << v }
89
+ show_in_doc = 1
83
90
  else
84
91
  show_in_doc = 0
85
92
  end
@@ -1,17 +1,28 @@
1
1
  module Apipie
2
2
  module Helpers
3
3
  def markup_to_html(text)
4
- Apipie.configuration.markup.to_html(text.strip_heredoc)
4
+ return "" if text.nil?
5
+ if Apipie.configuration.markup.respond_to? :to_html
6
+ Apipie.configuration.markup.to_html(text.strip_heredoc)
7
+ else
8
+ text.strip_heredoc
9
+ end
5
10
  end
6
11
 
7
12
  attr_accessor :url_prefix
8
13
 
14
+ def request_script_name
15
+ Thread.current[:apipie_req_script_name] || ""
16
+ end
17
+
18
+ def request_script_name=(script_name)
19
+ Thread.current[:apipie_req_script_name] = script_name
20
+ end
21
+
9
22
  def full_url(path)
10
23
  unless @url_prefix
11
24
  @url_prefix = ""
12
- if rails_prefix = ENV["RAILS_RELATIVE_URL_ROOT"]
13
- @url_prefix << rails_prefix
14
- end
25
+ @url_prefix << request_script_name
15
26
  @url_prefix << Apipie.configuration.doc_base_url
16
27
  end
17
28
  path = path.sub(/^\//,"")