restapi 0.0.3 → 0.0.4

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 (41) hide show
  1. data/Gemfile +2 -1
  2. data/Gemfile.lock +83 -64
  3. data/README.rdoc +375 -2
  4. data/app/controllers/restapi/restapis_controller.rb +21 -3
  5. data/app/helpers/restapi/restapis_helper.rb +31 -0
  6. data/app/public/restapi/javascripts/bundled/prettify.js +28 -0
  7. data/app/public/restapi/javascripts/restapi.js +4 -13
  8. data/app/public/restapi/stylesheets/bundled/prettify.css +30 -0
  9. data/app/views/layouts/restapi/restapi.html.erb +36 -0
  10. data/app/views/restapi/restapis/index.html.erb +35 -33
  11. data/app/views/restapi/restapis/method.html.erb +59 -0
  12. data/app/views/restapi/restapis/resource.html.erb +71 -0
  13. data/app/views/restapi/restapis/static.html.erb +103 -0
  14. data/lib/restapi.rb +0 -10
  15. data/lib/restapi/application.rb +25 -10
  16. data/lib/restapi/dsl_definition.rb +38 -32
  17. data/lib/restapi/markup.rb +12 -12
  18. data/lib/restapi/method_description.rb +10 -8
  19. data/lib/restapi/param_description.rb +23 -10
  20. data/lib/restapi/resource_description.rb +1 -1
  21. data/lib/restapi/restapi_module.rb +15 -0
  22. data/lib/restapi/validator.rb +37 -14
  23. data/lib/restapi/version.rb +1 -1
  24. data/lib/tasks/restapi.rake +157 -0
  25. data/restapi.gemspec +1 -1
  26. data/spec/controllers/restapis_controller_spec.rb +84 -0
  27. data/spec/controllers/users_controller_spec.rb +273 -221
  28. data/spec/dummy/app/controllers/twitter_example_controller.rb +209 -208
  29. data/spec/dummy/app/controllers/users_controller.rb +23 -33
  30. data/spec/dummy/config/initializers/restapi.rb +45 -23
  31. data/spec/dummy/config/routes.rb +12 -7
  32. metadata +26 -15
  33. data/app/public/restapi/javascripts/bundled/backbone.js +0 -1431
  34. data/app/public/restapi/javascripts/bundled/json2.js +0 -487
  35. data/app/public/restapi/javascripts/bundled/underscore.js +0 -999
  36. data/app/public/restapi/javascripts/jst.js +0 -127
  37. data/app/public/restapi/javascripts/routers/documentation_router.js +0 -52
  38. data/app/public/restapi/stylesheets/bundled/bootstrap-responsive.css +0 -686
  39. data/app/public/restapi/stylesheets/bundled/bootstrap.css +0 -3990
  40. data/spec/dummy/app/controllers/dogs_controller.rb +0 -20
  41. data/spec/dummy/app/helpers/application_helper.rb +0 -2
@@ -1,13 +1,3 @@
1
- require 'active_support/dependencies'
2
-
3
- # add path to restapi controller to ActiveSupport paths
4
- %w{ controllers views }.each do |dir|
5
- path = File.join(File.dirname(__FILE__), 'app', dir)
6
- $LOAD_PATH << path
7
- ActiveSupport::Dependencies.autoload_paths << path
8
- ActiveSupport::Dependencies.autoload_once_paths.delete(path)
9
- end
10
-
11
1
  require "restapi/routing"
12
2
  require "restapi/markup"
13
3
  require "restapi/restapi_module"
@@ -11,7 +11,7 @@ module Restapi
11
11
  end
12
12
  end
13
13
 
14
- attr_accessor :last_api_args, :last_errors, :last_params, :last_description
14
+ attr_accessor :last_api_args, :last_errors, :last_params, :last_description, :last_examples
15
15
  attr_reader :method_descriptions, :resource_descriptions
16
16
 
17
17
  def initialize
@@ -46,8 +46,12 @@ module Restapi
46
46
  Restapi::ResourceDescription.new(controller, resource_name, &block)
47
47
  end
48
48
 
49
- def add_method_description_args(args)
50
- @last_api_args << MethodDescription::Api.new(args)
49
+ def add_method_description_args(method, path, desc)
50
+ @last_api_args << MethodDescription::Api.new(method, path, desc)
51
+ end
52
+
53
+ def add_example(example)
54
+ @last_examples << example.strip_heredoc
51
55
  end
52
56
 
53
57
  # check if there is some saved description
@@ -94,6 +98,7 @@ module Restapi
94
98
  @last_errors = []
95
99
  @last_params = []
96
100
  @last_description = nil
101
+ @last_examples = []
97
102
  end
