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