restapi 0.0.3 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. data/Gemfile +2 -1
  2. data/Gemfile.lock +83 -64
  3. data/README.rdoc +375 -2
  4. data/app/controllers/restapi/restapis_controller.rb +21 -3
  5. data/app/helpers/restapi/restapis_helper.rb +31 -0
  6. data/app/public/restapi/javascripts/bundled/prettify.js +28 -0
  7. data/app/public/restapi/javascripts/restapi.js +4 -13
  8. data/app/public/restapi/stylesheets/bundled/prettify.css +30 -0
  9. data/app/views/layouts/restapi/restapi.html.erb +36 -0
  10. data/app/views/restapi/restapis/index.html.erb +35 -33
  11. data/app/views/restapi/restapis/method.html.erb +59 -0
  12. data/app/views/restapi/restapis/resource.html.erb +71 -0
  13. data/app/views/restapi/restapis/static.html.erb +103 -0
  14. data/lib/restapi.rb +0 -10
  15. data/lib/restapi/application.rb +25 -10
  16. data/lib/restapi/dsl_definition.rb +38 -32
  17. data/lib/restapi/markup.rb +12 -12
  18. data/lib/restapi/method_description.rb +10 -8
  19. data/lib/restapi/param_description.rb +23 -10
  20. data/lib/restapi/resource_description.rb +1 -1
  21. data/lib/restapi/restapi_module.rb +15 -0
  22. data/lib/restapi/validator.rb +37 -14
  23. data/lib/restapi/version.rb +1 -1
  24. data/lib/tasks/restapi.rake +157 -0
  25. data/restapi.gemspec +1 -1
  26. data/spec/controllers/restapis_controller_spec.rb +84 -0
  27. data/spec/controllers/users_controller_spec.rb +273 -221
  28. data/spec/dummy/app/controllers/twitter_example_controller.rb +209 -208
  29. data/spec/dummy/app/controllers/users_controller.rb +23 -33
  30. data/spec/dummy/config/initializers/restapi.rb +45 -23
  31. data/spec/dummy/config/routes.rb +12 -7
  32. metadata +26 -15
  33. data/app/public/restapi/javascripts/bundled/backbone.js +0 -1431
  34. data/app/public/restapi/javascripts/bundled/json2.js +0 -487
  35. data/app/public/restapi/javascripts/bundled/underscore.js +0 -999
  36. data/app/public/restapi/javascripts/jst.js +0 -127
  37. data/app/public/restapi/javascripts/routers/documentation_router.js +0 -52
  38. data/app/public/restapi/stylesheets/bundled/bootstrap-responsive.css +0 -686
  39. data/app/public/restapi/stylesheets/bundled/bootstrap.css +0 -3990
  40. data/spec/dummy/app/controllers/dogs_controller.rb +0 -20
  41. data/spec/dummy/app/helpers/application_helper.rb +0 -2
@@ -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
- self.subclasses.each do |validator_type|
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
- begin
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
- self.new(param_description, argument) if argument.is_a?(Class) && (argument != Hash || block.nil?)
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 #{@param_name} expecting to be #{@type.name}, got: #{@error_value.class.name}"
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 #{@param_name} expecting to match /#{@regexp.source}/, got '#{@error_value}'"
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 #{@param_name} has bad value (#{@error_value.inspect}). Expecting one of: #{@array.join(',')}."
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 #{@param_name} has bad value (\"#{@error_value}\"). #{@help}"
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
- "TODO"
205
+ "Has to be hash."
187
206
  end
188
207
 
189
208
  def description
190
- "TODO"
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
@@ -1,3 +1,3 @@
1
1
  module Restapi
2
- VERSION = '0.0.3'
2
+ VERSION = '0.0.4'
3
3
  end
@@ -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
@@ -18,5 +18,5 @@ Gem::Specification.new do |s|
18
18
 
19
19
  s.add_development_dependency "rspec-rails"
20
20
  s.add_development_dependency "rcov"
21
- #s.add_runtime_dependency "rest-client"
21
+ s.add_development_dependency "weary"
22
22
  end
