diff --git a/Gemfile b/Gemfile --- a/Gemfile +++ b/Gemfile @@ -41,3 +41,7 @@ gem "rdiscount", :require => "rdiscount" gem "test-unit" gem 'will_paginate', '~> 3.0.0' + +group :test, :development do + gem "rspec-rails", "~> 2.0" +end diff --git a/Gemfile.lock b/Gemfile.lock --- a/Gemfile.lock +++ b/Gemfile.lock @@ -37,6 +37,7 @@ coffee-script-source execjs coffee-script-source (1.3.3) + diff-lcs (1.1.3) erubis (2.7.0) execjs (1.4.0) multi_json (~> 1.0) @@ -81,6 +82,19 @@ rdiscount (1.6.8) rdoc (3.12) json (~> 1.4) + rspec (2.11.0) + rspec-core (~> 2.11.0) + rspec-expectations (~> 2.11.0) + rspec-mocks (~> 2.11.0) + rspec-core (2.11.1) + rspec-expectations (2.11.3) + diff-lcs (~> 1.1.3) + rspec-mocks (2.11.3) + rspec-rails (2.11.0) + actionpack (>= 3.0) + activesupport (>= 3.0) + railties (>= 3.0) + rspec (~> 2.11.0) sass (3.2.1) sass-rails (3.2.5) railties (~> 3.2.0) @@ -113,6 +127,7 @@ prototype-rails rails (= 3.2.8) rdiscount + rspec-rails (~> 2.0) sass-rails (~> 3.2.3) test-unit tmail diff --git a/app/models/contest.rb b/app/models/contest.rb --- a/app/models/contest.rb +++ b/app/models/contest.rb @@ -3,6 +3,6 @@ has_and_belongs_to_many :users has_and_belongs_to_many :problems - named_scope :enabled, :conditions => {:enabled => true} + scope :enabled, :conditions => {:enabled => true} end diff --git a/app/models/problem.rb b/app/models/problem.rb --- a/app/models/problem.rb +++ b/app/models/problem.rb @@ -8,7 +8,7 @@ validates_format_of :name, :with => /^\w+$/ validates_presence_of :full_name - named_scope :available, :conditions => {:available => true} + scope :available, :conditions => {:available => true} DEFAULT_TIME_LIMIT = 1 DEFAULT_MEMORY_LIMIT = 32 diff --git a/app/models/user.rb b/app/models/user.rb --- a/app/models/user.rb +++ b/app/models/user.rb @@ -23,7 +23,7 @@ has_and_belongs_to_many :contests, :uniq => true, :order => 'name' - named_scope :activated_users, :conditions => {:activated => true} + scope :activated_users, :conditions => {:activated => true} validates_presence_of :login validates_uniqueness_of :login diff --git a/app/views/announcements/edit.html.erb b/app/views/announcements/edit.html.erb --- a/app/views/announcements/edit.html.erb +++ b/app/views/announcements/edit.html.erb @@ -2,7 +2,7 @@ <%= error_messages_for :announcement %> -<% form_for(@announcement) do |f| %> +<%= form_for(@announcement) do |f| %>

Title
<%= f.text_field :title %> diff --git a/app/views/announcements/new.html.erb b/app/views/announcements/new.html.erb --- a/app/views/announcements/new.html.erb +++ b/app/views/announcements/new.html.erb @@ -2,7 +2,7 @@ <%= error_messages_for :announcement %> -<% form_for(@announcement) do |f| %> +<%= form_for(@announcement) do |f| %>

Title
<%= f.text_field :title %> diff --git a/app/views/contests/edit.html.erb b/app/views/contests/edit.html.erb --- a/app/views/contests/edit.html.erb +++ b/app/views/contests/edit.html.erb @@ -1,6 +1,6 @@

Editing contest

-<% form_for(@contest) do |f| %> +<%= form_for(@contest) do |f| %> <%= f.error_messages %> diff --git a/app/views/contests/new.html.erb b/app/views/contests/new.html.erb --- a/app/views/contests/new.html.erb +++ b/app/views/contests/new.html.erb @@ -1,6 +1,6 @@

New contest

-<% form_for(@contest) do |f| %> +<%= form_for(@contest) do |f| %> <%= f.error_messages %>