98
103
 
99
104
  # Return the current description, clearing it in the process.
@@ -120,6 +125,12 @@ module Restapi
120
125
  @last_params.clear
121
126
  params
122
127
  end
128
+
129
+ def get_examples
130
+ examples = @last_examples.clone
131
+ @last_examples.clear
132
+ examples
133
+ end
123
134
 
124
135
  def to_json(resource_name, method_name)
125
136
 
@@ -135,17 +146,21 @@ module Restapi
135
146
  end
136
147
 
137
148
  {
138
- 'docs' => {
139
- 'name' => Restapi.configuration.app_name,
140
- 'info' => Restapi.configuration.app_info,
141
- 'copyright' => Restapi.configuration.copyright,
142
- 'doc_url' => "#{ENV["RAILS_RELATIVE_URL_ROOT"]}#{Restapi.configuration.doc_base_url}",
143
- 'api_url' => Restapi.configuration.api_base_url,
144
- 'resources' => _resources
149
+ :docs => {
150
+ :name => Restapi.configuration.app_name,
151
+ :info => Restapi.configuration.app_info,
152
+ :copyright => Restapi.configuration.copyright,
153
+ :doc_url => "#{ENV["RAILS_RELATIVE_URL_ROOT"]}#{Restapi.configuration.doc_base_url}",
154
+ :api_url => Restapi.configuration.api_base_url,
155
+ :resources => _resources
145
156
  }
146
157
  }
147
158
  end
148
159
 
160
+ def reload_documentation
161
+ Dir[Restapi.configuration.api_controllers_matcher].each {|f| load f}
162
+ end
163
+
149
164
  private
150
165
 
151
166
  def get_resource_name(klass)
@@ -3,32 +3,30 @@
3
3
  module Restapi
4
4
 
5
5
  # DSL is a module that provides #api, #error, #param, #error.
6
-
7
6
  module DSL
8
7
 
9
8
  private
10
-
9
+
11
10
  # Describe whole resource
12
- #
11
+ #
13
12
  # Example:
14
13
  # api :desc => "Show user profile", :path => "/users/", :version => '1.0 - 3.4.2012'
15
14
  # param :id, Fixnum, :desc => "User ID", :required => true
16
15
  # desc <<-EOS
17
16
  # Long description...
18
17
  # EOS
19
- def resource_description(options = {}, &block)
18
+ def resource_description(options = {}, &block) #:doc:
19
+ Restapi.remove_resource_description(self)
20
20
  Restapi.define_resource_description(self, &block) if block_given?
21
21
  end
22
22
 
23
23
  # Declare an api.
24
24
  #
25
25
  # Example:
26
- # api :desc => "short description",
27
- # :path => "/resource_route",
28
- # :method => "GET"
26
+ # api :GET, "/resource_route", "short description",
29
27
  #
30
- def api(args)
31
- Restapi.add_method_description_args(args)
28
+ def api(method, path, desc = nil) #:doc:
29
+ Restapi.add_method_description_args(method, path, desc)
32
30
  end
33
31
 
34
32
  # Describe the next method.
@@ -39,7 +37,7 @@ module Restapi
39
37
  # puts "hello world"
40
38
  # end
41
39
  #
42
- def desc(description)
40
+ def desc(description) #:doc:
43
41
  if Restapi.last_description
44
42
  raise "Double method description."
45
43
  end
@@ -47,6 +45,12 @@ module Restapi
47
45
  end
48
46
  alias :description :desc
49
47
 
48
+ # Show some example of what does the described
49
+ # method return.
50
+ def example(example) #:doc:
51
+ Restapi.add_example(example)
52
+ end
53
+
50
54
  # Describe possible errors
51
55
  #
52
56
  # Example:
@@ -56,10 +60,10 @@ module Restapi
56
60
  # puts "hello world"
57
61
  # end
58
62
  #
59
- def error(args)
63
+ def error(args) #:doc:
60
64
  Restapi.last_errors << Restapi::ErrorDescription.new(args)
61
65
  end
62
-
66
+
63
67
  # Describe method's parameter
64
68
  #
65
69
  # Example:
@@ -68,17 +72,17 @@ module Restapi
68
72
  # puts greeting
69
73
  # end
70
74
  #
71
- def param(param_name, *args, &block)
75
+ def param(param_name, *args, &block) #:doc:
72
76
  Restapi.last_params << Restapi::ParamDescription.new(param_name, *args, &block)
73
77
  end
74
-
78
+
75
79
  # create method api and redefine newly added method
76
- def method_added(method_name)
80
+ def method_added(method_name) #:doc:
77
81
 
78
82
  super
79
-
83
+
80
84
  return unless Restapi.restapi_provided?
81
-
85
+
82
86
  # remove method description if exists and create new one
83
87
  Restapi.remove_method_description(self, method_name)
84
88
  description = Restapi.define_method_description(self, method_name)
@@ -86,30 +90,32 @@ module Restapi
86
90
  # redefine method only if validation is turned on
87
91
  if Restapi.configuration.validate == true
88
92
 
89
- old_method = instance_method(method_name)
90
-
93
+ old_method = instance_method(method_name)
94
+
91
95
  define_method(method_name) do |*args|
92
-
93
- description.params.each do |_, param|
94
96
 
95
- # check if required parameters are present
96
- if param.required && !params.has_key?(param.name)
97
- raise ArgumentError.new("Expecting #{param.name} parameter.")
98
- end
99
-
100
- # params validations
101
- if params.has_key?(param.name)
102
- param.validate(params[:"#{param.name}"])
103
- end
97
+ if Restapi.configuration.validate == true
98
+ description.params.each do |_, param|
104
99
 
105
- end # params.each
100
+ # check if required parameters are present
101
+ if param.required && !params.has_key?(param.name)
102
+ raise ArgumentError.new("Expecting #{param.name} parameter.")
103
+ end
104
+
105
+ # params validations
106
+ if params.has_key?(param.name)
107
+ param.validate(params[:"#{param.name}"])
108
+ end
109
+
110
+ end
111
+ end
106
112
 
107
113
  # run the original method code
108
114
  old_method.bind(self).call(*args)
109
115
  end
110
116
 
111
117
  end
112
-
118
+
113
119
  end # def method_added
114
120
  end # module DSL
115
121
  end # module Restapi
@@ -1,40 +1,40 @@
1
1
  module Restapi
2
-
2
+
3
3
  module Markup
4
-
4
+
5
5
  class RDoc
6
-
6
+
7
7
  def initialize
8
8
  require 'rdoc'
9
9
  require 'rdoc/markup/to_html'
10
10
  @rdoc ||= ::RDoc::Markup::ToHtml.new
11
11
  end
12
-
12
+
13
13
  def to_html(text)
14
14
  @rdoc.convert(text)
15
15
  end
16
-
16
+
17
17
  end
18
-
18
+
19
19
  class Markdown
20
-
20
+
21
21
  def initialize
22
22
  require 'redcarpet'
23
23
  @redcarpet ||= ::Redcarpet::Markdown.new(::Redcarpet::Render::HTML.new)
24
24
  end
25
-
25
+
26
26
  def to_html(text)
27
27
  @redcarpet.render(text)
28
28
  end
29
-
29
+
30
30
  end
31
-
31
+
32
32
  class Textile
33
-
33
+
34
34
  def initialize
35
35
  require 'RedCloth'
36
36
  end
37
-
37
+
38
38
  def to_html(text)
39
39
  RedCloth.new(text).to_html
40
40
  end
@@ -7,10 +7,10 @@ module Restapi
7
7
 
8
8
  attr_accessor :short_description, :api_url, :http_method
9
9
 
10
- def initialize(args)
11
- @http_method = args[:method] || args[:http_method] || args[:http]
12
- @short_description = args[:desc] || args[:short] || args[:description]
13
- @api_url = create_api_url(args[:path] || args[:url])
10
+ def initialize(method, path, desc)
11
+ @http_method = method.to_s
12
+ @api_url = create_api_url(path)
13
+ @short_description = desc
14
14
  end
15
15
 
16
16
  private
@@ -21,7 +21,7 @@ module Restapi
21
21
 
22
22
  end
23
23
 
24
- attr_reader :errors, :full_description, :method, :resource, :apis
24
+ attr_reader :errors, :full_description, :method, :resource, :apis, :examples
25
25
 
26
26
  def initialize(method, resource, app)
27
27
  @method = method
@@ -33,6 +33,7 @@ module Restapi
33
33
  @full_description = Restapi.markup_to_html(desc)
34
34
  @errors = app.get_errors
35
35
  @params_ordered = app.get_params
36
+ @examples = app.get_examples
36
37
 
37
38
  parent = @resource.controller.superclass
38
39
  if parent != ActionController::Base
@@ -66,7 +67,7 @@ module Restapi
66
67
  [
67
68
  ENV["RAILS_RELATIVE_URL_ROOT"],
68
69
  Restapi.configuration.doc_base_url,
69
- "##{@resource._id}/#{@method}"
70
+ "/#{@resource._id}/#{@method}"
70
71
  ].join
71
72
  end
72
73
 
@@ -74,7 +75,7 @@ module Restapi
74
75
  @apis.each.collect do |api|
75
76
  {
76
77
  :api_url => api.api_url,
77
- :http_method => api.http_method,
78
+ :http_method => api.http_method.to_s,
78
79
  :short_description => api.short_description
79
80
  }
80
81
  end
@@ -87,7 +88,8 @@ module Restapi
87
88
  :apis => method_apis_to_json,
88
89
  :full_description => @full_description,
89
90
  :errors => @errors,
90
- :params => params_ordered.map(&:to_json).flatten
91
+ :params => params_ordered.map(&:to_json).flatten,
92
+ :examples => @examples
91
93
  }