@@ -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
- describe UsersController do
4
-
5
- describe "resource" do
6
- it "should be described" do
7
- a = Restapi.get_resource_description(UsersController)
8
-
9
- #puts Restapi.method_descriptions['users#show'].inspect
10
- a._short_description.should eq('Site members')
11
- a._methods.count.should == 4
12
- a._methods.should include("users#show")
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
- describe "GET show" do
17
+ describe UsersController do
30
18
 
31
- it "find a user with id" do
32
- get :show, :id => 5, :session => "secret_hash"
33
-
34
- assert_response :success
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 "throw ArgumentError if some required parameter missing" do
38
- lambda { get :show, :id => 5 }.should raise_error(ArgumentError)
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 "responds with status 401 if session is wrong" do
42
- get :show, :id => 5, :session => "bad_hash"
43
- assert_response 401
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
- it "should be annotated" do
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
- a.method.should eq(:show)
53
- a.resource._id.should eq('users')
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
- a.apis.count.should == 1
60
- a.apis.first.short_description.should eq("Show user profile")
61
- a.apis.first.api_url.should
62
- eq("#{Restapi.configuration.api_base_url}/users/:id")
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
- param = a.params[:session]
66
- param.required.should eq(true)
67
- param.desc.should eq("\n<p>user is logged in</p>\n")
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
- param = a.params[:id]
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
- param = a.params[:array_param]
91
- param.desc.should eq("\n<p>array validator</p>\n")
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
- param = a.params[:proc_param]
97
- param.desc.should eq("\n<p>proc validator</p>\n")
98
- param.validator.class.should be(Restapi::Validator::ProcValidator)
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
- it "should understand float validator" do
128
- get :show, :id => 5, :session => "secret_hash", :float_param => "234.34"
129
- assert_response :success
130
- get :show, :id => 5, :session => "secret_hash", :float_param => "234"
131
- assert_response :success
132
- end
133
-
134
- it "should understand regexp validator" do
135
- get :show,
136
- :id => 5,
137
- :session => "secret_hash",
138
- :regexp_param => "24 years"
139
- assert_response :success
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
- :array_param => "blabla"
168
- }.should raise_error(ArgumentError)
169
-
170
- lambda {
171
- get :show,
172
- :id => 5,
173
- :session => "secret_hash",
174
- :array_param => 3
175
- }.should raise_error(ArgumentError)
176
- end
177
-
178
- it "should validate with Proc validator" do
179
- lambda {
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 => "asdgsag"
184
- }.should raise_error(ArgumentError)
185
-
186
- get :show,
187
- :id => 5,
188
- :session => "secret_hash",
189
- :proc_param => "param value"
190
- assert_response :success
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 "POST create" do
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
- lambda {
214
- post :create,
215
- :user => {
216
- :username => "root",
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
- lambda {
223
- post :create,
224
- :user => { :username => "root" }
225
- }.should raise_error(ArgumentError)
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
- post :create,
228
- :user => { :username => "root", :password => "pwd" }
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 "it should support Hash validator without specifying keys" do
234
- Restapi[UsersController, :create].to_json[:params].should include(:name => "facts",
235
- :validator => "Parameter has to be Hash.",
236
- :description => "\n<p>Additional optional facts about the user</p>\n",
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
- end
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
- apis = method_description.apis
249
- a1 = apis.first
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
- json_hash = {
254
+ json = Restapi[UsersController, :two_urls].to_json
255
+ expected_hash = {
262
256
  :errors => [],
263
- :doc_url => "#{Restapi.configuration.doc_base_url}#users/two_urls",
257
+ :examples => [],
258
+ :doc_url => "#{Restapi.configuration.doc_base_url}/users/two_urls",
264
259
  :full_description => '',
265
- :params => [{:required=>false,
266
- :validator=>"Parameter has to be String.",
267
- :description=>"\n<p>Authorization</p>\n", :name=>"oauth"},
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>Username for login</p>\n",
271
- :name=>"resource_param[username]"},
272
- {:required=>true,
273
- :validator=>"Parameter has to be String.",
274
- :description=>"\n<p>Password for login</p>\n",
275
- :name=>"resource_param[password]"},
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
- Restapi[UsersController, :two_urls].to_json.should eq(json_hash)
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