diff --git a/.travis.yml b/.travis.yml index c0b78f0fe..5f3454244 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,12 +1,8 @@ language: ruby sudo: false rvm: - - 1.9.3 - - 2.0.0 - - 2.1.7 + - 2.2.2 - 2.2.3 + - 2.3.3 gemfile: - - Gemfile.rails32 - - Gemfile.rails40 - - Gemfile.rails41 - - Gemfile.rails42 + - Gemfile.rails50 diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e2058002..9f2718624 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,15 @@ Changelog =========== +v0.4.0 +------ +Support for Ruby 2.2.2+ and Rails 5.0+. +Breaking change with Ruby <2.2 and Rails <5.0 + +- Rails 5.0 compatibility +- Rails 5.0 testability +- Booleans interpreted strictly as strings per Rails 5 default content type "application/x-www-form-urlencoded" + v0.3.7 ------ diff --git a/Gemfile.rails32 b/Gemfile.rails32 deleted file mode 100644 index 808c331ef..000000000 --- a/Gemfile.rails32 +++ /dev/null @@ -1,6 +0,0 @@ -source "https://rubygems.org" - -gemspec - -gem 'rails', '~> 3.2.0' -gem 'test-unit', '~> 3.0' diff --git a/Gemfile.rails40 b/Gemfile.rails40 deleted file mode 100644 index 6bdf7a655..000000000 --- a/Gemfile.rails40 +++ /dev/null @@ -1,7 +0,0 @@ -source "https://rubygems.org" - -gemspec - -gem 'rails', '~> 4.0.0' -gem 'mime-types', '~> 2.99.3' -gem 'json', '~> 1.8' diff --git a/Gemfile.rails41 b/Gemfile.rails41 deleted file mode 100644 index 0a7a2f156..000000000 --- a/Gemfile.rails41 +++ /dev/null @@ -1,6 +0,0 @@ -source "https://rubygems.org" - -gemspec - -gem 'rails', '~> 4.1.0' -gem 'mime-types', '~> 2.99.3' \ No newline at end of file diff --git a/Gemfile.rails42 b/Gemfile.rails42 deleted file mode 100644 index e91917e36..000000000 --- a/Gemfile.rails42 +++ /dev/null @@ -1,11 +0,0 @@ -source "https://rubygems.org" - -gemspec - -gem 'rails', '~> 4.2.5' -gem 'mime-types', '~> 2.99.3' - -if Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2.1.0') - gem 'nokogiri', '~> 1.6.8' - gem 'rdoc', '~> 4.2.2' -end diff --git a/Gemfile.rails50 b/Gemfile.rails50 new file mode 100644 index 000000000..bef782498 --- /dev/null +++ b/Gemfile.rails50 @@ -0,0 +1,7 @@ +source "https://rubygems.org" + +gemspec + +gem 'rails', '~> 5.0' +gem 'mime-types', '~> 2.99.3' +gem 'rails-controller-testing' # Enables using assigns() in rspec diff --git a/README.rst b/README.rst index 4a46ce017..a745eab34 100644 --- a/README.rst +++ b/README.rst @@ -251,7 +251,7 @@ Example: param :session, String, :desc => "user is logged in", :required => true param :regexp_param, /^[0-9]* years/, :desc => "regexp param" param :array_param, [100, "one", "two", 1, 2], :desc => "array validator" - param :boolean_param, [true, false], :desc => "array validator with boolean" + param :boolean_param, ["true", "false"], :desc => "array validator with boolean" param :proc_param, lambda { |val| val == "param value" ? true : "The only good value is 'param value'." }, :desc => "proc validator" diff --git a/lib/apipie/application.rb b/lib/apipie/application.rb index e59044f14..516093c6c 100644 --- a/lib/apipie/application.rb +++ b/lib/apipie/application.rb @@ -54,14 +54,9 @@ def rails_routes(route_set = nil) # the app might be nested when using contraints, namespaces etc. # this method does in depth search for the route controller def route_app_controller(app, route, visited_apps = []) - visited_apps << app - if app.respond_to?(:controller) - return app.controller(route.defaults) - elsif app.respond_to?(:app) && !visited_apps.include?(app.app) - return route_app_controller(app.app, route, visited_apps) + if route.defaults[:controller] + (route.defaults[:controller].camelize + "Controller").constantize end - rescue ActionController::RoutingError - # some errors in the routes will not stop us here: just ignoring end def routes_for_action(controller, method, args) diff --git a/lib/apipie/param_description.rb b/lib/apipie/param_description.rb index 95e6392fd..c090c5e11 100644 --- a/lib/apipie/param_description.rb +++ b/lib/apipie/param_description.rb @@ -73,7 +73,7 @@ def from_concern? end def validate(value) - return true if @allow_nil && value.nil? + return true if @allow_nil && value.blank? # Rails 5 doesn't pass value nil return true if @allow_blank && value.blank? if (!@allow_nil && value.nil?) || !@validator.valid?(value) error = @validator.error diff --git a/lib/apipie/validator.rb b/lib/apipie/validator.rb index b4973a60d..922ee5198 100644 --- a/lib/apipie/validator.rb +++ b/lib/apipie/validator.rb @@ -302,6 +302,8 @@ def params_ordered end def validate(value) + # TODO: use value.to_h instead, and use strong parameters in controller(s). + value = value.to_unsafe_h if value.is_a? ActionController::Parameters return false if !value.is_a? Hash if @hash_params @hash_params.each do |k, p| diff --git a/lib/apipie/version.rb b/lib/apipie/version.rb index ecfeadba2..03dc6fb16 100644 --- a/lib/apipie/version.rb +++ b/lib/apipie/version.rb @@ -1,3 +1,3 @@ module Apipie - VERSION = '0.3.7' + VERSION = '0.4.0' end diff --git a/spec/controllers/apipies_controller_spec.rb b/spec/controllers/apipies_controller_spec.rb index 0bbfcb1e2..867bf0a8d 100644 --- a/spec/controllers/apipies_controller_spec.rb +++ b/spec/controllers/apipies_controller_spec.rb @@ -12,37 +12,37 @@ end it "succeeds on version details" do - get :index, :version => "2.0" + get :index, :params => { :version => "2.0" } assert_response :success end it "returns not_found on wrong version" do - get :index, :version => "wrong_version" + get :index, :params => { :version => "wrong_version" } assert_response :not_found end it "succeeds on resource details" do - get :index, :version => "2.0", :resource => "architectures" + get :index, :params => { :version => "2.0", :resource => "architectures" } assert_response :success end it "returns not_found on wrong resource" do - get :index, :version => "2.0", :resource => "wrong_resource" + get :index, :params => { :version => "2.0", :resource => "wrong_resource" } assert_response :not_found end it "succeeds on method details" do - get :index, :version => "2.0", :resource => "architectures", :method => "index" + get :index, :params => { :version => "2.0", :resource => "architectures", :method => "index" } assert_response :success end it "returns not_found on wrong method" do - get :index, :version => "2.0", :resource => "architectures", :method => "wrong_method" + get :index, :params => { :version => "2.0", :resource => "architectures", :method => "wrong_method" } assert_response :not_found end @@ -215,17 +215,17 @@ it "uses the file in cache dir instead of generating the content on runtime" do get :index expect(response.body).to eq("apidoc.html cache v1") - get :index, :version => 'v1' + get :index, :params => { :version => 'v1' } expect(response.body).to eq("apidoc.html cache v1") - get :index, :version => 'v2' + get :index, :params => { :version => 'v2' } expect(response.body).to eq("apidoc.html cache v2") - get :index, :version => 'v1', :format => "html" + get :index, :params => { :version => 'v1', :format => "html" } expect(response.body).to eq("apidoc.html cache v1") - get :index, :version => 'v1', :format => "json" + get :index, :params => { :version => 'v1', :format => "json" } expect(response.body).to eq("apidoc.json cache") - get :index, :version => 'v1', :format => "html", :resource => "resource" + get :index, :params => { :version => 'v1', :format => "html", :resource => "resource" } expect(response.body).to eq("resource.html cache") - get :index, :version => 'v1', :format => "html", :resource => "resource", :method => "method" + get :index, :params => { :version => 'v1', :format => "html", :resource => "resource", :method => "method" } expect(response.body).to eq("method.html cache") end diff --git a/spec/controllers/concerns_controller_spec.rb b/spec/controllers/concerns_controller_spec.rb index a80d5c5fd..02addc3bd 100644 --- a/spec/controllers/concerns_controller_spec.rb +++ b/spec/controllers/concerns_controller_spec.rb @@ -8,7 +8,7 @@ end it "should reply to valid request" do - get :show, :id => '5', :session => "secret_hash" + get :show, :params => { :id => '5', :session => "secret_hash" } assert_response :success end diff --git a/spec/controllers/users_controller_spec.rb b/spec/controllers/users_controller_spec.rb index 9e5e9c5db..a0d1aad90 100644 --- a/spec/controllers/users_controller_spec.rb +++ b/spec/controllers/users_controller_spec.rb @@ -66,7 +66,7 @@ def compare_hashes(h1, h2) end it "should reply to valid request" do - get :show, :id => '5', :session => "secret_hash" + get :show, :params => { :id => '5', :session => "secret_hash" } assert_response :success end @@ -92,7 +92,7 @@ def reload_controllers end it "should reply to valid request" do - expect { get :show, :id => 5, :session => "secret_hash" }.not_to raise_error + expect { get :show, :params => { :id => 5, :session => "secret_hash" } }.not_to raise_error assert_response :success end @@ -101,8 +101,8 @@ def reload_controllers end it "should pass if required parameter has wrong type" do - expect { get :show, :id => 5, :session => "secret_hash" }.not_to raise_error - expect { get :show, :id => "ten", :session => "secret_hash" }.not_to raise_error + expect { get :show, :params => { :id => 5, :session => "secret_hash" } }.not_to raise_error + expect { get :show, :params => { :id => "ten", :session => "secret_hash"} }.not_to raise_error end end @@ -115,12 +115,12 @@ def reload_controllers end it "should reply to valid request" do - expect { get :show, :id => 5, :session => "secret_hash" }.not_to raise_error + expect { get :show, :params => { :id => 5, :session => "secret_hash" } }.not_to raise_error assert_response :success end it "should fail if extra parameter is passed in" do - expect { get :show, :id => 5, :session => "secret_hash", :badparam => 'badfoo' }.to raise_error(Apipie::UnknownParam, /\bbadparam\b/) + expect { get :show, :params => { :id => 5, :session => "secret_hash", :badparam => 'badfoo' } }.to raise_error(Apipie::UnknownParam, /\bbadparam\b/) end end @@ -132,13 +132,13 @@ def reload_controllers end it "should reply to valid request" do - get :show, :id => '5', :session => "secret_hash" + get :show, :params => { :id => '5', :session => "secret_hash" } assert_response :success end it "should work with nil value for a required hash param" do expect { - get :show, :id => '5', :session => "secret_hash", :hash_param => {:dummy_hash => nil} + get :show, :params => { :id => '5', :session => "secret_hash", :hash_param => {:dummy_hash => nil} } }.to raise_error(Apipie::ParamInvalid, /dummy_hash/) assert_response :success end @@ -150,63 +150,74 @@ def reload_controllers it "should work with custom Type validator" do expect { get :show, - :id => "not a number", - :session => "secret_hash" + :params => { :id => "not a number", :session => "secret_hash" } }.to raise_error(Apipie::ParamError, /id/) # old-style error rather than ParamInvalid end it "should work with Regexp validator" do get :show, - :id => 5, - :session => "secret_hash", - :regexp_param => "24 years" + :params => { + :id => 5, + :session => "secret_hash", + :regexp_param => "24 years" + } assert_response :success expect { get :show, - :id => 5, - :session => "secret_hash", - :regexp_param => "ten years" + :params => { + :id => 5, + :session => "secret_hash", + :regexp_param => "ten years" + } }.to raise_error(Apipie::ParamInvalid, /regexp_param/) end it "should work with Array validator" do - get :show, :id => 5, :session => "secret_hash", :array_param => "one" + get :show, :params => { :id => 5, :session => "secret_hash", :array_param => "one" } assert_response :success - get :show, :id => 5, :session => "secret_hash", :array_param => "two" + get :show, :params => { :id => 5, :session => "secret_hash", :array_param => "two" } assert_response :success - get :show, :id => 5, :session => "secret_hash", :array_param => '1' + get :show, :params => { :id => 5, :session => "secret_hash", :array_param => '1' } assert_response :success - get :show, :id => 5, :session => "secret_hash", :boolean_param => false + get :show, :params => { :id => 5, :session => "secret_hash", :boolean_param => false } assert_response :success expect { get :show, - :id => 5, - :session => "secret_hash", - :array_param => "blabla" + :params => { + :id => 5, + :session => "secret_hash", + :array_param => "blabla" + } }.to raise_error(Apipie::ParamInvalid, /array_param/) expect { get :show, - :id => 5, - :session => "secret_hash", - :array_param => 3 + :params => { + :id => 5, + :session => "secret_hash", + :array_param => 3 + } }.to raise_error(Apipie::ParamInvalid, /array_param/) end it "should work with Proc validator" do expect { get :show, - :id => 5, - :session => "secret_hash", - :proc_param => "asdgsag" + :params => { + :id => 5, + :session => "secret_hash", + :proc_param => "asdgsag" + } }.to raise_error(Apipie::ParamInvalid, /proc_param/) get :show, - :id => 5, - :session => "secret_hash", - :proc_param => "param value" + :params => { + :id => 5, + :session => "secret_hash", + :proc_param => "param value" + } assert_response :success end @@ -320,7 +331,7 @@ def reload_controllers } ] } - }.to raise_error(Apipie::ParamInvalid) + }.to raise_error # TODO (Rails 5): raise_error(Apipie::ParamInvalid) end end it "should work with empty array" do @@ -714,7 +725,7 @@ class IgnoredController < ApplicationController; end it "process correctly the parameters" do post :create, {:user => {:name => 'dummy', :pass => 'dummy', :membership => 'standard'}, :facts => nil} - expect(assigns(:api_params).with_indifferent_access).to eq({:user => {:name=>"dummy", :pass=>"dummy", :membership=>"standard"}, :facts => nil}.with_indifferent_access) + expect(assigns(:api_params).with_indifferent_access).to eq({:user => {:name=>"dummy", :pass=>"dummy", :membership=>"standard"}, :facts => ""}.with_indifferent_access) end it "ignore not described parameters" do diff --git a/spec/dummy/app/controllers/users_controller.rb b/spec/dummy/app/controllers/users_controller.rb index 7b70cea32..772c04872 100644 --- a/spec/dummy/app/controllers/users_controller.rb +++ b/spec/dummy/app/controllers/users_controller.rb @@ -178,7 +178,7 @@ class UsersController < ApplicationController param :regexp_param, /^[0-9]* years/, :desc => "regexp param" param :regexp2, /\b[A-Z0-9._%+-=]+@[A-Z0-9.-]+.[A-Z]{2,}\b/i, :desc => "email regexp" param :array_param, ["100", "one", "two", "1", "2"], :desc => "array validator" - param :boolean_param, [true, false], :desc => "array validator with boolean" + param :boolean_param, ["true", "false"], :desc => "array validator with boolean" param :proc_param, lambda { |val| val == "param value" ? true : "The only good value is 'param value'." }, :desc => "proc validator" diff --git a/spec/dummy/config/environments/development.rb b/spec/dummy/config/environments/development.rb index 4a797a03c..0be8bfa65 100644 --- a/spec/dummy/config/environments/development.rb +++ b/spec/dummy/config/environments/development.rb @@ -21,5 +21,8 @@ # Only use best-standards-support built into browsers config.action_dispatch.best_standards_support = :builtin + + # Do not eager load code on boot. (Rails 5) + config.eager_load = false end diff --git a/spec/dummy/config/environments/production.rb b/spec/dummy/config/environments/production.rb index cbdb27757..1ac38aea8 100644 --- a/spec/dummy/config/environments/production.rb +++ b/spec/dummy/config/environments/production.rb @@ -46,4 +46,7 @@ # Send deprecation notices to registered listeners config.active_support.deprecation = :notify + + # Eager load code on boot (Rails 5) + config.eager_load = true end diff --git a/spec/dummy/config/environments/test.rb b/spec/dummy/config/environments/test.rb index 768d8f044..5d38a3157 100644 --- a/spec/dummy/config/environments/test.rb +++ b/spec/dummy/config/environments/test.rb @@ -32,4 +32,7 @@ # Print deprecation notices to the stderr config.active_support.deprecation = :stderr + + # Do not eager load code on boot. (Rails 5) + config.eager_load = false end diff --git a/spec/dummy/config/routes.rb b/spec/dummy/config/routes.rb index 2d3d48b01..af7e14f64 100644 --- a/spec/dummy/config/routes.rb +++ b/spec/dummy/config/routes.rb @@ -9,9 +9,7 @@ end end resources :concerns, :only => [:index, :show] - namespace :files do - get '/*file_path', to: :download, format: false - end + get '/*file_path', to: "files#download", format: false resources :twitter_example do collection do get :lookup