92
94
  end
93
95
 
@@ -1,17 +1,17 @@
1
1
  module Restapi
2
2
 
3
3
  # method parameter description
4
- #
4
+ #
5
5
  # name - method name (show)
6
6
  # desc - description
7
7
  # required - boolean if required
8
8
  # validator - Validator::BaseValidator subclass
9
9
  class ParamDescription
10
10
 
11
- attr_reader :name, :desc, :required, :validator
11
+ attr_reader :name, :desc, :required, :allow_nil, :validator
12
12
 
13
13
  attr_accessor :parent
14
-
14
+
15
15
  def initialize(name, *args, &block)
16
16
 
17
17
  if args.size > 1 || !args.first.is_a?(Hash)
@@ -20,20 +20,21 @@ module Restapi
20
20
  validator_type = nil
21
21
  end
22
22
  options = args.pop || {}
23
-
23
+
24
24
  @name = name
25
25
  @desc = Restapi.markup_to_html(options[:desc] || '')
26
26
  @required = options[:required] || false
27
-
27
+ @allow_nil = options[:allow_nil] || false
28
+
28
29
  @validator = nil
29
30
  unless validator_type.nil?
30
- @validator =
31
- Validator::BaseValidator.find(self, validator_type, options, block)
31
+ @validator = Validator::BaseValidator.find(self, validator_type, options, block)
32
32
  raise "Validator not found." unless validator
