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
@@ -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(/^\//,"")