apipie-rails 0.0.24 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. data/.gitignore +1 -1
  2. data/.travis.yml +6 -0
  3. data/CHANGELOG.md +97 -0
  4. data/Gemfile +1 -1
  5. data/Gemfile.rails30 +5 -0
  6. data/Gemfile.rails32 +5 -0
  7. data/Gemfile.rails40 +5 -0
  8. data/Gemfile.rails41 +5 -0
  9. data/README.rst +126 -11
  10. data/apipie-rails.gemspec +1 -1
  11. data/app/controllers/apipie/apipies_controller.rb +3 -0
  12. data/app/helpers/apipie_helper.rb +9 -0
  13. data/app/views/apipie/apipies/_metadata.erb +1 -0
  14. data/app/views/apipie/apipies/_method_detail.erb +46 -0
  15. data/app/views/apipie/apipies/_params.html.erb +12 -0
  16. data/app/views/apipie/apipies/_params_plain.html.erb +4 -0
  17. data/app/views/apipie/apipies/apipie_checksum.json.erb +1 -0
  18. data/app/views/apipie/apipies/method.html.erb +1 -37
  19. data/app/views/apipie/apipies/plain.html.erb +1 -0
  20. data/app/views/apipie/apipies/resource.html.erb +6 -37
  21. data/app/views/apipie/apipies/static.html.erb +1 -0
  22. data/lib/apipie/application.rb +16 -2
  23. data/lib/apipie/configuration.rb +8 -2
  24. data/lib/apipie/dsl_definition.rb +25 -6
  25. data/lib/apipie/error_description.rb +22 -10
  26. data/lib/apipie/extractor.rb +2 -1
  27. data/lib/apipie/extractor/writer.rb +8 -6
  28. data/lib/apipie/method_description.rb +7 -4
  29. data/lib/apipie/middleware/checksum_in_headers.rb +35 -0
  30. data/lib/apipie/param_description.rb +25 -4
  31. data/lib/apipie/resource_description.rb +8 -4
  32. data/lib/apipie/routing.rb +1 -0
  33. data/lib/apipie/validator.rb +71 -3
  34. data/lib/apipie/version.rb +1 -1
  35. data/lib/tasks/apipie.rake +26 -5
  36. data/spec/controllers/apipies_controller_spec.rb +2 -0
  37. data/spec/controllers/concerns_controller_spec.rb +1 -1
  38. data/spec/controllers/users_controller_spec.rb +130 -19
  39. data/spec/dummy/app/controllers/api/v1/architectures_controller.rb +1 -0
  40. data/spec/dummy/app/controllers/api/v2/nested/architectures_controller.rb +22 -20
  41. data/spec/dummy/app/controllers/concerns/sample_controller.rb +32 -30
  42. data/spec/dummy/app/controllers/users_controller.rb +18 -5
  43. data/spec/lib/method_description_spec.rb +22 -0
  44. data/spec/lib/param_description_spec.rb +77 -0
  45. data/spec/lib/rake_spec.rb +68 -0
  46. data/spec/lib/resource_description_spec.rb +18 -0
  47. data/spec/lib/validator_spec.rb +9 -0
  48. data/spec/spec_helper.rb +2 -1
  49. data/spec/support/rake.rb +21 -0
  50. metadata +20 -7
  51. data/CHANGELOG +0 -72