diff --git a/app/views/main/_login_box.html.haml b/app/views/main/_login_box.html.haml --- a/app/views/main/_login_box.html.haml +++ b/app/views/main/_login_box.html.haml @@ -12,7 +12,7 @@ %hr/ %div{ :style => "border: solid 1px gray; padding: 4px; background: #eeeeff;"} - - form_tag :controller => 'login', :action => 'login' do + = form_tag :controller => 'login', :action => 'login' do %table %tr %td{:align => "right"} diff --git a/app/views/main/_submission_box.html.erb b/app/views/main/_submission_box.html.erb --- a/app/views/main/_submission_box.html.erb +++ b/app/views/main/_submission_box.html.erb @@ -1,4 +1,4 @@ -<% form_tag({:action => 'submit'}, :multipart => true) do %> +<%= form_tag({:action => 'submit'}, :multipart => true) do %> Problem: <%= select 'submission', 'problem_id', [[(t 'main.specified_in_header'),'-1']] + @problems.collect {|p| [p.full_name, p.id]}, diff --git a/app/views/problems/edit.rhtml b/app/views/problems/edit.rhtml --- a/app/views/problems/edit.rhtml +++ b/app/views/problems/edit.rhtml @@ -1,6 +1,6 @@

Editing problem

-<% form_tag :action => 'update', :id => @problem do %> +<%= form_tag :action => 'update', :id => @problem do %> <%= render :partial => 'form' %> <%= submit_tag 'Edit' %> <% end %> diff --git a/app/views/problems/list.rhtml b/app/views/problems/list.rhtml --- a/app/views/problems/list.rhtml +++ b/app/views/problems/list.rhtml @@ -14,7 +14,7 @@

- <% form_tag :action => 'quick_create' do %> + <%= form_tag :action => 'quick_create' do %> Quick New: <%= text_field 'problem', 'name' %> | diff --git a/app/views/problems/new.rhtml b/app/views/problems/new.rhtml --- a/app/views/problems/new.rhtml +++ b/app/views/problems/new.rhtml @@ -1,6 +1,6 @@

New problem

-<% form_tag :action => 'create' do %> +<%= form_tag :action => 'create' do %> <%= render :partial => 'form' %> <%= submit_tag "Create" %> <% end %> diff --git a/app/views/sites/edit.html.erb b/app/views/sites/edit.html.erb --- a/app/views/sites/edit.html.erb +++ b/app/views/sites/edit.html.erb @@ -2,7 +2,7 @@ <%= error_messages_for :site %> -<% form_for(@site) do |f| %> +<%= form_for(@site) do |f| %>

Name
<%= f.text_field :name %> diff --git a/app/views/sites/new.html.erb b/app/views/sites/new.html.erb --- a/app/views/sites/new.html.erb +++ b/app/views/sites/new.html.erb @@ -2,7 +2,7 @@ <%= error_messages_for :site %> -<% form_for(@site) do |f| %> +<%= form_for(@site) do |f| %>

Name
<%= f.text_field :name %> diff --git a/app/views/user_admin/contests.html.erb b/app/views/user_admin/contests.html.erb --- a/app/views/user_admin/contests.html.erb +++ b/app/views/user_admin/contests.html.erb @@ -15,7 +15,7 @@ <%= link_to "[no contest]", :action => 'contests', :id => 'none' %> <% end %>
- <% form_tag :action => 'assign_from_list' do %> + <%= form_tag :action => 'assign_from_list' do %> <%= hidden_field_tag 'users_contest_id', (@contest ? @contest.id : 'none') %> Assign all to <%= select("new_contest","id",Contest.all.collect {|c| [c.title, c.id]}) %> diff --git a/app/views/user_admin/edit.rhtml b/app/views/user_admin/edit.rhtml --- a/app/views/user_admin/edit.rhtml +++ b/app/views/user_admin/edit.rhtml @@ -1,6 +1,6 @@

Editing user

-<% form_tag :action => 'update', :id => @user do %> +<%= form_tag :action => 'update', :id => @user do %> <%= render :partial => 'form' %> <%= submit_tag 'Edit' %> <% end %> diff --git a/app/views/user_admin/list.rhtml b/app/views/user_admin/list.rhtml --- a/app/views/user_admin/list.rhtml +++ b/app/views/user_admin/list.rhtml @@ -2,7 +2,7 @@
Quick add - <% form_tag :action => 'create' do %> + <%= form_tag :action => 'create' do %>
@@ -23,7 +23,7 @@ <% end %>
Import from site management - <% form_tag({:action => 'import'}, :multipart => true) do %> + <%= form_tag({:action => 'import'}, :multipart => true) do %> File: <%= file_field_tag 'file' %> <%= submit_tag 'Import' %> <% end %>
diff --git a/app/views/user_admin/new.rhtml b/app/views/user_admin/new.rhtml --- a/app/views/user_admin/new.rhtml +++ b/app/views/user_admin/new.rhtml @@ -1,6 +1,6 @@

New user

-<% form_tag :action => 'create' do %> +<%= form_tag :action => 'create' do %> <%= render :partial => 'form' %> <%= submit_tag "Create" %> <% end %> diff --git a/app/views/user_admin/new_list.rhtml b/app/views/user_admin/new_list.rhtml --- a/app/views/user_admin/new_list.rhtml +++ b/app/views/user_admin/new_list.rhtml @@ -1,6 +1,6 @@