33
33
  end
34
34
  end
35
35
 
36
36
  def validate(value)
37
+ return true if @allow_nil && value.nil?
37
38
  unless @validator.valid?(value)
38
39
  raise ArgumentError.new(@validator.error)
39
40
  end
@@ -57,13 +58,25 @@ module Restapi
57
58
 
58
59
  def to_json
59
60
  if validator.is_a? Restapi::Validator::HashValidator
60
- validator.hash_params_ordered.map(&:to_json)
61
+ {
62
+ :name => name.to_s,
63
+ :full_name => full_name,
64
+ :description => desc,
65
+ :required => required,
66
+ :allow_nil => allow_nil,
67
+ :validator => validator.to_s,
68
+ :expected_type => validator.expected_type,
69
+ :params => validator.hash_params_ordered.map(&:to_json)
70
+ }
61
71
  else
62
72
  {
63
- :name => full_name,
73
+ :name => name.to_s,
74
+ :full_name => full_name,
64
75
  :description => desc,
65
76
  :required => required,
66
- :validator => validator.to_s
77
+ :allow_nil => allow_nil,
78
+ :validator => validator.to_s,
79
+ :expected_type => validator.expected_type
67
80
  }
68
81
  end
69
82
  end
@@ -56,7 +56,7 @@ module Restapi
56
56
  end
57
57
 
58
58
  def doc_url
59
- "#{ENV["RAILS_RELATIVE_URL_ROOT"]}#{Restapi.configuration.doc_base_url}##{@_id}"
59
+ "#{ENV["RAILS_RELATIVE_URL_ROOT"]}#{Restapi.configuration.doc_base_url}/#{@_id}"
60
60
  end
61
61
 
62
62
  def api_url; "#{Restapi.configuration.api_base_url}#{@_path}"; end
@@ -28,6 +28,21 @@ module Restapi
28
28
  class Configuration
29
29
  attr_accessor :app_name, :app_info, :copyright, :markup,
30
30
  :validate, :api_base_url, :doc_base_url
31
+
32
+ # matcher to be used in Dir.glob to find controllers to be reloaded e.g.
33
+ #
34
+ # "#{Rails.root}/app/controllers/api/*.rb"
35
+ attr_accessor :api_controllers_matcher
36
+
37
+ # set to true if you want to reload the controllers at each refresh of the
38
+ # documentation. It requires +:api_controllers_matcher+ to be set to work
39
+ # properly.
40
+ attr_writer :reload_controllers
41
+
42
+ def reload_controllers?
43
+ @reload_controllers = Rails.env.development? unless defined? @reload_controllers
44
+ return @reload_controllers && @api_controllers_matcher
45
+ end
31
46
 
32
47
  def app_info
33
48
  Restapi.markup_to_html(@app_info)