@@ -0,0 +1 @@
1
+ <pre class="prettyprint lang-yaml"><%= meta.to_yaml.gsub(/---\n/, "") %></pre>
@@ -0,0 +1,46 @@
1
+ <%= raw method[:full_description] %>
2
+
3
+ <% unless method[:formats].blank? %>
4
+ <%= heading('Supported Formats', h_level) %>
5
+ <%= method[:formats].join(', ') %>
6
+ <% end %>
7
+
8
+ <% unless method[:errors].blank? %>
9
+ <%= heading('Errors', h_level) %>
10
+ <% method[:errors].each do |err| %>
11
+ <%= err[:code] %>
12
+ <%= err[:description] %>
13
+ <br>
14
+ <% unless err[:metadata].blank? %>
15
+ Metadata:
16
+ <pre class="prettyprint lang-yaml"><%= err[:metadata].to_yaml %></pre>
17
+ <% end %>
18
+ <% end %>
19
+ <% end %>
20
+
21
+ <% unless method[:metadata].blank? %>
22
+ <%= heading('Metadata', h_level) %>
23
+ <%= render(:partial => "metadata", :locals => {:meta => method[:metadata]}) %>
24
+ <% end %>
25
+
26
+ <% unless method[:examples].blank? %>
27
+ <%= heading('Examples', h_level) %>
28
+ <% method[:examples].each do |example| %>
29
+ <pre class="prettyprint"><%= example %></pre>
30
+ <% end %>
31
+ <% end %>
32
+
33
+ <% unless method[:params].blank? %>
34
+ <%= heading('Params', h_level) %>
35
+ <table class='table'>
36
+ <thead>
37
+ <tr>
38
+ <th>Param name</th>
39
+ <th>Description</th>
40
+ </tr>
41
+ </thead>
42
+ <tbody>
43
+ <%= render(:partial => "params", :locals => {:params => method[:params]}) %>
44
+ </tbody>
45
+ </table>
46
+ <% end %>
@@ -1,6 +1,10 @@
1
1
  <% level ||= 0 %>
2
2
  <% col = 255 - level * 5 %>
3
3
  <% params.each do |param| %>
4
+ <% if !param[:show] %>
5
+ <%= render(:partial => "params", :locals => {:level => level, :params => param[:params]}) unless param[:params].blank? %>
6
+ <% next %>
7
+ <% end %>
4
8
  <tr style='background-color:rgb(<%= "#{col},#{col},#{col}" %>);'>
5
9
  <td>
6
10
  <strong><%= param[:full_name] %> </strong><br>
@@ -15,7 +19,15 @@
15
19
  <br>
16
20
  Value: <%= param[:validator] %>
17
21
  <% end %>
22
+
23
+ <% unless param[:metadata].blank? %>
24
+ <br>
25
+ Metadata:
26
+ <%= render(:partial => "metadata", :locals => {:meta => param[:metadata]}) %>
27
+ <% end %>
28
+
18
29
  </td>
30
+
19
31
  </tr>
20
32
 
21
33
  <%= render(:partial => "params", :locals => {:level => level + 1, :params => param[:params]}) unless param[:params].blank? %>
@@ -1,5 +1,9 @@
1
1
  <ul>
2
2
  <% params.each do |param| %>
3
+ <% if !param[:show] %>
4
+ <%= render(:partial => "params_plain", :locals => {:params => param[:params]}) unless param[:params].blank? %>
5
+ <% next %>
6
+ <% end %>
3
7
  <li>
4
8
  <strong><%= param[:name] %> </strong>:
5
9
  <small>
@@ -0,0 +1 @@
1
+ { "checksum": "<%= raw Apipie.checksum %>" }
@@ -27,43 +27,7 @@
27
27
  Also see <%= @method[:see].map { |s| link_to(s[:description], "#{s[:link]}.html") }.to_sentence.html_safe %>.
28
28
  <% end %>
29
29
 
30
- <%= raw @method[:full_description] %>
31
-
32
- <% unless @method[:examples].blank? %>
33
- <h2>Examples</h2>
34
- <% @method[:examples].each do |example| %>
35
- <pre class="prettyprint"><%= example %></pre>
36
- <% end %>
37
- <% end %>
38
-
39
- <% unless @method[:formats].blank? %>
40
- <h2>Supported Formats</h2>
41
- <%= @method[:formats].join(', ') %>
42
- <% end %>
43
-
44
- <% unless @method[:errors].blank? %>
45
- <h2>Errors</h2>
46
- <% @method[:errors].each do |err| %>
47
- <%= err[:code] %>
48
- <%= err[:description] %>
49
- <br>
50
- <% end %>
51
- <% end %>
52
-
53
- <% unless @method[:params].blank? %>
54
- <h2>Params</h2>
55
- <table class='table'>
56
- <thead>
57
- <tr>
58
- <th>Param name</th>
59
- <th>Description</th>
60
- </tr>
61
- </thead>
62
- <tbody>
63
- <%= render(:partial => "params", :locals => {:params => @method[:params]}) %>
64
- </tbody>
65
- </table>
66
- <% end %>
30
+ <%= render(:partial => "method_detail", :locals => {:method => @method, :h_level => 2}) %>
67
31
  </div>
