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/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
|