restapi 0.0.3 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
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)