68
32
 
69
33
  <% content_for :apipie_footer do %>
@@ -56,6 +56,7 @@
56
56
  <% method[:errors].each do |err| %>
57
57
  <%= err[:code] %>
58
58
  <%= err[:description] %>
59
+ <%= render(:partial => "metadata", :locals => {:meta => err[:metadata]}) %>
59
60
  <br>
60
61
  <% end %>
61
62
  <% end %>
@@ -21,6 +21,11 @@
21
21
  <div><%= raw @resource[:full_description] %></div>
22
22
  <% end %>
23
23
 
24
+ <% unless @resource[:metadata].blank? %>
25
+ <h2>Metadata</h2>
26
+ <%= render(:partial => "metadata", :locals => {:meta => @resource[:metadata]}) %>
27
+ <% end %>
28
+
24
29
  <% unless @resource[:formats].blank? %>
25
30
  <h2>Supported Formats</h2>
26
31
  <%= @resource[:formats].join(', ') %>
@@ -52,43 +57,7 @@
52
57
  <% end %>
53
58
 
54
59
  <div id='description-<%= m[:name] %>' class='collapse accordion-body'>
55
- <%= raw m[:full_description] %>
56
-
57
- <% unless m[:formats].blank? %>
58
- <h3>Supported Formats</h3>
59
- <%= m[:formats].join(', ') %>
60
- <% end %>
61
-
62
- <% unless m[:errors].blank? %>
63
- <h3>Errors</h3>
64
- <% m[:errors].each do |err| %>
65
- <%= err[:code] %>
66
- <%= err[:description] %>
67
- <br>
68
- <% end %>
69
- <% end %>
70
-
71
- <% unless m[:examples].blank? %>
72
- <h3>Examples</h3>
73
- <% m[:examples].each do |example| %>
74
- <pre class="prettyprint"><%= example %></pre>
75
- <% end %>
76
- <% end %>
77
-
78
- <% unless m[:params].blank? %>
79
- <h3>Params</h3>
80
- <table class='table'>
81
- <thead>
82
- <tr>
83
- <th>Param name</th>
84
- <th>Description</th>
85
- </tr>
86
- </thead>
87
- <tbody>
88
- <%= render(:partial => "params", :locals => {:params => m[:params]}) %>
89
- </tbody>
90
- </table>
91
- <% end %>
60
+ <%= render(:partial => "method_detail", :locals => {:method => m, :h_level => 3}) %>
92
61
  </div>
93
62
  <% end %>
94
63
  </div>
@@ -73,6 +73,7 @@
73
73
  <% method[:errors].each do |err| %>
74
74
  <%= err[:code] %>
75
75
  <%= err[:description] %>
76
+ <%= render(:partial => "metadata", :locals => {:meta => err[:metadata]}) %>
76
77
  <br>
77
78
  <% end %>
78
79
  <% end %>
@@ -1,5 +1,7 @@
1
1
  require 'apipie/static_dispatcher'
2
2
  require 'yaml'
3
+ require 'digest/md5'
4
+ require 'json'
3
5
 
4
6
  module Apipie
5
7
 
@@ -208,8 +210,8 @@ module Apipie
208
210
  tape_file = File.join(Rails.root,"doc","apipie_examples.yml")
209
211
  if File.exists?(tape_file)
210
212
  #if SafeYAML gem is enabled, it will load examples as an array of Hash, instead of hash
211
- if defined? SafeYAML
212
- @recorded_examples = YAML.load_file(tape_file, :safe=>false)
213
+ if YAML.respond_to?(:safe_load_file) && defined?(SafeYAML)
214
+ @recorded_examples = SafeYAML.load_file(tape_file, :safe=>false)
213
215
  else
214
216
  @recorded_examples = YAML.load_file(tape_file)
215
217
  end
@@ -264,6 +266,18 @@ module Apipie
264
266
  api_controllers_paths.each do |f|
