restapi 0.0.3 → 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +2 -1
- data/Gemfile.lock +83 -64
- data/README.rdoc +375 -2
- data/app/controllers/restapi/restapis_controller.rb +21 -3
- data/app/helpers/restapi/restapis_helper.rb +31 -0
- data/app/public/restapi/javascripts/bundled/prettify.js +28 -0
- data/app/public/restapi/javascripts/restapi.js +4 -13
- data/app/public/restapi/stylesheets/bundled/prettify.css +30 -0
- data/app/views/layouts/restapi/restapi.html.erb +36 -0
- data/app/views/restapi/restapis/index.html.erb +35 -33
- data/app/views/restapi/restapis/method.html.erb +59 -0
- data/app/views/restapi/restapis/resource.html.erb +71 -0
- data/app/views/restapi/restapis/static.html.erb +103 -0
- data/lib/restapi.rb +0 -10
- data/lib/restapi/application.rb +25 -10
- data/lib/restapi/dsl_definition.rb +38 -32
- data/lib/restapi/markup.rb +12 -12
- data/lib/restapi/method_description.rb +10 -8
- data/lib/restapi/param_description.rb +23 -10
- data/lib/restapi/resource_description.rb +1 -1
- data/lib/restapi/restapi_module.rb +15 -0
- data/lib/restapi/validator.rb +37 -14
- data/lib/restapi/version.rb +1 -1
- data/lib/tasks/restapi.rake +157 -0
- data/restapi.gemspec +1 -1
- data/spec/controllers/restapis_controller_spec.rb +84 -0
- data/spec/controllers/users_controller_spec.rb +273 -221
- data/spec/dummy/app/controllers/twitter_example_controller.rb +209 -208
- data/spec/dummy/app/controllers/users_controller.rb +23 -33
- data/spec/dummy/config/initializers/restapi.rb +45 -23
- data/spec/dummy/config/routes.rb +12 -7
- metadata +26 -15
- data/app/public/restapi/javascripts/bundled/backbone.js +0 -1431
- data/app/public/restapi/javascripts/bundled/json2.js +0 -487
- data/app/public/restapi/javascripts/bundled/underscore.js +0 -999
- data/app/public/restapi/javascripts/jst.js +0 -127
- data/app/public/restapi/javascripts/routers/documentation_router.js +0 -52
- data/app/public/restapi/stylesheets/bundled/bootstrap-responsive.css +0 -686
- data/app/public/restapi/stylesheets/bundled/bootstrap.css +0 -3990
- data/spec/dummy/app/controllers/dogs_controller.rb +0 -20
- data/spec/dummy/app/helpers/application_helper.rb +0 -2
data/lib/restapi.rb
CHANGED
@@ -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"
|
data/lib/restapi/application.rb
CHANGED
@@ -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(
|
50
|
-
@last_api_args << MethodDescription::Api.new(
|
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
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
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 :
|
27
|
-
# :path => "/resource_route",
|
28
|
-
# :method => "GET"
|
26
|
+
# api :GET, "/resource_route", "short description",
|
29
27
|
#
|
30
|
-
def api(
|
31
|
-
Restapi.add_method_description_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
|
-
|
96
|
-
|
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
|
-
|
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
|
data/lib/restapi/markup.rb
CHANGED
@@ -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(
|
11
|
-
@http_method =
|
12
|
-
@
|
13
|
-
@
|
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
|
-
"
|
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
|
-
|
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 =>
|
73
|
+
:name => name.to_s,
|
74
|
+
:full_name => full_name,
|
64
75
|
:description => desc,
|
65
76
|
:required => required,
|
66
|
-
:
|
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}
|
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)
|