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.
- 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/validator.rb
CHANGED
@@ -12,9 +12,14 @@ module Restapi
|
|
12
12
|
@param_description = param_description
|
13
13
|
end
|
14
14
|
|
15
|
+
def self.inherited(subclass)
|
16
|
+
@validators ||= []
|
17
|
+
@validators.insert 0, subclass
|
18
|
+
end
|
19
|
+
|
15
20
|
# find the right validator for given options
|
16
21
|
def self.find(param_description, argument, options, block)
|
17
|
-
|
22
|
+
@validators.each do |validator_type|
|
18
23
|
validator = validator_type.build(param_description, argument, options, block)
|
19
24
|
return validator if validator
|
20
25
|
end
|
@@ -49,6 +54,13 @@ module Restapi
|
|
49
54
|
self.description
|
50
55
|
end
|
51
56
|
|
57
|
+
# what type is expected, mostly string
|
58
|
+
# this information is used in cli client
|
59
|
+
# thor supported types — :string, :hash, :array, :numeric, or :boolean
|
60
|
+
def expected_type
|
61
|
+
'string'
|
62
|
+
end
|
63
|
+
|
52
64
|
end
|
53
65
|
|
54
66
|
# validate arguments type
|
@@ -57,29 +69,36 @@ module Restapi
|
|
57
69
|
def initialize(param_description, argument)
|
58
70
|
super(param_description)
|
59
71
|
@type = argument
|
60
|
-
@type = Integer if @type == Fixnum
|
61
72
|
end
|
62
73
|
|
63
74
|
def validate(value)
|
64
75
|
return false if value.nil?
|
65
|
-
|
66
|
-
Kernel.send(@type.to_s, value)
|
67
|
-
rescue ArgumentError
|
68
|
-
return false
|
69
|
-
end
|
76
|
+
value.is_a? @type
|
70
77
|
end
|
71
78
|
|
72
79
|
def self.build(param_description, argument, options, block)
|
73
|
-
|
80
|
+
if argument.is_a?(Class) && (argument != Hash || block.nil?)
|
81
|
+
self.new(param_description, argument)
|
82
|
+
end
|
74
83
|
end
|
75
84
|
|
76
85
|
def error
|
77
|
-
"Parameter #{
|
86
|
+
"Parameter #{param_name} expecting to be #{@type.name}, got: #{@error_value.class.name}"
|
78
87
|
end
|
79
88
|
|
80
89
|
def description
|
81
90
|
"Parameter has to be #{@type}."
|
82
91
|
end
|
92
|
+
|
93
|
+
def expected_type
|
94
|
+
if @type.ancestors.include? Hash
|
95
|
+
'hash'
|
96
|
+
elsif @type.ancestors.include? Numeric
|
97
|
+
'numeric'
|
98
|
+
else
|
99
|
+
'string'
|
100
|
+
end
|
101
|
+
end
|
83
102
|
end
|
84
103
|
|
85
104
|
# validate arguments value with regular expression
|
@@ -99,7 +118,7 @@ module Restapi
|
|
99
118
|
end
|
100
119
|
|
101
120
|
def error
|
102
|
-
"Parameter #{
|
121
|
+
"Parameter #{param_name} expecting to match /#{@regexp.source}/, got '#{@error_value}'"
|
103
122
|
end
|
104
123
|
|
105
124
|
def description
|
@@ -124,7 +143,7 @@ module Restapi
|
|
124
143
|
end
|
125
144
|
|
126
145
|
def error
|
127
|
-
"Parameter #{
|
146
|
+
"Parameter #{param_name} has bad value (#{@error_value.inspect}). Expecting one of: #{@array.join(',')}."
|
128
147
|
end
|
129
148
|
|
130
149
|
def description
|
@@ -148,7 +167,7 @@ module Restapi
|
|
148
167
|
end
|
149
168
|
|
150
169
|
def error
|
151
|
-
"Parameter #{
|
170
|
+
"Parameter #{param_name} has bad value (\"#{@error_value}\"). #{@help}"
|
152
171
|
end
|
153
172
|
|
154
173
|
def description
|
@@ -183,11 +202,11 @@ module Restapi
|
|
183
202
|
end
|
184
203
|
|
185
204
|
def error
|
186
|
-
"
|
205
|
+
"Has to be hash."
|
187
206
|
end
|
188
207
|
|
189
208
|
def description
|
190
|
-
"
|
209
|
+
"Has to be hash."
|
191
210
|
end
|
192
211
|
|
193
212
|
def param(param_name, *args, &block)
|
@@ -196,6 +215,10 @@ module Restapi
|
|
196
215
|
@hash_params_ordered << param_description
|
197
216
|
@hash_params[param_name.to_sym] = param_description
|
198
217
|
end
|
218
|
+
|
219
|
+
def expected_type
|
220
|
+
'hash'
|
221
|
+
end
|
199
222
|
end
|
200
223
|
|
201
224
|
end
|
data/lib/restapi/version.rb
CHANGED
@@ -0,0 +1,157 @@
|
|
1
|
+
namespace :restapi do
|
2
|
+
|
3
|
+
desc "Generate static documentation"
|
4
|
+
task :static => :environment do
|
5
|
+
|
6
|
+
av = ActionView::Base.new(File.expand_path("../../../app/views", __FILE__))
|
7
|
+
|
8
|
+
av.class_eval do
|
9
|
+
include Restapi::RestapisHelper
|
10
|
+
end
|
11
|
+
|
12
|
+
Dir[File.join(Rails.root, "app", "controllers", "**","*.rb")].each {|f| load f}
|
13
|
+
|
14
|
+
doc = Restapi.to_json()[:docs]
|
15
|
+
|
16
|
+
# dir in public directory
|
17
|
+
dir_path = File.join(::Rails.root.to_s, 'public', Restapi.configuration.doc_base_url)
|
18
|
+
FileUtils.rm_r(dir_path) if File.directory?(dir_path)
|
19
|
+
Dir.mkdir(dir_path)
|
20
|
+
|
21
|
+
copy_jscss(File.join(dir_path, Restapi.configuration.doc_base_url))
|
22
|
+
|
23
|
+
Restapi.configuration.doc_base_url = Restapi.configuration.doc_base_url[1..-1]
|
24
|
+
File.open(File.join(dir_path,'index.html'), "w") do |f|
|
25
|
+
f.write av.render(
|
26
|
+
:template => "restapi/restapis/static",
|
27
|
+
:locals => {:doc => doc},
|
28
|
+
:layout => 'layouts/restapi/restapi')
|
29
|
+
puts File.join(dir_path,'index.html')
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
def copy_jscss(dest)
|
35
|
+
src = File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'app', 'public', 'restapi'))
|
36
|
+
FileUtils.cp_r "#{src}/.", dest
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
desc "Generate CLI client for API documented with restapi gem."
|
41
|
+
task :client, [:api_base_url, :url_append] => [:environment] do |t, args|
|
42
|
+
|
43
|
+
args.with_defaults :api_base_url => 'http://localhost:3000', :url_append => ''
|
44
|
+
|
45
|
+
ActiveSupport::Dependencies.autoload_paths.each do |path|
|
46
|
+
Dir[path + "/*.rb"].each { |file| require file }
|
47
|
+
end
|
48
|
+
|
49
|
+
Dir.mkdir('clients') unless File.exists?('clients')
|
50
|
+
|
51
|
+
doc = Restapi.to_json()[:docs]
|
52
|
+
|
53
|
+
create_base doc, args
|
54
|
+
|
55
|
+
create_cli doc
|
56
|
+
end
|
57
|
+
|
58
|
+
def ac(content, code, spaces = 0)
|
59
|
+
content << " "*spaces << code << "\n"
|
60
|
+
end
|
61
|
+
|
62
|
+
def create_base doc, args
|
63
|
+
content = ""
|
64
|
+
ac content, "require 'rubygems'"
|
65
|
+
ac content, "require 'weary'"
|
66
|
+
ac content, "require 'thread' unless defined? Mutex\n"
|
67
|
+
ac content, "API_BASE_URL = ENV['API_URL'] || '#{args[:api_base_url]}'"
|
68
|
+
ac content, "URL_APPEND = ENV['URL_APPEND'] || '#{args[:url_append]}'\n"
|
69
|
+
|
70
|
+
doc[:resources].each do |key, resource|
|
71
|
+
|
72
|
+
ac content, "class #{key.camelize}Client < Weary::Client"
|
73
|
+
ac content, "domain API_BASE_URL", 2
|
74
|
+
resource[:methods].each do |method|
|
75
|
+
method[:apis].each do |api|
|
76
|
+
ac content, "# #{api[:short_description]}", 2
|
77
|
+
ac content, "#{api[:http_method].downcase} :#{method[:name]}, \"#{api[:api_url]}\#{URL_APPEND}\" do |resource|", 2
|
78
|
+
required = []
|
79
|
+
optional = []
|
80
|
+
|
81
|
+
method[:params].each do |param|
|
82
|
+
if param[:required]
|
83
|
+
required << param[:name].to_sym
|
84
|
+
else
|
85
|
+
optional << param[:name].to_sym
|
86
|
+
end
|
87
|
+
end
|
88
|
+
ac content, "resource.optional :#{optional.join(', :')}", 4 unless optional.blank?
|
89
|
+
ac content, "resource.required :#{required.join(', :')}", 4 unless required.blank?
|
90
|
+
|
91
|
+
ac content, "end", 2
|
92
|
+
end
|
93
|
+
end
|
94
|
+
ac content, "end\n"
|
95
|
+
end
|
96
|
+
|
97
|
+
File.open('clients/base.rb', 'w') { |f| f.write content }
|
98
|
+
end
|
99
|
+
|
100
|
+
def create_cli doc
|
101
|
+
|
102
|
+
content = ""
|
103
|
+
ac content, "require 'rubygems'"
|
104
|
+
ac content, "require 'thor'"
|
105
|
+
ac content, "require './base'"
|
106
|
+
|
107
|
+
doc[:resources].each do |key, resource|
|
108
|
+
ac content, "class #{key.camelize} < Thor"
|
109
|
+
# ac content, "$klasses['#{key}'] = #{key.camelize}", 2
|
110
|
+
# ac content, "@client = RestClient::Resource.new \"\#{$API_URL}#{resource[:api_url]}\"\n", 2
|
111
|
+
|
112
|
+
ac content, "no_tasks do", 2
|
113
|
+
ac content, " def client", 2
|
114
|
+
ac content, " @client ||= #{key.camelize}Client.new", 2
|
115
|
+
ac content, " end", 2
|
116
|
+
ac content, "end", 2
|
117
|
+
|
118
|
+
resource[:methods].each do |method|
|
119
|
+
|
120
|
+
method[:apis].each do |api|
|
121
|
+
|
122
|
+
params = []
|
123
|
+
method[:params].each do |param|
|
124
|
+
ac content, "method_option :#{param[:name]},", 2
|
125
|
+
ac content, ":required => #{param[:required] ? 'true' : 'false' },", 4
|
126
|
+
ac content, ":desc => '#{plaintext(param[:description])}',", 4
|
127
|
+
ac content, ":type => :#{param[:expected_type]}", 4
|
128
|
+
# :type — :string, :hash, :array, :numeric, or :boolean
|
129
|
+
params << param[:name]
|
130
|
+
end
|
131
|
+
|
132
|
+
ac content, "desc '#{method[:name]}', '#{api[:short_description]}'", 2
|
133
|
+
ac content, "def #{method[:name]}", 2
|
134
|
+
ac content, " resp = client.#{method[:name]}(options).perform do |response|", 2
|
135
|
+
ac content, " if response.success?", 2
|
136
|
+
ac content, " puts response.body", 2
|
137
|
+
ac content, " else", 2
|
138
|
+
ac content, " puts \"status: \#{response.status}\"", 2
|
139
|
+
ac content, " puts response.body", 2
|
140
|
+
ac content, " end", 2
|
141
|
+
ac content, " end", 2
|
142
|
+
ac content, " resp.status", 2
|
143
|
+
ac content, "end\n", 2
|
144
|
+
|
145
|
+
end
|
146
|
+
|
147
|
+
end
|
148
|
+
ac content, "end\n"
|
149
|
+
end
|
150
|
+
|
151
|
+
File.open('clients/cli.thor', 'w') { |f| f.write content }
|
152
|
+
end
|
153
|
+
|
154
|
+
def plaintext(text)
|
155
|
+
text.gsub(/<.*?>/, '').gsub("\n",' ').strip
|
156
|
+
end
|
157
|
+
end
|
data/restapi.gemspec
CHANGED
@@ -9,5 +9,89 @@ describe Restapi::RestapisController do
|
|
9
9
|
|
10
10
|
assert_response :success
|
11
11
|
end
|
12
|
+
|
13
|
+
end
|
14
|
+
|
15
|
+
describe "reload_controllers" do
|
16
|
+
|
17
|
+
RSpec::Matchers.define :reload_documentation do
|
18
|
+
match do
|
19
|
+
begin
|
20
|
+
orig = Restapi.get_resource_description("users")._short_description.dup
|
21
|
+
Restapi.get_resource_description("users")._short_description << 'Modified'
|
22
|
+
get :index
|
23
|
+
ret = Restapi.get_resource_description("users")._short_description == orig
|
24
|
+
ensure
|
25
|
+
Restapi.get_resource_description("users")._short_description.gsub!('Modified', "")
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
failure_message_for_should { "the documentation expected to be reloaded but it was not" }
|
30
|
+
failure_message_for_should_not { "the documentation expected not to be reloaded but it was" }
|
31
|
+
end
|
32
|
+
|
33
|
+
before do
|
34
|
+
Restapi.configuration.api_controllers_matcher = File.join(Rails.root, "app", "controllers", "**","*.rb")
|
35
|
+
if Restapi.configuration.send :instance_variable_defined?, "@reload_controllers"
|
36
|
+
Restapi.configuration.send :remove_instance_variable, "@reload_controllers"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
context "it's not specified explicitly" do
|
41
|
+
context "and it's in development environment" do
|
42
|
+
before do
|
43
|
+
Rails.stub(:env => mock(:development? => true))
|
44
|
+
end
|
45
|
+
it { should reload_documentation }
|
46
|
+
end
|
47
|
+
|
48
|
+
context "and it's not development environment" do
|
49
|
+
it { should_not reload_documentation }
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
|
54
|
+
context "it's explicitly enabled" do
|
55
|
+
before do
|
56
|
+
Restapi.configuration.reload_controllers = true
|
57
|
+
end
|
58
|
+
|
59
|
+
context "and it's in development environment" do
|
60
|
+
before do
|
61
|
+
Rails.stub(:env => mock(:development? => true))
|
62
|
+
end
|
63
|
+
it { should reload_documentation }
|
64
|
+
end
|
65
|
+
|
66
|
+
context "and it's not development environment" do
|
67
|
+
it { should reload_documentation }
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
context "it's explicitly enabled" do
|
72
|
+
before do
|
73
|
+
Restapi.configuration.reload_controllers = false
|
74
|
+
end
|
75
|
+
|
76
|
+
context "and it's in development environment" do
|
77
|
+
before do
|
78
|
+
Rails.stub(:env => mock(:development? => true))
|
79
|
+
end
|
80
|
+
it { should_not reload_documentation }
|
81
|
+
end
|
82
|
+
|
83
|
+
context "and it's not development environment" do
|
84
|
+
it { should_not reload_documentation }
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
context "api_controllers_matcher is specified" do
|
89
|
+
before do
|
90
|
+
Restapi.configuration.reload_controllers = true
|
91
|
+
Restapi.configuration.api_controllers_matcher = nil
|
92
|
+
end
|
93
|
+
|
94
|
+
it { should_not reload_documentation }
|
95
|
+
end
|
12
96
|
end
|
13
97
|
end
|
@@ -1,208 +1,205 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
a._methods.should include("users#create")
|
14
|
-
a._methods.should include("users#index")
|
15
|
-
a._methods.should include("users#two_urls")
|
16
|
-
md = a._params_ordered.find { |p| p.name == :id }
|
17
|
-
md.should_not be(nil)
|
18
|
-
md.name.should eq(:id)
|
19
|
-
md.desc.should eq("\n<p>User ID</p>\n")
|
20
|
-
md.required.should eq(false)
|
21
|
-
md.validator.class.should eq(Restapi::Validator::TypeValidator)
|
22
|
-
a._id.should eq('users')
|
23
|
-
a._path.should eq('/users')
|
24
|
-
a._version.should eq('1.0 - 3.4.2012')
|
25
|
-
a._name.should eq('Members')
|
3
|
+
def compare_hashes(h1, h2)
|
4
|
+
h1.each do |key, val|
|
5
|
+
if val.is_a? Hash
|
6
|
+
compare_hashes val, h2[key]
|
7
|
+
elsif val.is_a? Array
|
8
|
+
val.each_with_index do |v, i|
|
9
|
+
compare_hashes val[i], h2[key][i]
|
10
|
+
end
|
11
|
+
else
|
12
|
+
val.should eq(h2[key])
|
26
13
|
end
|
27
14
|
end
|
15
|
+
end
|
28
16
|
|
29
|
-
|
17
|
+
describe UsersController do
|
30
18
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
19
|
+
describe "resource description" do
|
20
|
+
subject { a = Restapi.get_resource_description(UsersController) }
|
21
|
+
|
22
|
+
it "should contain all resource methods" do
|
23
|
+
methods = subject._methods
|
24
|
+
methods.count.should == 4
|
25
|
+
methods.should include("users#show")
|
26
|
+
methods.should include("users#create")
|
27
|
+
methods.should include("users#index")
|
28
|
+
methods.should include("users#two_urls")
|
35
29
|
end
|
36
30
|
|
37
|
-
it "
|
38
|
-
|
31
|
+
it "should contain info about resource" do
|
32
|
+
subject._short_description.should eq('Site members')
|
33
|
+
subject._id.should eq('users')
|
34
|
+
subject._path.should eq('/users')
|
35
|
+
subject._version.should eq('1.0 - 3.4.2012')
|
36
|
+
subject._name.should eq('Members')
|
39
37
|
end
|
40
|
-
|
41
|
-
it "
|
42
|
-
|
43
|
-
|
38
|
+
|
39
|
+
it "should contain params defined on resource level" do
|
40
|
+
subject._params_ordered.count.should == 2
|
41
|
+
p = subject._params_ordered.first
|
42
|
+
p.should_not be(nil)
|
43
|
+
p.name.should eq(:id)
|
44
|
+
p.desc.should eq("\n<p>User ID</p>\n")
|
45
|
+
p.required.should eq(false)
|
46
|
+
p.validator.class.should eq(Restapi::Validator::IntegerValidator)
|
47
|
+
|
48
|
+
p = subject._params_ordered.second
|
49
|
+
p.should_not be(nil)
|
50
|
+
p.name.should eq(:resource_param)
|
51
|
+
p.desc.should eq("\n<p>Param description for all methods</p>\n")
|
52
|
+
p.required.should eq(false)
|
53
|
+
p.validator.class.should eq(Restapi::Validator::HashValidator)
|
44
54
|
end
|
55
|
+
end
|
45
56
|
|
46
|
-
|
47
|
-
|
48
|
-
a = Restapi.get_method_description(UsersController, :show)
|
49
|
-
b = Restapi[UsersController, :show]
|
50
|
-
a.should eq(b)
|
57
|
+
describe "validators" do
|
51
58
|
|
52
|
-
|
53
|
-
|
54
|
-
a.errors[0].code.should eq(401)
|
55
|
-
a.errors[0].description.should eq("Unauthorized")
|
56
|
-
a.errors[1].code.should eq(404)
|
57
|
-
a.errors[1].description.should eq("Not Found")
|
59
|
+
context "validations are disabled" do
|
60
|
+
before { Restapi.configuration.validate = false }
|
58
61
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
a.apis.first.http_method.should eq("GET")
|
62
|
+
it "should reply to valid request" do
|
63
|
+
get :show, :id => '5', :session => "secret_hash"
|
64
|
+
assert_response :success
|
65
|
+
end
|
64
66
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
param.validator.class.should be(Restapi::Validator::TypeValidator)
|
69
|
-
param.validator.instance_variable_get("@type").should eq(String)
|
70
|
-
|
71
|
-
param = a.params[:float_param]
|
72
|
-
param.required.should eq(false)
|
73
|
-
param.desc.should eq("\n<p>float param</p>\n")
|
74
|
-
param.validator.class.should be(Restapi::Validator::TypeValidator)
|
75
|
-
param.validator.instance_variable_get("@type").should eq(Float)
|
67
|
+
it "should pass if required parameter is missing" do
|
68
|
+
lambda { get :show, :id => 5 }.should_not raise_error
|
69
|
+
end
|
76
70
|
|
77
|
-
|
78
|
-
param.required.should eq(true)
|
79
|
-
param.desc.should eq("\n<p>user id</p>\n")
|
80
|
-
param.validator.class.should be(Restapi::Validator::TypeValidator)
|
81
|
-
param.validator.instance_variable_get("@type").should eq(Integer)
|
71
|
+
end
|
82
72
|
|
83
|
-
param = a.params[:regexp_param]
|
84
|
-
param.desc.should eq("\n<p>regexp param</p>\n")
|
85
|
-
param.required.should eq(false)
|
86
|
-
param.validator.class.should be(Restapi::Validator::RegexpValidator)
|
87
|
-
param.validator.instance_variable_get("@regexp").should
|
88
|
-
eq(/^[0-9]* years/)
|
89
73
|
|
90
|
-
|
91
|
-
|
92
|
-
param.validator.class.should be(Restapi::Validator::ArrayValidator)
|
93
|
-
param.validator.instance_variable_get("@array").should
|
94
|
-
eq([100, "one", "two", 1, 2])
|
74
|
+
context "validations are enabled" do
|
75
|
+
before { Restapi.configuration.validate = true }
|
95
76
|
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
a.full_description.length.should be > 400
|
101
|
-
end
|
102
|
-
|
103
|
-
it "should yell ArgumentError if id is not a number" do
|
104
|
-
lambda {
|
105
|
-
get :show,
|
106
|
-
:id => "not a number",
|
107
|
-
:session => "secret_hash"
|
108
|
-
}.should raise_error(ArgumentError)
|
109
|
-
end
|
110
|
-
|
111
|
-
it "should yell ArgumentError if float_param is not a float" do
|
112
|
-
lambda {
|
113
|
-
get :show,
|
114
|
-
:id => 5,
|
115
|
-
:session => "secret_hash",
|
116
|
-
:float_param => "234.2.34"
|
117
|
-
}.should raise_error(ArgumentError)
|
118
|
-
|
119
|
-
lambda {
|
120
|
-
get :show,
|
121
|
-
:id => 5,
|
122
|
-
:session => "secret_hash",
|
123
|
-
:float_param => "no float here"
|
124
|
-
}.should raise_error(ArgumentError)
|
125
|
-
end
|
77
|
+
it "should reply to valid request" do
|
78
|
+
get :show, :id => '5', :session => "secret_hash"
|
79
|
+
assert_response :success
|
80
|
+
end
|
126
81
|
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
end
|
141
|
-
|
142
|
-
it "should validate with regexp validator" do
|
143
|
-
lambda {
|
144
|
-
get :show,
|
145
|
-
:id => 5,
|
146
|
-
:session => "secret_hash",
|
147
|
-
:regexp_param => "ten years"
|
148
|
-
}.should raise_error(ArgumentError)
|
149
|
-
end
|
150
|
-
|
151
|
-
it "should validate with array validator" do
|
152
|
-
get :show, :id => 5, :session => "secret_hash", :array_param => "one"
|
153
|
-
assert_response :success
|
154
|
-
get :show, :id => 5, :session => "secret_hash", :array_param => "two"
|
155
|
-
assert_response :success
|
156
|
-
get :show, :id => 5, :session => "secret_hash", :array_param => 1
|
157
|
-
assert_response :success
|
158
|
-
get :show, :id => 5, :session => "secret_hash", :boolean_param => false
|
159
|
-
assert_response :success
|
160
|
-
end
|
161
|
-
|
162
|
-
it "should raise ArgumentError with array validator" do
|
163
|
-
lambda {
|
82
|
+
it "should fail if required parameter is missing" do
|
83
|
+
lambda { get :show, :id => 5 }.should raise_error(ArgumentError, / session /)
|
84
|
+
end
|
85
|
+
|
86
|
+
it "should work with Type validator" do
|
87
|
+
lambda {
|
88
|
+
get :show,
|
89
|
+
:id => "not a number",
|
90
|
+
:session => "secret_hash"
|
91
|
+
}.should raise_error(ArgumentError, / id /)
|
92
|
+
end
|
93
|
+
|
94
|
+
it "should work with Regexp validator" do
|
164
95
|
get :show,
|
165
96
|
:id => 5,
|
166
97
|
:session => "secret_hash",
|
167
|
-
:
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
98
|
+
:regexp_param => "24 years"
|
99
|
+
assert_response :success
|
100
|
+
|
101
|
+
lambda {
|
102
|
+
get :show,
|
103
|
+
:id => 5,
|
104
|
+
:session => "secret_hash",
|
105
|
+
:regexp_param => "ten years"
|
106
|
+
}.should raise_error(ArgumentError, / regexp_param /)
|
107
|
+
end
|
108
|
+
|
109
|
+
it "should work with Array validator" do
|
110
|
+
get :show, :id => 5, :session => "secret_hash", :array_param => "one"
|
111
|
+
assert_response :success
|
112
|
+
get :show, :id => 5, :session => "secret_hash", :array_param => "two"
|
113
|
+
assert_response :success
|
114
|
+
get :show, :id => 5, :session => "secret_hash", :array_param => 1
|
115
|
+
assert_response :success
|
116
|
+
get :show, :id => 5, :session => "secret_hash", :boolean_param => false
|
117
|
+
assert_response :success
|
118
|
+
|
119
|
+
lambda {
|
120
|
+
get :show,
|
121
|
+
:id => 5,
|
122
|
+
:session => "secret_hash",
|
123
|
+
:array_param => "blabla"
|
124
|
+
}.should raise_error(ArgumentError, / array_param /)
|
125
|
+
|
126
|
+
lambda {
|
127
|
+
get :show,
|
128
|
+
:id => 5,
|
129
|
+
:session => "secret_hash",
|
130
|
+
:array_param => 3
|
131
|
+
}.should raise_error(ArgumentError, / array_param /)
|
132
|
+
end
|
133
|
+
|
134
|
+
it "should work with Proc validator" do
|
135
|
+
lambda {
|
136
|
+
get :show,
|
137
|
+
:id => 5,
|
138
|
+
:session => "secret_hash",
|
139
|
+
:proc_param => "asdgsag"
|
140
|
+
}.should raise_error(ArgumentError, / proc_param /)
|
141
|
+
|
180
142
|
get :show,
|
181
143
|
:id => 5,
|
182
144
|
:session => "secret_hash",
|
183
|
-
:proc_param => "
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
145
|
+
:proc_param => "param value"
|
146
|
+
assert_response :success
|
147
|
+
end
|
148
|
+
|
149
|
+
it "should work with Hash validator" do
|
150
|
+
post :create, :user => { :username => "root", :password => "12345", :membership => "standard" }
|
151
|
+
assert_response :success
|
152
|
+
|
153
|
+
a = Restapi[UsersController, :create]
|
154
|
+
param = a.params_ordered.select {|p| p.name == :user }
|
155
|
+
param.count.should == 1
|
156
|
+
param.first.validator.class.should eq(Restapi::Validator::HashValidator)
|
157
|
+
hash_params = param.first.validator.hash_params_ordered
|
158
|
+
hash_params.count.should == 3
|
159
|
+
hash_params[0].name == :username
|
160
|
+
hash_params[1].name == :password
|
161
|
+
hash_params[2].name == :membership
|
162
|
+
|
163
|
+
lambda {
|
164
|
+
post :create, :user => { :username => "root", :password => "12345", :membership => "____" }
|
165
|
+
}.should raise_error(ArgumentError, / membership /)
|
166
|
+
|
167
|
+
lambda {
|
168
|
+
post :create, :user => { :username => "root" }
|
169
|
+
}.should raise_error(ArgumentError, / password /)
|
170
|
+
|
171
|
+
post :create, :user => { :username => "root", :password => "pwd" }
|
172
|
+
assert_response :success
|
173
|
+
end
|
174
|
+
|
175
|
+
it "should support Hash validator without specifying keys" do
|
176
|
+
params = Restapi[UsersController, :create].to_json[:params]
|
177
|
+
params.should include(:name => "facts",
|
178
|
+
:full_name => "facts",
|
179
|
+
:validator => "Parameter has to be Hash.",
|
180
|
+
:description => "\n<p>Additional optional facts about the user</p>\n",
|
181
|
+
:required => false,
|
182
|
+
:allow_nil => true,
|
183
|
+
:expected_type => "hash")
|
184
|
+
end
|
185
|
+
|
186
|
+
it "should allow nil when allow_nil is set to true" do
|
187
|
+
post :create,
|
188
|
+
:user => {
|
189
|
+
:username => "root",
|
190
|
+
:password => "12345",
|
191
|
+
:membership => "standard",
|
192
|
+
},
|
193
|
+
:facts => nil
|
194
|
+
assert_response :success
|
195
|
+
end
|
196
|
+
|
191
197
|
end
|
192
|
-
|
193
198
|
end
|
194
199
|
|
195
|
-
describe "
|
196
|
-
|
197
|
-
it "should understand hash validator" do
|
198
|
-
post :create,
|
199
|
-
:user => {
|
200
|
-
:username => "root",
|
201
|
-
:password => "12345",
|
202
|
-
:membership => "standard"
|
203
|
-
}
|
204
|
-
assert_response :success
|
200
|
+
describe "method description" do
|
205
201
|
|
202
|
+
it "should contain basic info about method" do
|
206
203
|
a = Restapi[UsersController, :create]
|
207
204
|
a.apis.count.should == 1
|
208
205
|
api = a.apis.first
|
@@ -210,72 +207,90 @@ describe UsersController do
|
|
210
207
|
api.api_url.should eq("/api/users")
|
211
208
|
api.http_method.should eq("POST")
|
212
209
|
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
:password => "12345",
|
218
|
-
:membership => "____"
|
219
|
-
}
|
220
|
-
}.should raise_error(ArgumentError)
|
210
|
+
b = Restapi.get_method_description(UsersController, :show)
|
211
|
+
b.should eq(Restapi[UsersController, :show])
|
212
|
+
b.method.should eq(:show)
|
213
|
+
b.resource._id.should eq('users')
|
221
214
|
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
215
|
+
b.apis.count.should == 1
|
216
|
+
api = b.apis.first
|
217
|
+
api.short_description.should eq("Show user profile")
|
218
|
+
api.api_url.should eq("#{Restapi.configuration.api_base_url}/users/:id")
|
219
|
+
api.http_method.should eq("GET")
|
220
|
+
b.full_description.length.should be > 400
|
221
|
+
end
|
226
222
|
|
227
|
-
|
228
|
-
|
229
|
-
assert_response :success
|
223
|
+
it "should contain possible errors description" do
|
224
|
+
a = Restapi.get_method_description(UsersController, :show)
|
230
225
|
|
226
|
+
a.errors[0].code.should eq(401)
|
227
|
+
a.errors[0].description.should eq("Unauthorized")
|
228
|
+
a.errors[1].code.should eq(404)
|
229
|
+
a.errors[1].description.should eq("Not Found")
|
231
230
|
end
|
232
231
|
|
233
|
-
it "
|
234
|
-
Restapi
|
235
|
-
|
236
|
-
|
237
|
-
:required => false)
|
232
|
+
it "should contain all params description" do
|
233
|
+
a = Restapi.get_method_description(UsersController, :show)
|
234
|
+
a.params.count.should == 8
|
235
|
+
a.instance_variable_get('@params_ordered').count.should == 6
|
238
236
|
end
|
239
237
|
|
240
|
-
|
241
|
-
|
242
|
-
describe 'two_urls' do
|
243
|
-
|
244
|
-
it "should store all api method description" do
|
238
|
+
it "should contain all api method description" do
|
245
239
|
method_description = Restapi[UsersController, :two_urls]
|
246
240
|
method_description.class.should be(Restapi::MethodDescription)
|
247
241
|
method_description.apis.count.should == 2
|
248
|
-
|
249
|
-
|
250
|
-
a2 = apis.second
|
251
|
-
|
242
|
+
a1, a2 = method_description.apis
|
243
|
+
|
252
244
|
a1.short_description.should eq('Get company users')
|
253
245
|
a1.api_url.should eq('/api/company_users')
|
254
246
|
a1.http_method.should eq('GET')
|
247
|
+
|
255
248
|
a2.short_description.should eq('Get users working in given company')
|
256
249
|
a2.api_url.should eq('/api/company/:id/users')
|
257
250
|
a2.http_method.should eq('GET')
|
258
251
|
end
|
259
|
-
|
252
|
+
|
260
253
|
it "should be described by valid json" do
|
261
|
-
|
254
|
+
json = Restapi[UsersController, :two_urls].to_json
|
255
|
+
expected_hash = {
|
262
256
|
:errors => [],
|
263
|
-
:
|
257
|
+
:examples => [],
|
258
|
+
:doc_url => "#{Restapi.configuration.doc_base_url}/users/two_urls",
|
264
259
|
:full_description => '',
|
265
|
-
:params => [{:
|
266
|
-
:
|
267
|
-
:
|
268
|
-
{:required=>true,
|
260
|
+
:params => [{:full_name=>"oauth",
|
261
|
+
:required=>false,
|
262
|
+
:allow_nil => false,
|
269
263
|
:validator=>"Parameter has to be String.",
|
270
|
-
:description=>"\n<p>
|
271
|
-
:name=>"
|
272
|
-
|
273
|
-
|
274
|
-
:description=>"\n<p>
|
275
|
-
:
|
264
|
+
:description=>"\n<p>Authorization</p>\n",
|
265
|
+
:name=>"oauth",
|
266
|
+
:expected_type=>"string"},
|
267
|
+
{:validator=>"Has to be hash.",
|
268
|
+
:description=>"\n<p>Param description for all methods</p>\n",
|
269
|
+
:expected_type=>"hash",
|
270
|
+
:allow_nil=>false,
|
271
|
+
:name=>"resource_param",
|
272
|
+
:required=>false,
|
273
|
+
:full_name=>"resource_param",
|
274
|
+
:params=>
|
275
|
+
[{:required=>true,
|
276
|
+
:allow_nil => false,
|
277
|
+
:validator=>"Parameter has to be String.",
|
278
|
+
:description=>"\n<p>Username for login</p>\n",
|
279
|
+
:name=>"username", :full_name=>"resource_param[username]",
|
280
|
+
:expected_type=>"string"},
|
281
|
+
{:required=>true,
|
282
|
+
:allow_nil => false,
|
283
|
+
:validator=>"Parameter has to be String.",
|
284
|
+
:description=>"\n<p>Password for login</p>\n",
|
285
|
+
:name=>"password", :full_name=>"resource_param[password]",
|
286
|
+
:expected_type=>"string"}
|
287
|
+
]
|
288
|
+
},
|
276
289
|
{:required=>false, :validator=>"Parameter has to be Integer.",
|
290
|
+
:allow_nil => false,
|
277
291
|
:description=>"\n<p>Company ID</p>\n",
|
278
|
-
:name=>"id"
|
292
|
+
:name=>"id", :full_name=>"id",
|
293
|
+
:expected_type=>"numeric"},
|
279
294
|
],
|
280
295
|
:name => :two_urls,
|
281
296
|
:apis => [
|
@@ -290,8 +305,45 @@ describe UsersController do
|
|
290
305
|
}
|
291
306
|
]
|
292
307
|
}
|
293
|
-
|
294
|
-
|
308
|
+
|
309
|
+
compare_hashes json, expected_hash
|
310
|
+
end
|
311
|
+
|
312
|
+
end
|
313
|
+
|
314
|
+
describe "param description" do
|
315
|
+
|
316
|
+
it "should contain all specified information" do
|
317
|
+
a = Restapi.get_method_description(UsersController, :show)
|
318
|
+
|
319
|
+
param = a.params[:session]
|
320
|
+
param.required.should eq(true)
|
321
|
+
param.desc.should eq("\n<p>user is logged in</p>\n")
|
322
|
+
param.validator.class.should be(Restapi::Validator::TypeValidator)
|
323
|
+
param.validator.instance_variable_get("@type").should eq(String)
|
324
|
+
|
325
|
+
param = a.params[:id]
|
326
|
+
param.required.should eq(true)
|
327
|
+
param.desc.should eq("\n<p>user id</p>\n")
|
328
|
+
param.validator.class.should be(Restapi::Validator::IntegerValidator)
|
329
|
+
param.validator.instance_variable_get("@type").should eq(Integer)
|
330
|
+
|
331
|
+
param = a.params[:regexp_param]
|
332
|
+
param.desc.should eq("\n<p>regexp param</p>\n")
|
333
|
+
param.required.should eq(false)
|
334
|
+
param.validator.class.should be(Restapi::Validator::RegexpValidator)
|
335
|
+
param.validator.instance_variable_get("@regexp").should
|
336
|
+
eq(/^[0-9]* years/)
|
337
|
+
|
338
|
+
param = a.params[:array_param]
|
339
|
+
param.desc.should eq("\n<p>array validator</p>\n")
|
340
|
+
param.validator.class.should be(Restapi::Validator::ArrayValidator)
|
341
|
+
param.validator.instance_variable_get("@array").should
|
342
|
+
eq([100, "one", "two", 1, 2])
|
343
|
+
|
344
|
+
param = a.params[:proc_param]
|
345
|
+
param.desc.should eq("\n<p>proc validator</p>\n")
|
346
|
+
param.validator.class.should be(Restapi::Validator::ProcValidator)
|
295
347
|
end
|
296
348
|
|
297
349
|
end
|