265
267
  load_controller_from_file f
266
268
  end
269
+ @checksum = compute_checksum if Apipie.configuration.update_checksum
270
+ end
271
+
272
+ def compute_checksum
273
+ all_docs = Apipie.available_versions.inject({}) do |all, version|
274
+ all.update(version => Apipie.to_json(version))
275
+ end
276
+ Digest::MD5.hexdigest(JSON.dump(all_docs))
277
+ end
278
+
279
+ def checksum
280
+ @checksum ||= compute_checksum
267
281
  end
268
282
 
269
283
  # Is there a reason to interpret the DSL for this run?
@@ -4,8 +4,8 @@ module Apipie
4
4
  attr_accessor :app_name, :app_info, :copyright, :markup, :disqus_shortname,
5
5
  :api_base_url, :doc_base_url, :required_by_default, :layout,
6
6
  :default_version, :debug, :version_in_url, :namespaced_resources,
7
- :validate, :validate_value, :validate_presence, :authenticate, :doc_path
8
-
7
+ :validate, :validate_value, :validate_presence, :authenticate, :doc_path,
8
+ :show_all_examples, :process_params, :update_checksum, :checksum_path
9
9
 
10
10
  alias_method :validate?, :validate
11
11
  alias_method :required_by_default?, :required_by_default
@@ -36,6 +36,9 @@ module Apipie
36
36
  end
37
37
  alias_method :validate_presence?, :validate_presence
38
38
 
39
+ def process_value?
40
+ @process_params
41
+ end
39
42
  # set to true if you want to use pregenerated documentation cache and avoid
40
43
  # generating the documentation on runtime (usefull for production
41
44
  # environment).
@@ -124,6 +127,9 @@ module Apipie
124
127
  @version_in_url = true
125
128
  @namespaced_resources = false
126
129
  @doc_path = "doc"
130
+ @process_params = false
131
+ @checksum_path = [@doc_base_url, '/api/']
132
+ @update_checksum = false
127
133
  end
128
134
  end
129
135
  end
@@ -6,7 +6,7 @@ module Apipie
6
6
  module DSL
7
7
 
8
8
  module Base
9
- attr_reader :apipie_resource_descriptions
9
+ attr_reader :apipie_resource_descriptions, :api_params
10
10
 
11
11
  private
12
12
 
@@ -29,7 +29,8 @@ module Apipie
29
29
  :examples => [],
30
30
  :see => [],
31
31
  :formats => nil,
32
- :api_versions => []
32
+ :api_versions => [],
33
+ :meta => nil
33
34
  }
34
35
  end
35
36
  end
@@ -76,7 +77,7 @@ module Apipie
76
77
  # Example:
77
78
  # api :GET, "/resource_route", "short description",
78
79
  #
79
- def api(method, path, desc = nil) #:doc:
80
+ def api(method, path, desc = nil, options={}) #:doc:
80
81
  return unless Apipie.active_dsl?
81
82
  _apipie_dsl_data[:api_args] << [method, path, desc]
82
83
  end
@@ -164,20 +165,27 @@ module Apipie
164
165
  _apipie_dsl_data[:formats] = formats
165
166
  end
166
167
 
168
+ # Describe additional metadata
169
+ #
170
+ # meta :author => { :name => 'John', :surname => 'Doe' }
171
+ def meta(meta) #:doc:
172
+ _apipie_dsl_data[:meta] = meta
173
+ end
174
+
167
175
 
168
176
  # Describe possible errors
169
177
  #
170
178
  # Example:
171
- # error :desc => "speaker is sleeping", :code => 500
179
+ # error :desc => "speaker is sleeping", :code => 500, :meta => [:some, :more, :data]
172
180
  # error 500, "speaker is sleeping"
173
181
  # def hello_world
174
182
  # return 500 if self.speaker.sleeping?
175
183
  # puts "hello world"
176
184
  # end
177
185
  #
178
- def error(*args) #:doc:
186
+ def error(code_or_options, desc=nil, options={}) #:doc:
179
187
  return unless Apipie.active_dsl?
180
- _apipie_dsl_data[:errors] << args
188
+ _apipie_dsl_data[:errors] << [code_or_options, desc, options]
181
189
  end
