restapi 0.0.2 → 0.0.3
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.
- data/.travis.yml +5 -0
- data/Gemfile +4 -7
- data/Gemfile.lock +5 -25
- data/README.rdoc +3 -0
- data/app/controllers/restapi/restapis_controller.rb +3 -12
- data/app/public/restapi/javascripts/jst.js +41 -22
- data/app/public/restapi/javascripts/restapi.js +4 -5
- data/app/public/restapi/javascripts/routers/documentation_router.js +13 -8
- data/lib/restapi.rb +1 -2
- data/lib/restapi/application.rb +28 -23
- data/lib/restapi/dsl_definition.rb +8 -12
- data/lib/restapi/helpers.rb +2 -2
- data/lib/restapi/markup.rb +45 -0
- data/lib/restapi/method_description.rb +82 -16
- data/lib/restapi/param_description.rb +36 -11
- data/lib/restapi/resource_description.rb +18 -10
- data/lib/restapi/restapi_module.rb +6 -7
- data/lib/restapi/static_dispatcher.rb +2 -0
- data/lib/restapi/validator.rb +40 -31
- data/lib/restapi/version.rb +1 -1
- data/restapi.gemspec +1 -1
- data/spec/controllers/users_controller_spec.rb +156 -32
- data/spec/dummy/app/controllers/application_controller.rb +5 -0
- data/spec/dummy/app/controllers/dogs_controller.rb +0 -2
- data/spec/dummy/app/controllers/users_controller.rb +20 -5
- data/spec/dummy/config/database.yml +2 -3
- data/spec/dummy/config/environment.rb +3 -0
- data/spec/dummy/config/initializers/restapi.rb +6 -2
- data/spec/dummy/config/routes.rb +11 -64
- metadata +7 -21
- data/spec/dummy/app/views/users/doc.html.erb +0 -24
data/lib/restapi/helpers.rb
CHANGED
@@ -0,0 +1,45 @@
|
|
1
|
+
module Restapi
|
2
|
+
|
3
|
+
module Markup
|
4
|
+
|
5
|
+
class RDoc
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
require 'rdoc'
|
9
|
+
require 'rdoc/markup/to_html'
|
10
|
+
@rdoc ||= ::RDoc::Markup::ToHtml.new
|
11
|
+
end
|
12
|
+
|
13
|
+
def to_html(text)
|
14
|
+
@rdoc.convert(text)
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
class Markdown
|
20
|
+
|
21
|
+
def initialize
|
22
|
+
require 'redcarpet'
|
23
|
+
@redcarpet ||= ::Redcarpet::Markdown.new(::Redcarpet::Render::HTML.new)
|
24
|
+
end
|
25
|
+
|
26
|
+
def to_html(text)
|
27
|
+
@redcarpet.render(text)
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
class Textile
|
33
|
+
|
34
|
+
def initialize
|
35
|
+
require 'RedCloth'
|
36
|
+
end
|
37
|
+
|
38
|
+
def to_html(text)
|
39
|
+
RedCloth.new(text).to_html
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
end
|
@@ -1,38 +1,104 @@
|
|
1
|
+
require 'set'
|
1
2
|
module Restapi
|
2
|
-
|
3
|
+
|
3
4
|
class MethodDescription
|
4
|
-
|
5
|
-
|
5
|
+
|
6
|
+
class Api
|
7
|
+
|
8
|
+
attr_accessor :short_description, :api_url, :http_method
|
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])
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def create_api_url(path)
|
19
|
+
"#{Restapi.configuration.api_base_url}#{path}"
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
attr_reader :errors, :full_description, :method, :resource, :apis
|
6
25
|
|
7
26
|
def initialize(method, resource, app)
|
8
27
|
@method = method
|
9
28
|
@resource = resource
|
10
|
-
|
11
|
-
@
|
12
|
-
|
13
|
-
@http = args[:method]
|
29
|
+
|
30
|
+
@apis = app.get_api_args
|
31
|
+
|
14
32
|
desc = app.get_description || ''
|
15
|
-
@full_description = Restapi.
|
33
|
+
@full_description = Restapi.markup_to_html(desc)
|
16
34
|
@errors = app.get_errors
|
17
|
-
@
|
35
|
+
@params_ordered = app.get_params
|
36
|
+
|
37
|
+
parent = @resource.controller.superclass
|
38
|
+
if parent != ActionController::Base
|
39
|
+
@parent_resource = parent.controller_name
|
40
|
+
end
|
41
|
+
@resource.add_method("#{resource._id}##{method}")
|
42
|
+
end
|
43
|
+
|
44
|
+
def params
|
45
|
+
params_ordered.reduce({}) { |h,p| h[p.name] = p; h }
|
46
|
+
end
|
47
|
+
|
48
|
+
def params_ordered
|
49
|
+
all_params = []
|
50
|
+
# get params from parent resource description
|
51
|
+
if @parent_resource
|
52
|
+
parent = Restapi.get_resource_description(@parent_resource)
|
53
|
+
merge_params(all_params, parent._params_ordered) if parent
|
54
|
+
end
|
55
|
+
|
56
|
+
# get params from actual resource description
|
57
|
+
if @resource
|
58
|
+
merge_params(all_params, resource._params_ordered)
|
59
|
+
end
|
60
|
+
|
61
|
+
merge_params(all_params, @params_ordered)
|
62
|
+
all_params.find_all(&:validator)
|
18
63
|
end
|
19
64
|
|
20
|
-
def doc_url
|
21
|
-
|
65
|
+
def doc_url
|
66
|
+
[
|
67
|
+
ENV["RAILS_RELATIVE_URL_ROOT"],
|
68
|
+
Restapi.configuration.doc_base_url,
|
69
|
+
"##{@resource._id}/#{@method}"
|
70
|
+
].join
|
71
|
+
end
|
72
|
+
|
73
|
+
def method_apis_to_json
|
74
|
+
@apis.each.collect do |api|
|
75
|
+
{
|
76
|
+
:api_url => api.api_url,
|
77
|
+
:http_method => api.http_method,
|
78
|
+
:short_description => api.short_description
|
79
|
+
}
|
80
|
+
end
|
81
|
+
end
|
22
82
|
|
23
83
|
def to_json
|
24
84
|
{
|
25
85
|
:doc_url => doc_url,
|
26
|
-
:api_url => api_url,
|
27
86
|
:name => @method,
|
28
|
-
:
|
29
|
-
:short_description => @short_description,
|
87
|
+
:apis => method_apis_to_json,
|
30
88
|
:full_description => @full_description,
|
31
89
|
:errors => @errors,
|
32
|
-
:params =>
|
90
|
+
:params => params_ordered.map(&:to_json).flatten
|
33
91
|
}
|
34
92
|
end
|
35
93
|
|
94
|
+
private
|
95
|
+
|
96
|
+
def merge_params(params, new_params)
|
97
|
+
new_param_names = Set.new(new_params.map(&:name))
|
98
|
+
params.delete_if { |p| new_param_names.include?(p.name) }
|
99
|
+
params.concat(new_params)
|
100
|
+
end
|
101
|
+
|
36
102
|
end
|
37
103
|
|
38
|
-
end
|
104
|
+
end
|
@@ -9,6 +9,8 @@ module Restapi
|
|
9
9
|
class ParamDescription
|
10
10
|
|
11
11
|
attr_reader :name, :desc, :required, :validator
|
12
|
+
|
13
|
+
attr_accessor :parent
|
12
14
|
|
13
15
|
def initialize(name, *args, &block)
|
14
16
|
|
@@ -20,12 +22,15 @@ module Restapi
|
|
20
22
|
options = args.pop || {}
|
21
23
|
|
22
24
|
@name = name
|
23
|
-
@desc = Restapi.
|
25
|
+
@desc = Restapi.markup_to_html(options[:desc] || '')
|
24
26
|
@required = options[:required] || false
|
25
27
|
|
26
|
-
@validator =
|
27
|
-
|
28
|
-
|
28
|
+
@validator = nil
|
29
|
+
unless validator_type.nil?
|
30
|
+
@validator =
|
31
|
+
Validator::BaseValidator.find(self, validator_type, options, block)
|
32
|
+
raise "Validator not found." unless validator
|
33
|
+
end
|
29
34
|
end
|
30
35
|
|
31
36
|
def validate(value)
|
@@ -34,15 +39,35 @@ module Restapi
|
|
34
39
|
end
|
35
40
|
end
|
36
41
|
|
42
|
+
def full_name
|
43
|
+
name_parts = parents_and_self.map(&:name)
|
44
|
+
return ([name_parts.first] + name_parts[1..-1].map { |n| "[#{n}]" }).join("")
|
45
|
+
end
|
46
|
+
|
47
|
+
# returns an array of all the parents: starting with the root parent
|
48
|
+
# ending with itself
|
49
|
+
def parents_and_self
|
50
|
+
ret = []
|
51
|
+
if self.parent
|
52
|
+
ret.concat(self.parent.parents_and_self)
|
53
|
+
end
|
54
|
+
ret << self
|
55
|
+
ret
|
56
|
+
end
|
57
|
+
|
37
58
|
def to_json
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
59
|
+
if validator.is_a? Restapi::Validator::HashValidator
|
60
|
+
validator.hash_params_ordered.map(&:to_json)
|
61
|
+
else
|
62
|
+
{
|
63
|
+
:name => full_name,
|
64
|
+
:description => desc,
|
65
|
+
:required => required,
|
66
|
+
:validator => validator.to_s
|
67
|
+
}
|
68
|
+
end
|
44
69
|
end
|
45
70
|
|
46
71
|
end
|
47
72
|
|
48
|
-
end
|
73
|
+
end
|
@@ -10,12 +10,14 @@ module Restapi
|
|
10
10
|
# id - resouce name
|
11
11
|
class ResourceDescription
|
12
12
|
|
13
|
-
attr_reader :_short_description, :_full_description, :_methods, :_id,
|
13
|
+
attr_reader :controller, :_short_description, :_full_description, :_methods, :_id,
|
14
|
+
:_path, :_version, :_name, :_params_ordered
|
14
15
|
|
15
|
-
def initialize(resource_name, &block)
|
16
|
+
def initialize(controller, resource_name, &block)
|
16
17
|
@_methods = []
|
17
|
-
@
|
18
|
+
@_params_ordered = []
|
18
19
|
|
20
|
+
@controller = controller
|
19
21
|
@_id = resource_name
|
20
22
|
@_version = "1"
|
21
23
|
@_name = @_id.humanize
|
@@ -25,19 +27,24 @@ module Restapi
|
|
25
27
|
|
26
28
|
block.arity < 1 ? instance_eval(&block) : block.call(self) if block_given?
|
27
29
|
end
|
28
|
-
|
30
|
+
|
29
31
|
def param(param_name, *args, &block)
|
30
|
-
|
32
|
+
param_description = Restapi::ParamDescription.new(param_name, *args, &block)
|
33
|
+
@_params_ordered << param_description
|
31
34
|
end
|
32
35
|
|
33
36
|
def path(path); @_path = path; end
|
37
|
+
|
34
38
|
def version(version); @_version = version; end
|
39
|
+
|
35
40
|
def name(name); @_name = name; end
|
41
|
+
|
36
42
|
def short(short); @_short_description = short; end
|
37
43
|
alias :short_description :short
|
44
|
+
|
38
45
|
def desc(description)
|
39
46
|
description ||= ''
|
40
|
-
@_full_description = Restapi.
|
47
|
+
@_full_description = Restapi.markup_to_html(description)
|
41
48
|
end
|
42
49
|
alias :description :desc
|
43
50
|
alias :full_description :desc
|
@@ -48,7 +55,10 @@ module Restapi
|
|
48
55
|
@_methods.uniq!
|
49
56
|
end
|
50
57
|
|
51
|
-
def doc_url
|
58
|
+
def doc_url
|
59
|
+
"#{ENV["RAILS_RELATIVE_URL_ROOT"]}#{Restapi.configuration.doc_base_url}##{@_id}"
|
60
|
+
end
|
61
|
+
|
52
62
|
def api_url; "#{Restapi.configuration.api_base_url}#{@_path}"; end
|
53
63
|
|
54
64
|
def to_json(method_name = nil)
|
@@ -69,7 +79,5 @@ module Restapi
|
|
69
79
|
:methods => _methods
|
70
80
|
}
|
71
81
|
end
|
72
|
-
|
73
82
|
end
|
74
|
-
|
75
|
-
end
|
83
|
+
end
|
@@ -1,7 +1,5 @@
|
|
1
1
|
require "restapi/helpers"
|
2
2
|
require "restapi/application"
|
3
|
-
require "ostruct"
|
4
|
-
require "erb"
|
5
3
|
|
6
4
|
module Restapi
|
7
5
|
extend Restapi::Helpers
|
@@ -28,14 +26,15 @@ module Restapi
|
|
28
26
|
end
|
29
27
|
|
30
28
|
class Configuration
|
31
|
-
attr_accessor :app_name, :app_info, :copyright, :
|
32
|
-
|
33
|
-
|
34
|
-
|
29
|
+
attr_accessor :app_name, :app_info, :copyright, :markup,
|
30
|
+
:validate, :api_base_url, :doc_base_url
|
31
|
+
|
32
|
+
def app_info
|
33
|
+
Restapi.markup_to_html(@app_info)
|
35
34
|
end
|
36
35
|
|
37
36
|
def initialize
|
38
|
-
@
|
37
|
+
@markup = Restapi::Markup::RDoc.new
|
39
38
|
@app_name = "Another API"
|
40
39
|
@app_info = "Another API description"
|
41
40
|
@copyright = nil
|
@@ -46,6 +46,8 @@ module Restapi
|
|
46
46
|
case env['REQUEST_METHOD']
|
47
47
|
when 'GET', 'HEAD'
|
48
48
|
path = env['PATH_INFO'].sub("#{@baseurl}/","/restapi/").chomp('/')
|
49
|
+
path.sub!("#{ENV["RAILS_RELATIVE_URL_ROOT"]}",'')
|
50
|
+
|
49
51
|
if match = @file_handler.match?(path)
|
50
52
|
env["PATH_INFO"] = match
|
51
53
|
return @file_handler.call(env)
|
data/lib/restapi/validator.rb
CHANGED
@@ -6,12 +6,16 @@ module Restapi
|
|
6
6
|
# and implement class method build and instance method validate
|
7
7
|
class BaseValidator
|
8
8
|
|
9
|
-
attr_accessor :
|
9
|
+
attr_accessor :param_description
|
10
|
+
|
11
|
+
def initialize(param_description)
|
12
|
+
@param_description = param_description
|
13
|
+
end
|
10
14
|
|
11
15
|
# find the right validator for given options
|
12
|
-
def self.find(argument, options, block)
|
16
|
+
def self.find(param_description, argument, options, block)
|
13
17
|
self.subclasses.each do |validator_type|
|
14
|
-
validator = validator_type.build(argument, options, block)
|
18
|
+
validator = validator_type.build(param_description, argument, options, block)
|
15
19
|
return validator if validator
|
16
20
|
end
|
17
21
|
return nil
|
@@ -28,6 +32,10 @@ module Restapi
|
|
28
32
|
end
|
29
33
|
end
|
30
34
|
|
35
|
+
def param_name
|
36
|
+
@param_description.name
|
37
|
+
end
|
38
|
+
|
31
39
|
# validator description
|
32
40
|
def description
|
33
41
|
"TODO: validator description"
|
@@ -46,7 +54,8 @@ module Restapi
|
|
46
54
|
# validate arguments type
|
47
55
|
class TypeValidator < BaseValidator
|
48
56
|
|
49
|
-
def initialize(argument)
|
57
|
+
def initialize(param_description, argument)
|
58
|
+
super(param_description)
|
50
59
|
@type = argument
|
51
60
|
@type = Integer if @type == Fixnum
|
52
61
|
end
|
@@ -60,8 +69,8 @@ module Restapi
|
|
60
69
|
end
|
61
70
|
end
|
62
71
|
|
63
|
-
def self.build(argument, options, block)
|
64
|
-
self.new(argument) if argument.is_a?(Class) && argument != Hash
|
72
|
+
def self.build(param_description, argument, options, block)
|
73
|
+
self.new(param_description, argument) if argument.is_a?(Class) && (argument != Hash || block.nil?)
|
65
74
|
end
|
66
75
|
|
67
76
|
def error
|
@@ -76,7 +85,8 @@ module Restapi
|
|
76
85
|
# validate arguments value with regular expression
|
77
86
|
class RegexpValidator < BaseValidator
|
78
87
|
|
79
|
-
def initialize(argument)
|
88
|
+
def initialize(param_description, argument)
|
89
|
+
super(param_description)
|
80
90
|
@regexp = argument
|
81
91
|
end
|
82
92
|
|
@@ -84,8 +94,8 @@ module Restapi
|
|
84
94
|
value =~ @regexp
|
85
95
|
end
|
86
96
|
|
87
|
-
def self.build(argument, options, proc)
|
88
|
-
self.new(argument) if argument.is_a? Regexp
|
97
|
+
def self.build(param_description, argument, options, proc)
|
98
|
+
self.new(param_description, argument) if argument.is_a? Regexp
|
89
99
|
end
|
90
100
|
|
91
101
|
def error
|
@@ -100,26 +110,17 @@ module Restapi
|
|
100
110
|
# arguments value must be one of given in array
|
101
111
|
class ArrayValidator < BaseValidator
|
102
112
|
|
103
|
-
def initialize(argument)
|
113
|
+
def initialize(param_description, argument)
|
114
|
+
super(param_description)
|
104
115
|
@array = argument
|
105
116
|
end
|
106
117
|
|
107
118
|
def validate(value)
|
108
|
-
|
109
|
-
@array.find do |expected|
|
110
|
-
expected_class = expected.class
|
111
|
-
expected_class = Integer if expected_class == Fixnum
|
112
|
-
begin
|
113
|
-
converted_value = Kernel.send(expected_class.to_s, value)
|
114
|
-
rescue ArgumentError
|
115
|
-
false
|
116
|
-
end
|
117
|
-
converted_value === expected
|
118
|
-
end
|
119
|
+
@array.include?(value)
|
119
120
|
end
|
120
121
|
|
121
|
-
def self.build(argument, options, proc)
|
122
|
-
self.new(argument) if argument.is_a?(Array)
|
122
|
+
def self.build(param_description, argument, options, proc)
|
123
|
+
self.new(param_description, argument) if argument.is_a?(Array)
|
123
124
|
end
|
124
125
|
|
125
126
|
def error
|
@@ -133,7 +134,8 @@ module Restapi
|
|
133
134
|
|
134
135
|
class ProcValidator < BaseValidator
|
135
136
|
|
136
|
-
def initialize(argument)
|
137
|
+
def initialize(param_description, argument)
|
138
|
+
super(param_description)
|
137
139
|
@proc = argument
|
138
140
|
end
|
139
141
|
|
@@ -141,8 +143,8 @@ module Restapi
|
|
141
143
|
(@help = @proc.call(value)) === true
|
142
144
|
end
|
143
145
|
|
144
|
-
def self.build(argument, options, proc)
|
145
|
-
self.new(argument) if argument.is_a?(Proc) && argument.arity == 1
|
146
|
+
def self.build(param_description, argument, options, proc)
|
147
|
+
self.new(param_description, argument) if argument.is_a?(Proc) && argument.arity == 1
|
146
148
|
end
|
147
149
|
|
148
150
|
def error
|
@@ -156,12 +158,16 @@ module Restapi
|
|
156
158
|
|
157
159
|
class HashValidator < BaseValidator
|
158
160
|
|
159
|
-
|
160
|
-
|
161
|
+
attr_reader :hash_params_ordered
|
162
|
+
|
163
|
+
def self.build(param_description, argument, options, block)
|
164
|
+
self.new(param_description, block) if block.is_a?(Proc) && block.arity <= 0 && argument == Hash
|
161
165
|
end
|
162
166
|
|
163
|
-
def initialize(argument)
|
167
|
+
def initialize(param_description, argument)
|
168
|
+
super(param_description)
|
164
169
|
@proc = argument
|
170
|
+
@hash_params_ordered = []
|
165
171
|
@hash_params = {}
|
166
172
|
|
167
173
|
self.instance_exec(&@proc)
|
@@ -185,9 +191,12 @@ module Restapi
|
|
185
191
|
end
|
186
192
|
|
187
193
|
def param(param_name, *args, &block)
|
188
|
-
|
194
|
+
param_description = Restapi::ParamDescription.new(param_name, *args, &block)
|
195
|
+
param_description.parent = self.param_description
|
196
|
+
@hash_params_ordered << param_description
|
197
|
+
@hash_params[param_name.to_sym] = param_description
|
189
198
|
end
|
190
199
|
end
|
191
200
|
|
192
201
|
end
|
193
|
-
end
|
202
|
+
end
|