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
@@ -1,367 +0,0 @@
1
- = API Documentation Tool {<img src="https://secure.travis-ci.org/Pajk/apipie-rails.png?branch=master" alt="Build Status" />}[http://travis-ci.org/Pajk/apipie-rails]
2
-
3
- == What?
4
-
5
- Every API needs a documentation otherwise no one know how to use it.
6
- It is not easy to maintain such documentation always up to date if it
7
- develops over time. Additionaly to this most programmers don't like writing
8
- documentation and so it could happen that your API is useless because
9
- of outdated documentation. If the documentation is just text completely
10
- separated from code then there is no simple way to verify its correctness.
11
-
12
- This gem adds some new methods to Rails controllers that can be used
13
- to describe resources which are exposed by our API. It is meant to
14
- be used for {RESTful}[http://cs.wikipedia.org/wiki/Representational_State_Transfer]
15
- web services and because we are talking about Rails you probably know
16
- what it is all about.
17
-
18
- Informations you entered with provided DSL are used to generate
19
- online documentation and validate values of incoming requests parameters.
20
-
21
- == Why?
22
-
23
- Documentation will be closer to your code and thus less prone to become
24
- obsolete. You get documentation frontend without effort. And if you want your
25
- own design you can do it anytime because json API for your API documentation
26
- is provided. There are no limits on how to use those informations, the
27
- builtin web interface is only one of possible options.
28
-
29
- Request parameters will be validated before they will get in touch with yours
30
- controller action or model code. And you inform API users about expected
31
- parameters values. This information will be always actual because otherwise
32
- your tests fail (of course you have tests!).
33
-
34
- This is how the web interface look like. Try it live on
35
- http://restapi-likes.rhcloud.com. Example users resource
36
- description are taken from Twitter(c) API.
37
-
38
- https://img.skitch.com/20120428-nruk3e87xs2cu4ydsjujdh11fq.png
39
-
40
- https://img.skitch.com/20120428-bni2cmq5cyhjuw1jkd78e3qjxn.png
41
-
42
- == How?
43
-
44
- === Gemfile
45
-
46
- Add +apipie+ gem to your gemfile and run bundle.
47
-
48
- gem 'apipie-rails'
49
-
50
- For bleeding edge version use GitHub version
51
-
52
- gem 'apipie-rails', :git => 'git://github.com/Pajk/apipie-rails.git'
53
-
54
- === Config file
55
-
56
- Create configuration file in e.g. */config/initializers/apipie.rb*.
57
- You can set application name, footer text, API and documentation base URL
58
- and turn off validations. You can also choose your favorite markup language
59
- of full descriptions.
60
-
61
- [app_name] Name of your application used in breadcrumbs navigation.
62
- [copyright] Copyright information (shown in page footer).
63
- [doc_base_url] Documentation frontend base url.
64
- [api_base_url] Base url of your API, most probably /api.
65
- [validate] Parameters validation is turned off when set to false.
66
- [app_info] Application long description.
67
- [reload_controllers] Set to enable/disable reloading controllers (and the documentation with it), by default enabled in development.
68
- [api_controllers_matcher] For reloading to work properly you need to specify where your API controllers are.
69
- [markup] You can choose markup language for descriptions of your application,
70
- resources and methods. RDoc is the default but you can choose from
71
- Apipie::Markup::Markdown.new or Apipie::Markup::Textile.new.
72
- In order to use Markdown you need Redcarpet gem and for Textile you
73
- need RedCloth. Add those to your gemfile and run bundle if you
74
- want to use them. You can also add any other markup language
75
- processor.
76
- [layout] Name of a layout template to use instead of Apipie's layout. You can use Apipie.include_stylesheets and Apipie.include_javascripts helpers to include Apipie's stylesheets and javascripts.
77
-
78
- Example:
79
-
80
- Apipie.configure do |config|
81
- config.app_name = "Test app"
82
- config.copyright = "&copy; 2012 Pavel Pokorny"
83
- config.doc_base_url = "/apidoc"
84
- config.api_base_url = "/api"
85
- config.validate = false
86
- config.markup = Apipie::Markup::Markdown.new
87
- config.reload_controllers = true
88
- config.api_controllers_matcher = File.join(Rails.root, "app", "controllers", "**","*.rb")
89
- config.app_info = <<-DOC
90
- This is where you can inform user about your application and API
91
- in general.
92
- DOC
93
- end
94
-
95
- === Routes
96
-
97
- Add +apipie+ to your *routes.rb*, that's all.
98
-
99
-
100
- === Resource Description
101
-
102
- Resource can be described in corresponding controller by calling +resource_description+ method with block. Parameters described here are used
103
- for every method unless they are overridden with NilValidator.
104
- Parameters are inherited in controller hierarchy. So for example you can define
105
- in base ApplicationController parameters for authentication and they will
106
- be checked in every request.
107
-
108
- [name] You can force resource to display under some alias.
109
- [short] One line short description.
110
- [path] Relative URL path of this resource.
111
- [version] Version of this resource API, use arbitrary string.
112
- [formats] Resource level request / response formats.
113
- [param] The very same parameter description as you will use method
114
- description. Generally use this for parameters that apply
115
- to every method in the controller (such as :user hash).
116
- It is possible to hide it for individual methods by setting
117
- it's validator to nil.
118
- [description] The full multiline description of the resource and
119
- yours API options
120
-
121
- Example:
122
-
123
- class UsersController < ApplicationController
124
-
125
- resource_description do
126
- name 'Members'
127
- short 'Site members'
128
- path '/users'
129
- version '1.0 - 3.4.2012'
130
- formats ['json', 'xml']
131
- param :id, Fixnum, :desc => "User ID", :required => false
132
- param :user, Hash, :desc => 'Param description for all methods' do
133
- param :username, String, :required => true,
134
- :desc => "Username for login"
135
- param :password, String, :required => true,
136
- :desc => "Password for login"
137
- end
138
- description <<-DOC
139
- Full description of this resource.
140
- DOC
141
- end
142
- #...
143
- end
144
-
145
-
146
- === Method Description
147
-
148
- Then describe methods available to your API.
149
-
150
- [api] Say how is this method exposed and provide short description.
151
- The first parameter is HTTP method (one of :GET/:POST/:PUT/:DELETE).
152
- The second parameter is relative URL path which is mapped to this
153
- method. The last parameter is methods short description.
154
- You can use this +api+ method more than once for one method. It could
155
- be useful when there are more routes mapped to it.
156
- [param] Look at Parameter description section for details.
157
- [formats] Method level request / response formats.
158
- [error] Describe each possible error that can happend what calling this
159
- method. HTTP response code and description can be provided.
160
- [description] Full method description which will be converted to HTML by
161
- choosen markup language processor.
162
- [example] Provide example of server response, whole communication or response type.
163
- It will be formatted as code.
164
- [see] Provide reference to another method, this has to be string with
165
- controller_name#method_name.
166
-
167
- Example:
168
-
169
- api :GET, "/users/:id", "Show user profile"
170
- error :code => 401, :desc => "Unauthorized"
171
- error :code => 404, :desc => "Not Found"
172
- param :session, String, :desc => "user is logged in", :required => true
173
- param :regexp_param, /^[0-9]* years/, :desc => "regexp param"
174
- param :array_param, [100, "one", "two", 1, 2], :desc => "array validator"
175
- param :boolean_param, [true, false], :desc => "array validator with boolean"
176
- param :proc_param, lambda { |val|
177
- val == "param value" ? true : "The only good value is 'param value'."
178
- }, :desc => "proc validator"
179
- description "method description"
180
- formats ['json', 'jsonp', 'xml']
181
- example " 'user': {...} "
182
- see "users#showme"
183
- def show
184
- #...
185
- end
186
-
187
- == Parameter Description
188
-
189
- Use +param+ to describe every possible parameter. You can use Hash validator
190
- in cooperation with block given to param method to describe nested parameters.
191
-
192
- [name] The first argument is parameter name as a symbol.
193
- [validator] Second parameter is parameter validator, choose one from section
194
- Validators.
195
- [desc] Parameter description.
196
- [required] Set this true/false to make it required/optional.
197
- Default is optional
198
-
199
- Example:
200
-
201
- param :user, Hash, :desc => "User info" do
202
- param :username, String, :desc => "Username for login", :required => true
203
- param :password, String, :desc => "Password for login", :required => true
204
- param :membership, ["standard","premium"], :desc => "User membership"
205
- end
206
- def create
207
- #...
208
- end
209
-
210
-
211
- == Validators
212
-
213
- Every parameter needs to have associated validator. For now there are some
214
- basic validators. You can always provide your own to reach complex
215
- results.
216
- If validations are enabled (default state) the parameters of every
217
- request are validated. If the value is wrong a +ArgumentError+ exception
218
- is raised and can be rescued and processed. It contains some description
219
- of parameter value expectations. Validations can be turned off
220
- in configuration file.
221
-
222
- === TypeValidator
223
-
224
- Check the parameter type. Only String, Hash and Array are supported
225
- for the sake of simplicity. Read more to to find out how to add
226
- your own validator.
227
-
228
- param :session, String, :desc => "user is logged in", :required => true
229
- param :facts, Hash, :desc => "Additional optional facts about the user"
230
-
231
- === RegexpValidator
232
-
233
- Check parameter value against given regular expression.
234
-
235
- param :regexp_param, /^[0-9]* years/, :desc => "regexp param"
236
-
237
- === ArrayValidator
238
-
239
- Check if parameter value is included given array.
240
-
241
- param :array_param, [100, "one", "two", 1, 2], :desc => "array validator"
242
-
243
- === ProcValidator
244
-
245
- If you need more complex validation and you know you won't reuse it you
246
- can use Proc/lambda validator. Provide your own Proc taking value
247
- of parameter as the only argument. Return true if value pass validation
248
- or return some text about what is wrong. _Don't use the keyword *return*
249
- if you provide instance of Proc (with lambda it is ok), just use the last
250
- statement return property of ruby_.
251
-
252
- param :proc_param, lambda { |val|
253
- val == "param value" ? true : "The only good value is 'param value'."
254
- }, :desc => "proc validator"
255
-
256
- === HashValidator
257
-
258
- You can describe hash parameters in depth if you provide a block with
259
- description of nested values.
260
-
261
- param :user, Hash, :desc => "User info" do
262
- param :username, String, :desc => "Username for login", :required => true
263
- param :password, String, :desc => "Password for login", :required => true
264
- param :membership, ["standard","premium"], :desc => "User membership"
265
- end
266
-
267
- === NilValidator
268
-
269
- In fact there is any NilValidator but setting it to nil can be used to
270
- override parameters described on resource level.
271
-
272
- Example:
273
-
274
- param :user, nil
275
- def destroy
276
- #...
277
- end
278
-
279
- === Adding custom validator
280
-
281
- Only basic validators are included but it is really easy to add your own.
282
- Create new initializer with subclass of Apipie::Validator::BaseValidator.
283
- Two methods are required to implement - instance method
284
- <tt>validate(value)</tt> and class method
285
- <tt>build(param_description, argument, options, block)</tt>.
286
-
287
- When searching for validator +build+ method of every subclass of
288
- Apipie::Validator::BaseValidator is called. The first one whitch return
289
- constructed validator object is used.
290
-
291
- Example: Adding IntegerValidator
292
-
293
- We want to check if parameter value is an integer like this:
294
-
295
- param :id, Integer, :desc => "Company ID"
296
-
297
- So we create apipie_validators.rb initializer with this content:
298
-
299
- class IntegerValidator < Apipie::Validator::BaseValidator
300
-
301
- def initialize(param_description, argument)
302
- super(param_description)
303
- @type = argument
304
- end
305
-
306
- def validate(value)
307
- return false if value.nil?
308
- !!(value.to_s =~ /^[-+]?[0-9]+$/)
309
- end
310
-
311
- def self.build(param_description, argument, options, block)
312
- if argument == Integer || argument == Fixnum
313
- self.new(param_description, argument)
314
- end
315
- end
316
-
317
- def description
318
- "Must be #{@type}."
319
- end
320
- end
321
-
322
- Parameters of the build method:
323
-
324
- [param_description] Instance of Apipie::ParamDescription contains all
325
- given informations about validated parameter.
326
- [argument] Specified validator, in our example it is +Integer+
327
- [options] Hash with specified options, for us just
328
- <tt>{:desc => "Company ID"}</tt>
329
- [block] Block converted into Proc, use it as you desire. In this example nil.
330
-
331
-
332
- == Markup
333
-
334
- The default markup language is {RDoc}[http://rdoc.rubyforge.org/RDoc/Markup.html]. It can be changed in
335
- config file (config.markup=) to one of these:
336
- [Markdown] Use Apipie::Markup::Markdown.new. You need Redcarpet gem.
337
- [Textile] Use Apipie::Markup::Textile.new. You need RedCloth gem.
338
-
339
- Or provide you own object with <tt>to_html(text)</tt> method.
340
- For inspiration this is how Textile look like:
341
-
342
- class Textile
343
- def initialize
344
- require 'RedCloth'
345
- end
346
- def to_html(text)
347
- RedCloth.new(text).to_html
348
- end
349
- end
350
-
351
- == Static page
352
-
353
- To generate static version of documentation run <tt>rake apipie:static</tt> task.
354
- It will create folder with one-page documentation in your public directory. Rename
355
- or delete it if you want to use 'normal' dynamic version again.
356
-
357
- == {CLI client}[https://github.com/Pajk/apipie-rails/wiki/CLI-client]
358
-
359
- == {Extractor}[https://github.com/Pajk/apipie-rails/wiki/Extractor]
360
-
361
- == Authors
362
-
363
- {Pajk}[https://github.com/Pajk] and {iNecas}[https://github.com/iNecas]
364
-
365
- == Contributors
366
-
367
- See {Contributors page}[https://github.com/Pajk/apipie-rails/graphs/contributors]. Special thanks to all of them!
@@ -1,133 +0,0 @@
1
- require 'rest_client'
2
- require 'oauth'
3
- require 'json'
4
- require 'apipie/client/rest_client_oauth'
5
-
6
- module Apipie
7
- module Client
8
-
9
- class Base
10
- attr_reader :client, :config
11
-
12
- def initialize(config, options = { })
13
- @client = RestClient::Resource.new config[:base_url],
14
- :user => config[:username],
15
- :password => config[:password],
16
- :oauth => config[:oauth],
17
- :headers => { :content_type => 'application/json',
18
- :accept => 'application/json' }
19
- @config = config
20
- end
21
-
22
- def call(method, path, params = { }, headers = { })
23
- headers ||= { }
24
-
25
- args = [method]
26
- if [:post, :put].include?(method)
27
- args << params.to_json
28
- else
29
- headers[:params] = params if params
30
- end
31
-
32
- args << headers if headers
33
- process_data client[path].send(*args)
34
- end
35
-
36
- def self.doc
37
- raise NotImplementedError
38
- end
39
-
40
- def self.validation_hash(method)
41
- validation_hashes[method.to_s]
42
- end
43
-
44
- def self.method_doc(method)
45
- method_docs[method.to_s]
46
- end
47
-
48
- def validate_params!(params, rules)
49
- return unless params.is_a?(Hash)
50
-
51
- invalid_keys = params.keys.map(&:to_s) - (rules.is_a?(Hash) ? rules.keys : rules)
52
- raise ArgumentError, "Invalid keys: #{invalid_keys.join(", ")}" unless invalid_keys.empty?
53
-
54
- if rules.is_a? Hash
55
- rules.each do |key, sub_keys|
56
- validate_params!(params[key], sub_keys) if params[key]
57
- end
58
- end
59
- end
60
-
61
- protected
62
-
63
- def process_data(response)
64
- data = begin
65
- JSON.parse(response.body)
66
- rescue JSON::ParserError
67
- response.body
68
- end
69
- return data, response
70
- end
71
-
72
- def check_params(params, options = { })
73
- raise ArgumentError unless (method = options[:method])
74
- return unless config[:enable_validations]
75
-
76
- case options[:allowed]
77
- when true
78
- validate_params!(params, self.class.validation_hash(method))
79
- when false
80
- raise ArgumentError, "this method '#{method}' does not support params" if params && !params.empty?
81
- else
82
- raise ArgumentError, "options :allowed should be true or false, it was #{options[:allowed]}"
83
- end
84
- end
85
-
86
- # @return url and rest of the params
87
- def fill_params_in_url(url, params)
88
- params ||= { }
89
- # insert param values
90
- url_param_names = params_in_path(url)
91
- url = params_in_path(url).inject(url) do |url, param_name|
92
- param_value = params[param_name] or
93
- raise ArgumentError, "missing param '#{param_name}' in parameters"
94
- url.sub(":#{param_name}", param_value.to_s)
95
- end
96
-
97
- return url, params.reject { |param_name, _| url_param_names.include? param_name }
98
- end
99
-
100
- private
101
-
102
- def self.method_docs
103
- @method_docs ||= doc['methods'].inject({ }) do |hash, method|
104
- hash[method['name']] = method
105
- hash
106
- end
107
- end
108
-
109
- def self.validation_hashes
110
- @validation_hashes ||= method_docs.inject({ }) do |hash, pair|
111
- name, method_doc = pair
112
- hash[name] = construct_validation_hash method_doc
113
- hash
114
- end
115
- end
116
-
117
- def self.construct_validation_hash(method)
118
- if method['params'].any? { |p| p['params'] }
119
- method['params'].reduce({ }) do |h, p|
120
- h.update(p['name'] => (p['params'] ? p['params'].map { |pp| pp['name'] } : nil))
121
- end
122
- else
123
- method['params'].map { |p| p['name'] }
124
- end
125
- end
126
-
127
- def params_in_path(url)
128
- url.scan(/:([^\/]*)/).map { |m| m.first }
129
- end
130
-
131
- end
132
- end
133
- end