182
190
 
183
191
  def _apipie_define_validators(description)
@@ -186,6 +194,8 @@ module Apipie
186
194
 
187
195
  old_method = instance_method(description.method)
188
196
 
197
+
198
+ # @todo we should use before_filter
189
199
  define_method(description.method) do |*args|
190
200
 
191
201
  if Apipie.configuration.validate_presence?
@@ -202,6 +212,15 @@ module Apipie
202
212
  end
203
213
  end
204
214
 
215
+ if Apipie.configuration.process_value?
216
+ @api_params = {}
217
+
218
+ description.params.each do |_, param|
219
+ # params processing
220
+ @api_params[param.as] = param.process_value(params[:"#{param.name}"]) if params.has_key?(param.name)
221
+ end
222
+ end
223
+
205
224
  # run the original method code
206
225
  old_method.bind(self).call(*args)
207
226
  end
@@ -2,22 +2,34 @@ module Apipie
2
2
 
3
3
  class ErrorDescription
4
4
 
5
- attr_reader :code, :description
5
+ attr_reader :code, :description, :metadata
6
6
 
7
- def initialize(args)
8
- if args.first.is_a? Hash
9
- args = args.first
10
- elsif args.count == 2
11
- args = {:code => args.first, :description => args.second}
7
+ def self.from_dsl_data(args)
8
+ code_or_options, desc, options = args
9
+ Apipie::ErrorDescription.new(code_or_options,
10
+ desc,
11
+ options)
12
+ end
13
+
14
+ def initialize(code_or_options, desc=nil, options={})
15
+ if code_or_options.is_a? Hash
16
+ code_or_options.symbolize_keys!
17
+ @code = code_or_options[:code]
18
+ @metadata = code_or_options[:meta]
19
+ @description = code_or_options[:desc] || code_or_options[:description]
12
20
  else
13
- raise ArgumentError "ApipieError: Bad use of error method."
21
+ @code = code_or_options
22
+ @metadata = options[:meta]
23
+ @description = desc
14
24
  end
15
- @code = args[:code] || args['code']
16
- @description = args[:desc] || args[:description] || args['desc'] || args['description']
17
25
  end
18
26
 
19
27
  def to_json
20
- {:code => code, :description => description}
28
+ {
29
+ :code => code,
30
+ :description => description,
31
+ :metadata => metadata
32
+ }
21
33
  end
22
34
 
23
35
  end
@@ -86,7 +86,8 @@ module Apipie
86
86
  controller_path, action = route[:controller], route[:action]
87
87
  next unless controller_path && action
88
88
 
89
- controller = "#{controller_path}_controller".camelize
89
+ controller_path = controller_path.split('::').map(&:camelize).join('::')
90
+ controller = "#{controller_path}Controller"
90
91
 
91
92
  path = if /^#{Regexp.escape(api_prefix)}(.*)$/ =~ route[:path]
92
93
  $1.sub!(/\(\.:format\)$/,"")
@@ -84,7 +84,9 @@ module Apipie
84
84
  method_descriptions = Apipie.get_method_descriptions(call[:controller], call[:action])
85
85
  call[:versions] = method_descriptions.map(&:version)
86
86
 
87
- if call[:versions].any? { |v| ! showed_in_versions.include?(v) }
87
+ if Apipie.configuration.show_all_examples
88
+ show_in_doc = 1
89
+ elsif call[:versions].any? { |v| ! showed_in_versions.include?(v) }
88
90
  call[:versions].each { |v| showed_in_versions << v }
89
91
  show_in_doc = 1
90
92
  else
@@ -99,11 +101,11 @@ module Apipie
99
101
 
100
102
  def load_old_examples
101
103
  if File.exists?(@examples_file)
102
- if defined? SafeYAML
103
- return YAML.load_file(@examples_file, :safe=>false)
104
- else
105
- return YAML.load_file(@examples_file)
106
- end
104
+ if defined? SafeYAML
105
+ return YAML.load_file(@examples_file, :safe=>false)
106
+ else
107
+ return YAML.load_file(@examples_file)
108
+ end
107
109
  end
108
110
  return {}
109
111
  end