Adding list of users

-<% form_tag :action => 'create_from_list' do %> +<%= form_tag :action => 'create_from_list' do %> <%= submit_tag 'create users' %>
List of user information in this format: user_id,name(,passwd(,alias))
Note that passwd and alias is optional.
diff --git a/lib/tasks/rspec.rake b/lib/tasks/rspec.rake deleted file mode 100644 --- a/lib/tasks/rspec.rake +++ /dev/null @@ -1,144 +0,0 @@ -gem 'test-unit' if RUBY_VERSION.to_f >= 1.9 -rspec_gem_dir = nil -Dir["#{Rails.root}/vendor/gems/*"].each do |subdir| - rspec_gem_dir = subdir if subdir.gsub("#{Rails.root}/vendor/gems/","") =~ /^(\w+-)?rspec-(\d+)/ && File.exist?("#{subdir}/lib/spec/rake/spectask.rb") -end -rspec_plugin_dir = File.expand_path(File.dirname(__FILE__) + '/../../vendor/plugins/rspec') - -if rspec_gem_dir && (test ?d, rspec_plugin_dir) - raise "\n#{'*'*50}\nYou have rspec installed in both vendor/gems and vendor/plugins\nPlease pick one and dispose of the other.\n#{'*'*50}\n\n" -end - -if rspec_gem_dir - $LOAD_PATH.unshift("#{rspec_gem_dir}/lib") -elsif File.exist?(rspec_plugin_dir) - $LOAD_PATH.unshift("#{rspec_plugin_dir}/lib") -end - -# Don't load rspec if running "rake gems:*" -unless ARGV.any? {|a| a =~ /^gems/} - -begin - require 'spec/rake/spectask' -rescue MissingSourceFile - module Spec - module Rake - class SpecTask - def initialize(name) - task name do - # if rspec-rails is a configured gem, this will output helpful material and exit ... - require File.expand_path(File.join(File.dirname(__FILE__),"..","..","config","environment")) - - # ... otherwise, do this: - raise <<-MSG - -#{"*" * 80} -* You are trying to run an rspec rake task defined in -* #{__FILE__}, -* but rspec can not be found in vendor/gems, vendor/plugins or system gems. -#{"*" * 80} -MSG - end - end - end - end - end -end - -Rake.application.instance_variable_get('@tasks').delete('default') - -spec_prereq = File.exist?(File.join(Rails.root, 'config', 'database.yml')) ? "db:test:prepare" : :noop -task :noop do -end - -task :default => :spec -task :stats => "spec:statsetup" - -desc "Run all specs in spec directory (excluding plugin specs)" -Spec::Rake::SpecTask.new(:spec => spec_prereq) do |t| - t.spec_opts = ['--options', "\"#{Rails.root}/spec/spec.opts\""] - t.spec_files = FileList['spec/**/*_spec.rb'] -end - -namespace :spec do - desc "Run all specs in spec directory with RCov (excluding plugin specs)" - Spec::Rake::SpecTask.new(:rcov) do |t| - t.spec_opts = ['--options', "\"#{Rails.root}/spec/spec.opts\""] - t.spec_files = FileList['spec/**/*_spec.rb'] - t.rcov = true - t.rcov_opts = lambda do - IO.readlines("#{Rails.root}/spec/rcov.opts").map {|l| l.chomp.split " "}.flatten - end - end - - desc "Print Specdoc for all specs (excluding plugin specs)" - Spec::Rake::SpecTask.new(:doc) do |t| - t.spec_opts = ["--format", "specdoc", "--dry-run"] - t.spec_files = FileList['spec/**/*_spec.rb'] - end - - desc "Print Specdoc for all plugin examples" - Spec::Rake::SpecTask.new(:plugin_doc) do |t| - t.spec_opts = ["--format", "specdoc", "--dry-run"] - t.spec_files = FileList['vendor/plugins/**/spec/**/*_spec.rb'].exclude('vendor/plugins/rspec/*') - end - - [:models, :controllers, :views, :helpers, :lib, :integration].each do |sub| - desc "Run the code examples in spec/#{sub}" - Spec::Rake::SpecTask.new(sub => spec_prereq) do |t| - t.spec_opts = ['--options', "\"#{Rails.root}/spec/spec.opts\""] - t.spec_files = FileList["spec/#{sub}/**/*_spec.rb"] - end - end - - desc "Run the code examples in vendor/plugins (except RSpec's own)" - Spec::Rake::SpecTask.new(:plugins => spec_prereq) do |t| - t.spec_opts = ['--options', "\"#{Rails.root}/spec/spec.opts\""] - t.spec_files = FileList['vendor/plugins/**/spec/**/*_spec.rb'].exclude('vendor/plugins/rspec/*').exclude("vendor/plugins/rspec-rails/*") - end - - namespace :plugins do - desc "Runs the examples for rspec_on_rails" - Spec::Rake::SpecTask.new(:rspec_on_rails) do |t| - t.spec_opts = ['--options', "\"#{Rails.root}/spec/spec.opts\""] - t.spec_files = FileList['vendor/plugins/rspec-rails/spec/**/*_spec.rb'] - end - end - - # Setup specs for stats - task :statsetup do - require 'code_statistics' - ::STATS_DIRECTORIES << %w(Model\ specs spec/models) if File.exist?('spec/models') - ::STATS_DIRECTORIES << %w(View\ specs spec/views) if File.exist?('spec/views') - ::STATS_DIRECTORIES << %w(Controller\ specs spec/controllers) if File.exist?('spec/controllers') - ::STATS_DIRECTORIES << %w(Helper\ specs spec/helpers) if File.exist?('spec/helpers') - ::STATS_DIRECTORIES << %w(Library\ specs spec/lib) if File.exist?('spec/lib') - ::STATS_DIRECTORIES << %w(Routing\ specs spec/routing) if File.exist?('spec/routing') - ::STATS_DIRECTORIES << %w(Integration\ specs spec/integration) if File.exist?('spec/integration') - ::CodeStatistics::TEST_TYPES << "Model specs" if File.exist?('spec/models') - ::CodeStatistics::TEST_TYPES << "View specs" if File.exist?('spec/views') - ::CodeStatistics::TEST_TYPES << "Controller specs" if File.exist?('spec/controllers') - ::CodeStatistics::TEST_TYPES << "Helper specs" if File.exist?('spec/helpers') - ::CodeStatistics::TEST_TYPES << "Library specs" if File.exist?('spec/lib') - ::CodeStatistics::TEST_TYPES << "Routing specs" if File.exist?('spec/routing') - ::CodeStatistics::TEST_TYPES << "Integration specs" if File.exist?('spec/integration') - end - - namespace :db do - namespace :fixtures do - desc "Load fixtures (from spec/fixtures) into the current environment's database. Load specific fixtures using FIXTURES=x,y. Load from subdirectory in test/fixtures using FIXTURES_DIR=z." - task :load => :environment do - ActiveRecord::Base.establish_connection(Rails.env) - base_dir = File.join(Rails.root, 'spec', 'fixtures') - fixtures_dir = ENV['FIXTURES_DIR'] ? File.join(base_dir, ENV['FIXTURES_DIR']) : base_dir - - require 'active_record/fixtures' - (ENV['FIXTURES'] ? ENV['FIXTURES'].split(/,/).map {|f| File.join(fixtures_dir, f) } : Dir.glob(File.join(fixtures_dir, '*.{yml,csv}'))).each do |fixture_file| - Fixtures.create_fixtures(File.dirname(fixture_file), File.basename(fixture_file, '.*')) - end - end - end - end -end - -end diff --git a/log/.gitkeep b/log/.gitkeep new file mode 100644 diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,54 +1,38 @@ -# This file is copied to ~/spec when you run 'ruby script/generate rspec' -# from the project root directory. +# This file is copied to spec/ when you run 'rails generate rspec:install' ENV["RAILS_ENV"] ||= 'test' -require File.expand_path(File.join(File.dirname(__FILE__),'..','config','environment')) -require 'spec/autorun' -require 'spec/rails' - -# Uncomment the next line to use webrat's matchers -#require 'webrat/integrations/rspec-rails' - -# Requires supporting files with custom matchers and macros, etc, -# in ./support/ and its subdirectories. -Dir[File.expand_path(File.join(File.dirname(__FILE__),'support','**','*.rb'))].each {|f| require f} - -Spec::Runner.configure do |config| - # If you're not using ActiveRecord you should remove these - # lines, delete config/database.yml and disable :active_record - # in your config/boot.rb - config.use_transactional_fixtures = true - config.use_instantiated_fixtures = false - config.fixture_path = RAILS_ROOT + '/test/fixtures/' +require File.expand_path("../../config/environment", __FILE__) +require 'rspec/rails' +require 'rspec/autorun' - # == Fixtures - # - # You can declare fixtures for each example_group like this: - # describe "...." do - # fixtures :table_a, :table_b - # - # Alternatively, if you prefer to declare them only once, you can - # do so right here. Just uncomment the next line and replace the fixture - # names with your fixtures. - # - # config.global_fixtures = :table_a, :table_b +# Requires supporting ruby files with custom matchers and macros, etc, +# in spec/support/ and its subdirectories. +Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f} + +RSpec.configure do |config| + # ## Mock Framework # - # If you declare global fixtures, be aware that they will be declared - # for all of your examples, even those that don't use them. - # - # You can also declare which fixtures to use (for example fixtures for test/fixtures): - # - # config.fixture_path = RAILS_ROOT + '/spec/fixtures/' - # - # == Mock Framework - # - # RSpec uses it's own mocking framework by default. If you prefer to - # use mocha, flexmock or RR, uncomment the appropriate line: + # If you prefer to use mocha, flexmock or RR, uncomment the appropriate line: # # config.mock_with :mocha # config.mock_with :flexmock # config.mock_with :rr - # - # == Notes - # - # For more information take a look at Spec::Runner::Configuration and Spec::Runner + + # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures + config.fixture_path = "#{::Rails.root}/spec/fixtures" + + # If you're not using ActiveRecord, or you'd prefer not to run each of your + # examples within a transaction, remove the following line or assign false + # instead of true. + config.use_transactional_fixtures = true + + # If true, the base class of anonymous controllers will be inferred + # automatically. This will be the default behavior in future versions of + # rspec-rails. + config.infer_base_class_for_anonymous_controllers = false + + # Run specs in random order to surface order dependencies. If you find an + # order dependency and want to debug it, you can fix the order by providing + # the seed, which is printed after each run. + # --seed 1234 + config.order = "random" end diff --git a/vendor/plugins/verification/Gemfile b/vendor/plugins/verification/Gemfile new file mode 100644 --- /dev/null +++ b/vendor/plugins/verification/Gemfile @@ -0,0 +1,3 @@ +source "https://rubygems.org" + +gemspec diff --git a/vendor/plugins/verification/MIT-LICENSE b/vendor/plugins/verification/MIT-LICENSE new file mode 100644 --- /dev/null +++ b/vendor/plugins/verification/MIT-LICENSE @@ -0,0 +1,20 @@ +Copyright (c) 2010 David Heinemeier Hansson + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/plugins/verification/README b/vendor/plugins/verification/README new file mode 100644 --- /dev/null +++ b/vendor/plugins/verification/README @@ -0,0 +1,34 @@ +This module provides a class-level method for specifying that certain +actions are guarded against being called without certain prerequisites +being met. This is essentially a special kind of before_filter. + +An action may be guarded against being invoked without certain request +parameters being set, or without certain session values existing. + +When a verification is violated, values may be inserted into the flash, and +a specified redirection is triggered. If no specific action is configured, +verification failures will by default result in a 400 Bad Request response. + +Usage: + + class GlobalController < ActionController::Base + # Prevent the #update_settings action from being invoked unless + # the 'admin_privileges' request parameter exists. The + # settings action will be redirected to in current controller + # if verification fails. + verify :params => "admin_privileges", :only => :update_post, + :redirect_to => { :action => "settings" } + + # Disallow a post from being updated if there was no information + # submitted with the post, and if there is no active post in the + # session, and if there is no "note" key in the flash. The route + # named category_url will be redirected to if verification fails. + + verify :params => "post", :session => "post", "flash" => "note", + :only => :update_post, + :add_flash => { "alert" => "Failed to create your message" }, + :redirect_to => :category_url + +Note that these prerequisites are not business rules. They do not examine +the content of the session or the parameters. That level of validation should +be encapsulated by your domain model or helper methods in the controller. \ No newline at end of file diff --git a/vendor/plugins/verification/Rakefile b/vendor/plugins/verification/Rakefile new file mode 100644 --- /dev/null +++ b/vendor/plugins/verification/Rakefile @@ -0,0 +1,22 @@ +require 'rake' +require 'rake/testtask' +require 'rdoc/task' + +desc 'Default: run unit tests.' +task :default => :test + +desc 'Test the verification plugin.' +Rake::TestTask.new(:test) do |t| + t.libs << 'lib' + t.libs << 'test' + t.pattern = 'test/**/*_test.rb' +end + +desc 'Generate documentation for the verification plugin.' +Rake::RDocTask.new(:rdoc) do |rdoc| + rdoc.rdoc_dir = 'rdoc' + rdoc.title = 'Verification' + rdoc.options << '--line-numbers' << '--inline-source' + rdoc.rdoc_files.include('README') + rdoc.rdoc_files.include('lib/**/*.rb') +end diff --git a/vendor/plugins/verification/init.rb b/vendor/plugins/verification/init.rb new file mode 100644 --- /dev/null +++ b/vendor/plugins/verification/init.rb @@ -0,0 +1,3 @@ +# Include hook code here + +require 'action_controller/verification' \ No newline at end of file diff --git a/vendor/plugins/verification/lib/action_controller/verification.rb b/vendor/plugins/verification/lib/action_controller/verification.rb new file mode 100644 --- /dev/null +++ b/vendor/plugins/verification/lib/action_controller/verification.rb @@ -0,0 +1,132 @@ +module ActionController #:nodoc: + module Verification #:nodoc: + extend ActiveSupport::Concern + + include AbstractController::Callbacks, Flash, Rendering + + # This module provides a class-level method for specifying that certain + # actions are guarded against being called without certain prerequisites + # being met. This is essentially a special kind of before_filter. + # + # An action may be guarded against being invoked without certain request + # parameters being set, or without certain session values existing. + # + # When a verification is violated, values may be inserted into the flash, and + # a specified redirection is triggered. If no specific action is configured, + # verification failures will by default result in a 400 Bad Request response. + # + # Usage: + # + # class GlobalController < ActionController::Base + # # Prevent the #update_settings action from being invoked unless + # # the 'admin_privileges' request parameter exists. The + # # settings action will be redirected to in current controller + # # if verification fails. + # verify :params => "admin_privileges", :only => :update_post, + # :redirect_to => { :action => "settings" } + # + # # Disallow a post from being updated if there was no information + # # submitted with the post, and if there is no active post in the + # # session, and if there is no "note" key in the flash. The route + # # named category_url will be redirected to if verification fails. + # + # verify :params => "post", :session => "post", "flash" => "note", + # :only => :update_post, + # :add_flash => { "alert" => "Failed to create your message" }, + # :redirect_to => :category_url + # + # Note that these prerequisites are not business rules. They do not examine + # the content of the session or the parameters. That level of validation should + # be encapsulated by your domain model or helper methods in the controller. + module ClassMethods + # Verify the given actions so that if certain prerequisites are not met, + # the user is redirected to a different action. The +options+ parameter + # is a hash consisting of the following key/value pairs: + # + # :params:: + # a single key or an array of keys that must be in the params + # hash in order for the action(s) to be safely called. + # :session:: + # a single key or an array of keys that must be in the session + # in order for the action(s) to be safely called. + # :flash:: + # a single key or an array of keys that must be in the flash in order + # for the action(s) to be safely called. + # :method:: + # a single key or an array of keys--any one of which must match the + # current request method in order for the action(s) to be safely called. + # (The key should be a symbol: :get or :post, for + # example.) + # :xhr:: + # true/false option to ensure that the request is coming from an Ajax + # call or not. + # :add_flash:: + # a hash of name/value pairs that should be merged into the session's + # flash if the prerequisites cannot be satisfied. + # :add_headers:: + # a hash of name/value pairs that should be merged into the response's + # headers hash if the prerequisites cannot be satisfied. + # :redirect_to:: + # the redirection parameters to be used when redirecting if the + # prerequisites cannot be satisfied. You can redirect either to named + # route or to the action in some controller. + # :render:: + # the render parameters to be used when the prerequisites cannot be satisfied. + # :only:: + # only apply this verification to the actions specified in the associated + # array (may also be a single value). + # :except:: + # do not apply this verification to the actions specified in the associated + # array (may also be a single value). + def verify(options={}) + before_filter :only => options[:only], :except => options[:except] do + verify_action options + end + end + end + + private + + def verify_action(options) #:nodoc: + if prereqs_invalid?(options) + flash.update(options[:add_flash]) if options[:add_flash] + response.headers.merge!(options[:add_headers]) if options[:add_headers] + apply_remaining_actions(options) unless performed? + end + end + + def prereqs_invalid?(options) # :nodoc: + verify_presence_of_keys_in_hash_flash_or_params(options) || + verify_method(options) || + verify_request_xhr_status(options) + end + + def verify_presence_of_keys_in_hash_flash_or_params(options) # :nodoc: + [*options[:params] ].find { |v| v && params[v.to_sym].nil? } || + [*options[:session]].find { |v| session[v].nil? } || + [*options[:flash] ].find { |v| flash[v].nil? } + end + + def verify_method(options) # :nodoc: + [*options[:method]].all? { |v| request.request_method_symbol != v.to_sym } if options[:method] + end + + def verify_request_xhr_status(options) # :nodoc: + !!request.xhr? != options[:xhr] unless options[:xhr].nil? + end + + def apply_redirect_to(redirect_to_option) # :nodoc: + (redirect_to_option.is_a?(Symbol) && redirect_to_option != :back) ? self.__send__(redirect_to_option) : redirect_to_option + end + + def apply_remaining_actions(options) # :nodoc: + case + when options[:render] ; render(options[:render]) + when options[:redirect_to] ; redirect_to(apply_redirect_to(options[:redirect_to])) + else head(:bad_request) + end + end + end +end + +ActionController::Base.send :include, ActionController::Verification diff --git a/vendor/plugins/verification/lib/verification.rb b/vendor/plugins/verification/lib/verification.rb new file mode 100644 --- /dev/null +++ b/vendor/plugins/verification/lib/verification.rb @@ -0,0 +1,1 @@ +require 'action_controller/verification' diff --git a/vendor/plugins/verification/test/test_helper.rb b/vendor/plugins/verification/test/test_helper.rb new file mode 100644 --- /dev/null +++ b/vendor/plugins/verification/test/test_helper.rb @@ -0,0 +1,20 @@ +require 'bundler/setup' +require 'test/unit' +require 'active_support' +require 'action_controller' +require 'mocha' + +require File.dirname(__FILE__) + '/../lib/action_controller/verification' + +SharedTestRoutes = ActionDispatch::Routing::RouteSet.new +SharedTestRoutes.draw do + match ":controller(/:action(/:id))" +end + +ActionController::Base.send :include, SharedTestRoutes.url_helpers + +module ActionController + class TestCase + setup { @routes = SharedTestRoutes } + end +end diff --git a/vendor/plugins/verification/test/verification_test.rb b/vendor/plugins/verification/test/verification_test.rb new file mode 100644 --- /dev/null +++ b/vendor/plugins/verification/test/verification_test.rb @@ -0,0 +1,289 @@ +require 'test_helper' + +class VerificationTestController < ActionController::Base + verify :only => :guarded_one, :params => "one", + :add_flash => { :error => 'unguarded' }, + :redirect_to => { :action => "unguarded" } + + verify :only => :guarded_two, :params => %w( one two ), + :redirect_to => { :action => "unguarded" } + + verify :only => :guarded_with_flash, :params => "one", + :add_flash => { :notice => "prereqs failed" }, + :redirect_to => { :action => "unguarded" } + + verify :only => :guarded_in_session, :session => "one", + :redirect_to => { :action => "unguarded" } + + verify :only => [:multi_one, :multi_two], :session => %w( one two ), + :redirect_to => { :action => "unguarded" } + + verify :only => :guarded_by_method, :method => :post, + :redirect_to => { :action => "unguarded" } + + verify :only => :guarded_by_xhr, :xhr => true, + :redirect_to => { :action => "unguarded" } + + verify :only => :guarded_by_not_xhr, :xhr => false, + :redirect_to => { :action => "unguarded" } + + before_filter :unconditional_redirect, :only => :two_redirects + verify :only => :two_redirects, :method => :post, + :redirect_to => { :action => "unguarded" } + + verify :only => :must_be_post, :method => :post, :render => { :status => 405, :text => "Must be post" }, :add_headers => { "Allow" => "POST" } + + verify :only => :must_be_put, :method => :put, :render => { :status => 405, :text => "Must be put" }, :add_headers => { "Allow" => "PUT" } + + verify :only => :guarded_one_for_named_route_test, :params => "one", + :redirect_to => :foo_url + + verify :only => :no_default_action, :params => "santa" + + verify :only => :guarded_with_back, :method => :post, + :redirect_to => :back + + def guarded_one + render :text => "#{params[:one]}" + end + + def guarded_one_for_named_route_test + render :text => "#{params[:one]}" + end + + def guarded_with_flash + render :text => "#{params[:one]}" + end + + def guarded_two + render :text => "#{params[:one]}:#{params[:two]}" + end + + def guarded_in_session + render :text => "#{session["one"]}" + end + + def multi_one + render :text => "#{session["one"]}:#{session["two"]}" + end + + def multi_two + render :text => "#{session["two"]}:#{session["one"]}" + end + + def guarded_by_method + render :text => "#{request.method}" + end + + def guarded_by_xhr + render :text => "#{!!request.xhr?}" + end + + def guarded_by_not_xhr + render :text => "#{!!request.xhr?}" + end + + def unguarded + render :text => "#{params[:one]}" + end + + def two_redirects + render :nothing => true + end + + def must_be_post + render :text => "Was a post!" + end + + def must_be_put + render :text => "Was a put!" + end + + def guarded_with_back + render :text => "#{params[:one]}" + end + + def no_default_action + # Will never run + end + + protected + + def unconditional_redirect + redirect_to :action => "unguarded" + end +end + +class VerificationTest < ActionController::TestCase + tests ::VerificationTestController + + def test_using_symbol_back_with_no_referrer + assert_raise(ActionController::RedirectBackError) { get :guarded_with_back } + end + + def test_using_symbol_back_redirects_to_referrer + @request.env["HTTP_REFERER"] = "/foo" + get :guarded_with_back + assert_redirected_to '/foo' + end + + def test_no_deprecation_warning_for_named_route + assert_not_deprecated do + with_routing do |set| + set.draw do + match 'foo', :to => 'test#foo', :as => :foo + match 'verification_test/:action', :to => ::VerificationTestController + end + get :guarded_one_for_named_route_test, :two => "not one" + assert_redirected_to '/foo' + end + end + end + + def test_guarded_one_with_prereqs + get :guarded_one, :one => "here" + assert_equal "here", @response.body + end + + def test_guarded_one_without_prereqs + get :guarded_one + assert_redirected_to :action => "unguarded" + assert_equal 'unguarded', flash[:error] + end + + def test_guarded_with_flash_with_prereqs + get :guarded_with_flash, :one => "here" + assert_equal "here", @response.body + assert flash.empty? + end + + def test_guarded_with_flash_without_prereqs + get :guarded_with_flash + assert_redirected_to :action => "unguarded" + assert_equal "prereqs failed", flash[:notice] + end + + def test_guarded_two_with_prereqs + get :guarded_two, :one => "here", :two => "there" + assert_equal "here:there", @response.body + end + + def test_guarded_two_without_prereqs_one + get :guarded_two, :two => "there" + assert_redirected_to :action => "unguarded" + end + + def test_guarded_two_without_prereqs_two + get :guarded_two, :one => "here" + assert_redirected_to :action => "unguarded" + end + + def test_guarded_two_without_prereqs_both + get :guarded_two + assert_redirected_to :action => "unguarded" + end + + def test_unguarded_with_params + get :unguarded, :one => "here" + assert_equal "here", @response.body + end + + def test_unguarded_without_params + get :unguarded + assert @response.body.blank? + end + + def test_guarded_in_session_with_prereqs + get :guarded_in_session, {}, "one" => "here" + assert_equal "here", @response.body + end + + def test_guarded_in_session_without_prereqs + get :guarded_in_session + assert_redirected_to :action => "unguarded" + end + + def test_multi_one_with_prereqs + get :multi_one, {}, "one" => "here", "two" => "there" + assert_equal "here:there", @response.body + end + + def test_multi_one_without_prereqs + get :multi_one + assert_redirected_to :action => "unguarded" + end + + def test_multi_two_with_prereqs + get :multi_two, {}, "one" => "here", "two" => "there" + assert_equal "there:here", @response.body + end + + def test_multi_two_without_prereqs + get :multi_two + assert_redirected_to :action => "unguarded" + end + + def test_guarded_by_method_with_prereqs + post :guarded_by_method + assert_equal "POST", @response.body + end + + def test_guarded_by_method_without_prereqs + get :guarded_by_method + assert_redirected_to :action => "unguarded" + end + + def test_guarded_by_xhr_with_prereqs + xhr :post, :guarded_by_xhr + assert_equal "true", @response.body + end + + def test_guarded_by_xhr_without_prereqs + get :guarded_by_xhr + assert_redirected_to :action => "unguarded" + end + + def test_guarded_by_not_xhr_with_prereqs + get :guarded_by_not_xhr + assert_equal "false", @response.body + end + + def test_guarded_by_not_xhr_without_prereqs + xhr :post, :guarded_by_not_xhr + assert_redirected_to :action => "unguarded" + end + + def test_guarded_post_and_calls_render_succeeds + post :must_be_post + assert_equal "Was a post!", @response.body + end + + def test_guarded_put_and_calls_render_succeeds + put :must_be_put + assert_equal "Was a put!", @response.body + end + + def test_default_failure_should_be_a_bad_request + post :no_default_action + assert_response :bad_request + end + + def test_guarded_post_and_calls_render_fails_and_sets_allow_header + get :must_be_post + assert_response 405 + assert_equal "Must be post", @response.body + assert_equal "POST", @response.headers["Allow"] + end + + def test_second_redirect + assert_nothing_raised { get :two_redirects } + end + + def test_guarded_http_method_respects_overwritten_request_method + # Overwrite http method on application level like Rails supports via sending a _method parameter + @request.stubs(:request_method_symbol).returns(:put) + + post :must_be_put + assert_equal "Was a put!", @response.body + end +end diff --git a/vendor/plugins/verification/verification.gemspec b/vendor/plugins/verification/verification.gemspec new file mode 100644 --- /dev/null +++ b/vendor/plugins/verification/verification.gemspec @@ -0,0 +1,26 @@ +$:.push File.expand_path("../lib", __FILE__) + +Gem::Specification.new do |s| + s.name = "verification" + s.version = "1.0.3" + s.platform = Gem::Platform::RUBY + s.authors = ["David Heinemeier Hansson"] + s.email = ["david@loudthinking.com"] + s.homepage = "https://rubygems.org/gems/verification" + s.summary = %q{Verify preconditions for Rails actions} + s.description = %q{Verify preconditions for Rails actions} + + s.files = `git ls-files`.split("\n") + s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n") + s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) } + s.require_paths = ["lib"] + + s.add_dependency('activesupport', '>= 3.0.0', '< 3.3.0') + s.add_dependency('actionpack', '>= 3.0.0', '< 3.3.0') + + s.add_development_dependency('rake') + s.add_development_dependency('test-unit') + s.add_development_dependency('activesupport') + s.add_development_dependency('actionpack') + s.add_development_dependency('mocha') +end