Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Added absence validator for active model. #7155

Closed
wants to merge 14 commits into from

12 participants

Roberto Vasquez Angel Robert Pankowecki Ev Dolzhenko Godfrey Chan Kendall Buchanan Francesco Rodríguez Carlos Antonio da Silva Steve Klabnik Wil Moore III Yves Senn Rafael Mendonça França James Coglan
Roberto Vasquez Angel

Please accept my pull request for an absence validator.

See following discussion: https://groups.google.com/forum/#!topic/rubyonrails-core/KS_Olcnlw8w

I included passing tests and docs (Copied and adapted from the presence validator).

Thanks!

Saludos,

Roberto

Robert Pankowecki

+1

Ev Dolzhenko

+1 Can confirm that needed this in almost every project with relatively complex models

Roberto Vasquez Angel

I use it on trees, where i need attributes to be empty on roots. But I think, there should be better examples.

Anyone?

Godfrey Chan
Owner

With the strict option set to true, this could be used to verify the integrity of your models... so I'm +1 on this.

Kendall Buchanan

+1 – Require first and last names for users older than 13, but prevent update for younger than 13.

Francesco Rodríguez frodsan commented on the diff
...del/test/cases/validations/absence_validation_test.rb
((37 lines not shown))
+ end
+
+ def test_accepts_array_arguments
+ Topic.validates_absence_of %w(title content)
+ t = Topic.new
+
+ t.title = "foo"
+ t.content = "bar"
+
+ assert t.invalid?
+ assert_equal ["must be blank"], t.errors[:title]
+ assert_equal ["must be blank"], t.errors[:content]
+ end
+
+ def test_validates_acceptance_of_with_custom_error_using_quotes
+ Person.validates_absence_of :karma, :message => "This string contains 'single' and \"double\" quotes"
Francesco Rodríguez
frodsan added a note

Please use 1.9 hash syntax. Thanks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Francesco Rodríguez

I'm :+1: on this.

activemodel/lib/active_model/validations/absence.rb
@@ -0,0 +1,34 @@
+require 'active_support/core_ext/object/blank'
+
+module ActiveModel
+
+ # == Active Model Absence Validator
Francesco Rodríguez
frodsan added a note

Could you move this title to class AbsenceValidator?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Francesco Rodríguez

@robotex82 Could you add a CHANGELOG entry please? Thanks!

...del/test/cases/validations/absence_validation_test.rb
((5 lines not shown))
+require 'models/person'
+require 'models/custom_reader'
+
+class AbsenceValidationTest < ActiveModel::TestCase
+
+ teardown do
+ Topic.reset_callbacks(:validate)
+ Person.reset_callbacks(:validate)
+ CustomReader.reset_callbacks(:validate)
+ end
+
+ def test_validate_absences
+ Topic.validates_absence_of(:title, :content)
+
+ t = Topic.new
+

Remove blank line.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Carlos Antonio da Silva carlosantoniodasilva commented on the diff
...del/test/cases/validations/absence_validation_test.rb
@@ -0,0 +1,90 @@
+# encoding: utf-8
+require 'cases/helper'
+
+require 'models/topic'
+require 'models/person'
+require 'models/custom_reader'
+
+class AbsenceValidationTest < ActiveModel::TestCase
+

Remove blank line.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Carlos Antonio da Silva carlosantoniodasilva commented on the diff
...del/test/cases/validations/absence_validation_test.rb
((27 lines not shown))
+
+ t.title = ""
+ t.content = "something"
+
+ assert t.invalid?
+ assert_equal ["must be blank"], t.errors[:content]
+
+ t.content = ""
+
+ assert t.valid?
+ end
+
+ def test_accepts_array_arguments
+ Topic.validates_absence_of %w(title content)
+ t = Topic.new
+

Remove blank line.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Carlos Antonio da Silva carlosantoniodasilva commented on the diff
...del/test/cases/validations/absence_validation_test.rb
((39 lines not shown))
+ def test_accepts_array_arguments
+ Topic.validates_absence_of %w(title content)
+ t = Topic.new
+
+ t.title = "foo"
+ t.content = "bar"
+
+ assert t.invalid?
+ assert_equal ["must be blank"], t.errors[:title]
+ assert_equal ["must be blank"], t.errors[:content]
+ end
+
+ def test_validates_acceptance_of_with_custom_error_using_quotes
+ Person.validates_absence_of :karma, :message => "This string contains 'single' and \"double\" quotes"
+ p = Person.new
+

Remove blank line.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
...del/test/cases/validations/absence_validation_test.rb
((51 lines not shown))
+ def test_validates_acceptance_of_with_custom_error_using_quotes
+ Person.validates_absence_of :karma, :message => "This string contains 'single' and \"double\" quotes"
+ p = Person.new
+
+ p.karma = "good"
+
+ assert p.invalid?
+ assert_equal "This string contains 'single' and \"double\" quotes", p.errors[:karma].last
+ end
+
+ def test_validates_absence_of_for_ruby_class
+ Person.validates_absence_of :karma
+
+ p = Person.new
+
+ p.karma = "good"

Remove blank line.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
...del/test/cases/validations/absence_validation_test.rb
((54 lines not shown))
+
+ p.karma = "good"
+
+ assert p.invalid?
+ assert_equal "This string contains 'single' and \"double\" quotes", p.errors[:karma].last
+ end
+
+ def test_validates_absence_of_for_ruby_class
+ Person.validates_absence_of :karma
+
+ p = Person.new
+
+ p.karma = "good"
+
+ assert p.invalid?
+

Remove blank line.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
...del/test/cases/validations/absence_validation_test.rb
((70 lines not shown))
+ assert_equal ["must be blank"], p.errors[:karma]
+
+ p.karma = nil
+ assert p.valid?
+ end
+
+ def test_validates_absence_of_for_ruby_class_with_custom_reader
+ CustomReader.validates_absence_of :karma
+
+ p = CustomReader.new
+
+ p[:karma] = "excellent"
+
+ assert p.invalid?
+
+ assert_equal ["must be blank"], p.errors[:karma]

Same thing for blank lines here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
activemodel/lib/active_model/errors.rb
@@ -324,6 +324,19 @@ def add_on_blank(attributes, options = {})
add(attribute, :blank, options) if value.blank?
end
end
+
+ # Will add an error message to each of the attributes in +attributes+ that
+ # is not blank (using Object#blank?).
+ #
+ # person.errors.add_on_not_blank(:name)
+ # person.errors.messages
+ # # => { :name => ["must be blank"] }
+ def add_on_not_blank(attributes, options = {})
+ [attributes].flatten.each do |attribute|
+ value = @base.send(:read_attribute_for_validation, attribute)
+ add(attribute, :not_blank, options) unless value.blank?
+ end
+ end

I don't think we need to have this in here, I believe that should be handled by the validator itself in this case. There's no need to extend Errors API with this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Carlos Antonio da Silva

I wonder if we should use present instead of not_blank, thoughts?

@robotex82 thanks!

Roberto Vasquez Angel

Thank you guys!

Sorry, I have been busy all the time, but i'll have a look at your suggestions asap. Hopefully before next weekend.

Do you see any chance to have this merged into master for v4.0?

Carlos Antonio da Silva

@robotex82 I believe so :)

Roberto Vasquez Angel

I updated my local copy and changed add_on_not_blank to add_on_present.

I had some trouble with updating and rebasing - I admit, I lack experience on this topic - but I hope I didn't mess up.

Could someone please check my updated pull request?

What do I have to do next?

Carlos Antonio da Silva

@robotex82 thanks, but apparently you have some commits that shouldn't be there, can you try a new rebase, so that we can see only your PR diff?

Roberto Vasquez Angel

I did:

git rebase upstream/master
git push --force

is that correct?

Excuse my noobish questions! I googled for the correct process of updating forks, rebasing, etc. but it's still not really clear to me.

activemodel/lib/active_model/validations/absence.rb
@@ -0,0 +1,33 @@
+require 'active_support/core_ext/object/blank'
+
+module ActiveModel
+ module Validations
+ # == Active Model Absence Validator

You can remove this comment.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
activemodel/lib/active_model/errors.rb
@@ -328,6 +328,19 @@ def add_on_blank(attributes, options = {})
end
end
+ # Will add an error message to each of the attributes in +attributes+ that
+ # is present (using Object#present?).
+ #
+ # person.errors.add_on_present(:name)
+ # person.errors.messages
+ # # => { :name => ["must be blank"] }
+ def add_on_present(attributes, options = {})
+ [attributes].flatten.each do |attribute|

Can use Array(attributes), it has changed these days in master.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
activemodel/lib/active_model/validations/absence.rb
@@ -0,0 +1,33 @@
+require 'active_support/core_ext/object/blank'
+

You don't need this require.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Carlos Antonio da Silva

@robotex82 it's fine now, thanks. I added some comments, and we'll need a two changelog entries in Active Model, one for Errors#add_on_present and other for the validation itself, can you please add them, and squash your commits? Thanks.

Roberto Vasquez Angel

Should I append or prepend to the changelog?

Carlos Antonio da Silva

@robotex82 prepend, always at top :).

Roberto Vasquez Angel robotex82 Add `ActiveModel::Validations::AbsenceValidator`, a validator to chec…
…k the absence of attributes.

Add `ActiveModel::Errors#add_on_present` method. Adds error messages to present attributes.
072b977
Roberto Vasquez Angel

Hope it's ok now.....

Carlos Antonio da Silva

I added some minor comments, can you take a look at them before we merge? Also, what do you think about adding an example for the changelog entries, similar to the ones you added to the docs? (just look at other changelog examples to see how to format them).

Other than that, just squash your commits and we're good to go :). Thanks!

Roberto Vasquez Angel

Well, I added the example to the changelog. But again I'm stuck with git :(

I have a commit history like that:

1a9f792 Updated changelog
072b977 Add `ActiveModel::Validations::AbsenceValidator`, a validator to check the  absence of attributes. Add `ActiveModel::Errors#add_on_present` method. Adds error messages to presen
c35fa0b Removed blank lines Changed add_on_not_blank to add_on_present
02e8148 Added absence validator for active model.
c233b2f Fix guides home links and maintain compatibility with small screens

So I thought I google how to squash commits. I tried some things but I keep getting errors or the commits don't go away.

So, please, could some of you experieced people, tell me how I package things up?

Thanks, and sorry again...squashing, rabaseing, etc. is new stuff to me.

@carlosantoniodasilva: I couldn't find your comments, where are they?

Carlos Antonio da Silva

@robotex82 just look at the pull request Files Changed tab with the diff, and you'll see the comments (just ignore the "Remove blank line." ones).

Now about the rebase, Jon has just given a quick explanation here, but if you still have doubts, please let me know. Thanks!

Carlos Antonio da Silva

@steveklabnik Ah true, I forgot that, awesome thanks :heart:

Steve Klabnik
Collaborator

You didn't forget that at all; I saw this comment, was like "man, I really need to put this in a place where I can find it again," posted it, and then linked it here. ;)

Carlos Antonio da Silva

Haha sorry, I thought I had seen that among your other contribution related posts. Awesome anyway, you were fast on that :smile:

carlosantoniodasilva and others added some commits
Carlos Antonio da Silva carlosantoniodasilva Remove block given check from private find_with_ids
This is already handled by #find, it's a duplicate check, since
find_with_ids is not called from anywhere else.
838c5e8
Carlos Antonio da Silva carlosantoniodasilva Use cached quoted_table_name instead of going through the connection 4995979
Carlos Antonio da Silva carlosantoniodasilva Remove not used indifferent_access requires from Base and FinderMethods fda107b
Yves Senn senny `plugin new` adds dummy app tasks when necessary.
Closes #8121

The `plugin new` generator always adds the dummy app rake tasks,
when a dummy app was created.
a085d27
Rafael Mendonça França rafaelfranca Add CHANGELOG entry for #8108 on master too.
[ci skip]
6f8b95c
Gabriel Sobrinho & Ricardo Henrique Add test to avoid regression of 58e48d5 50337da
Yves Senn senny test case to lock down the behavior of #7842 4d10d8b
Yves Senn senny routing prefix slash replacement is no longer necessary 4c14e05
James Coglan jcoglan Store FlashHashes in the session as plain hashes rather than custom o…
…bjects with unstable class names and instance variables.

Refactor FlashHash to take values for its ivars in the constructor, to pretty up FlashHash.from_session_value.

Remove stale comment on FlashHash: it is no longer Marshaled in the session so we can change its implementation.

Remove blank lines I introduced in controller/test_case.rb.

Unit tests for FlashHash#to_session_value.

Put in a compatibility layer to accept FlashHash serializations from Rails 3.0+.

Test that Rails 3.2 session flashes are correctly converted to the new format.

Remove code path for processing Rails 3.0 FlashHashes since they can no longer deserialize.
f66ec25
Carlos Antonio da Silva carlosantoniodasilva Remove free usage of #tap 9a68393
Roberto Vasquez Angel robotex82 Add `ActiveModel::Validations::AbsenceValidator`, a validator to chec…
…k the absence of attributes.

Add `ActiveModel::Errors#add_on_present` method. Adds error messages to present attributes.
64df864
Roberto Vasquez Angel

This looks ....wrong?!

Carlos Antonio da Silva

Looks like there are some extra commits now :)

Roberto Vasquez Angel

OT: Time to read a book about GIT. Suggestions?

Roberto Vasquez Angel

I read the section on rebasing, but I don't get this right. :(

How do we get this right? Should I create a completely new pull request?

Steve Klabnik
Collaborator

@robotex82 Because it's been a month, and you're obviously having some trouble, and I had some time this morning, I took care of this.

You can see the commit here: d72a07f

I also took care of @frodsan 's comment about the 1.9 hash syntax, and double checked @carlosantoniodasilva 's comments about blank lines.

Thank you for your pull request, hopefully you will overcome the git beast next time ;)

Wil Moore III

@robotex82: I highly recommend the Mastering Git series by @tlberglund and @matthewmccullough:

Full Disclosure: I know them both personally (and I appear in one of the videos) so I am a bit biased -- but trust me, watch these videos a few times through and you will level-up in your git-fu for sure.

Roberto Vasquez Angel

Awesome, thank you guys! :heart:

Sorry for the late answer, but I've been very busy!

While checking the commit, I discovered, that the CHANGELOG is messed up a little bit:

Actual:

*   Add `ActiveModel::Validations::AbsenceValidator`, a validator to check the
    absence of attributes.

        class Person
          include ActiveModel::Validations

          attr_accessor :first_name
          validates_absence_of :first_name
        end

        person = Person.new
        person.first_name = "John"
        person.valid?
        # => false
        person.errors.messages
        # => {:first_name=>["must be blank"]}

    *Roberto Vasquez Angel*

*   `[attribute]_changed?` now returns `false` after a call to `reset_[attribute]!`

Shouldn't it be:

    *Roberto Vasquez Angel*

*   Add `ActiveModel::Validations::AbsenceValidator`, a validator to check the
    absence of attributes.

        class Person
          include ActiveModel::Validations

          attr_accessor :first_name
          validates_absence_of :first_name
        end

        person = Person.new
        person.first_name = "John"
        person.valid?
        # => false
        person.errors.messages
        # => {:first_name=>["must be blank"]}

*   `[attribute]_changed?` now returns `false` after a call to `reset_[attribute]!`

How do we get this right?

Thanks again!

Carlos Antonio da Silva

Not sure what you mean, but the name goes after the change, at the end.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Nov 7, 2012
  1. Roberto Vasquez Angel
  2. Roberto Vasquez Angel

    Removed blank lines

    robotex82 authored
    Changed add_on_not_blank to add_on_present
  3. Roberto Vasquez Angel

    Add `ActiveModel::Validations::AbsenceValidator`, a validator to chec…

    robotex82 authored
    …k the absence of attributes.
    
    Add `ActiveModel::Errors#add_on_present` method. Adds error messages to present attributes.
Commits on Nov 8, 2012
  1. Carlos Antonio da Silva Roberto Vasquez Angel

    Remove block given check from private find_with_ids

    carlosantoniodasilva authored robotex82 committed
    This is already handled by #find, it's a duplicate check, since
    find_with_ids is not called from anywhere else.
  2. Carlos Antonio da Silva Roberto Vasquez Angel
  3. Carlos Antonio da Silva Roberto Vasquez Angel
  4. Yves Senn Roberto Vasquez Angel

    `plugin new` adds dummy app tasks when necessary.

    senny authored robotex82 committed
    Closes #8121
    
    The `plugin new` generator always adds the dummy app rake tasks,
    when a dummy app was created.
  5. Rafael Mendonça França Roberto Vasquez Angel

    Add CHANGELOG entry for #8108 on master too.

    rafaelfranca authored robotex82 committed
    [ci skip]
  6. Roberto Vasquez Angel

    Add test to avoid regression of 58e48d5

    Gabriel Sobrinho & Ricardo Henrique authored robotex82 committed
  7. Yves Senn Roberto Vasquez Angel

    test case to lock down the behavior of #7842

    senny authored robotex82 committed
  8. Yves Senn Roberto Vasquez Angel

    routing prefix slash replacement is no longer necessary

    senny authored robotex82 committed
  9. James Coglan Roberto Vasquez Angel

    Store FlashHashes in the session as plain hashes rather than custom o…

    jcoglan authored robotex82 committed
    …bjects with unstable class names and instance variables.
    
    Refactor FlashHash to take values for its ivars in the constructor, to pretty up FlashHash.from_session_value.
    
    Remove stale comment on FlashHash: it is no longer Marshaled in the session so we can change its implementation.
    
    Remove blank lines I introduced in controller/test_case.rb.
    
    Unit tests for FlashHash#to_session_value.
    
    Put in a compatibility layer to accept FlashHash serializations from Rails 3.0+.
    
    Test that Rails 3.2 session flashes are correctly converted to the new format.
    
    Remove code path for processing Rails 3.0 FlashHashes since they can no longer deserialize.
  10. Carlos Antonio da Silva Roberto Vasquez Angel

    Remove free usage of #tap

    carlosantoniodasilva authored robotex82 committed
  11. Roberto Vasquez Angel

    Add `ActiveModel::Validations::AbsenceValidator`, a validator to chec…

    robotex82 authored
    …k the absence of attributes.
    
    Add `ActiveModel::Errors#add_on_present` method. Adds error messages to present attributes.
This page is out of date. Refresh to see the latest.
16 actionpack/CHANGELOG.md
View
@@ -1,5 +1,21 @@
## Rails 4.0.0 (unreleased) ##
+* Fix input name when `:multiple => true` and `:index` are set.
+
+ Before:
+
+ check_box("post", "comment_ids", { :multiple => true, :index => "foo" }, 1)
+ #=> <input name=\"post[foo][comment_ids]\" type=\"hidden\" value=\"0\" /><input id=\"post_foo_comment_ids_1\" name=\"post[foo][comment_ids]\" type=\"checkbox\" value=\"1\" />
+
+ After:
+
+ check_box("post", "comment_ids", { :multiple => true, :index => "foo" }, 1)
+ #=> <input name=\"post[foo][comment_ids][]\" type=\"hidden\" value=\"0\" /><input id=\"post_foo_comment_ids_1\" name=\"post[foo][comment_ids][]\" type=\"checkbox\" value=\"1\" />
+
+ Fix #8108
+
+ *Daniel Fox, Grant Hutchins & Trace Wax*
+
* Clear url helpers when reloading routes.
*Santiago Pastorino*
3  actionpack/lib/action_controller/test_case.rb
View
@@ -509,7 +509,7 @@ def process(action, http_method = 'GET', *args)
@request.assign_parameters(@routes, controller_class_name, action.to_s, parameters)
@request.session.update(session) if session
- @request.session["flash"] = @request.flash.update(flash || {})
+ @request.flash.update(flash || {})
@controller.request = @request
@controller.response = @response
@@ -526,6 +526,7 @@ def process(action, http_method = 'GET', *args)
@response.prepare!
@assigns = @controller.respond_to?(:view_assigns) ? @controller.view_assigns : {}
+ @request.session['flash'] = @request.flash.to_session_value
@request.session.delete('flash') if @request.session['flash'].blank?
@response
end
33 actionpack/lib/action_dispatch/middleware/flash.rb
View
@@ -4,7 +4,7 @@ class Request < Rack::Request
# read a notice you put there or <tt>flash["notice"] = "hello"</tt>
# to put a new one.
def flash
- @env[Flash::KEY] ||= (session["flash"] || Flash::FlashHash.new).tap(&:sweep)
+ @env[Flash::KEY] ||= Flash::FlashHash.from_session_value(session["flash"])
end
end
@@ -70,16 +70,31 @@ def notice=(message)
end
end
- # Implementation detail: please do not change the signature of the
- # FlashHash class. Doing that will likely affect all Rails apps in
- # production as the FlashHash currently stored in their sessions will
- # become invalid.
class FlashHash
include Enumerable
- def initialize #:nodoc:
- @discard = Set.new
- @flashes = {}
+ def self.from_session_value(value)
+ flash = case value
+ when FlashHash # Rails 3.1, 3.2
+ new(value.instance_variable_get(:@flashes), value.instance_variable_get(:@used))
+ when Hash # Rails 4.0
+ new(value['flashes'], value['discard'])
+ else
+ new
+ end
+
+ flash.sweep
+ flash
+ end
+
+ def to_session_value
+ return nil if empty?
+ { 'discard' => @discard.to_a, 'flashes' => @flashes }
+ end
+
+ def initialize(flashes = {}, discard = []) #:nodoc:
+ @discard = Set.new(discard)
+ @flashes = flashes
@now = nil
end
@@ -223,7 +238,7 @@ def call(env)
if flash_hash
if !flash_hash.empty? || session.key?('flash')
- session["flash"] = flash_hash
+ session["flash"] = flash_hash.to_session_value
new_hash = flash_hash.dup
else
new_hash = flash_hash
4 actionpack/lib/action_dispatch/routing/mapper.rb
View
@@ -491,9 +491,7 @@ def define_generate_prefix(app, name)
prefix_options = options.slice(*_route.segment_keys)
# we must actually delete prefix segment keys to avoid passing them to next url_for
_route.segment_keys.each { |k| options.delete(k) }
- prefix = _routes.url_helpers.send("#{name}_path", prefix_options)
- prefix = '' if prefix == '/'
- prefix
+ _routes.url_helpers.send("#{name}_path", prefix_options)
end
end
end
21 actionpack/test/controller/flash_hash_test.rb
View
@@ -46,6 +46,27 @@ def test_to_hash
assert_equal({'foo' => 'bar'}, @hash.to_hash)
end
+ def test_to_session_value
+ @hash['foo'] = 'bar'
+ assert_equal({'flashes' => {'foo' => 'bar'}, 'discard' => []}, @hash.to_session_value)
+
+ @hash.discard('foo')
+ assert_equal({'flashes' => {'foo' => 'bar'}, 'discard' => %w[foo]}, @hash.to_session_value)
+
+ @hash.now['qux'] = 1
+ assert_equal({'flashes' => {'foo' => 'bar', 'qux' => 1}, 'discard' => %w[foo qux]}, @hash.to_session_value)
+
+ @hash.sweep
+ assert_equal(nil, @hash.to_session_value)
+ end
+
+ def test_from_session_value
+ rails_3_2_cookie = 'BAh7B0kiD3Nlc3Npb25faWQGOgZFRkkiJWY4ZTFiODE1MmJhNzYwOWMyOGJiYjE3ZWM5MjYzYmE3BjsAVEkiCmZsYXNoBjsARm86JUFjdGlvbkRpc3BhdGNoOjpGbGFzaDo6Rmxhc2hIYXNoCToKQHVzZWRvOghTZXQGOgpAaGFzaHsAOgxAY2xvc2VkRjoNQGZsYXNoZXN7BkkiDG1lc3NhZ2UGOwBGSSIKSGVsbG8GOwBGOglAbm93MA=='
+ session = Marshal.load(Base64.decode64(rails_3_2_cookie))
+ hash = Flash::FlashHash.from_session_value(session['flash'])
+ assert_equal({'flashes' => {'message' => 'Hello'}, 'discard' => %w[message]}, hash.to_session_value)
+ end
+
def test_empty?
assert @hash.empty?
@hash['zomg'] = 'bears'
5 actionpack/test/dispatch/prefix_generation_test.rb
View
@@ -241,6 +241,11 @@ def setup
assert_equal "/something/", app_object.root_path
end
+ test "[OBJECT] generating application's route includes default_url_options[:trailing_slash]" do
+ RailsApplication.routes.default_url_options[:trailing_slash] = true
+ assert_equal "/awesome/blog/posts", engine_object.posts_path
+ end
+
test "[OBJECT] generating engine's route with url_for" do
path = engine_object.url_for(:controller => "inside_engine_generating",
:action => "show",
18 activemodel/CHANGELOG.md
View
@@ -1,5 +1,23 @@
## Rails 4.0.0 (unreleased) ##
+* Add `ActiveModel::Validations::AbsenceValidator`, a validator to check the
+ absence of attributes.
+
+ class Person < ActiveRecord::Base
+ validates_absence_of :first_name
+ end
+
+ person = Person.new
+ person.first_name = "John"
+ person.valid?
+ => false
+ # first_name must be blank
+
+* Added `ActiveModel::Errors#add_on_present` method. Adds error messages to
+ present attributes.
+
+ *Roberto Vasquez Angel*
+
* Add `ActiveModel::ForbiddenAttributesProtection`, a simple module to
protect attributes from mass assignment when non-permitted attributes are passed.
13 activemodel/lib/active_model/errors.rb
View
@@ -328,6 +328,19 @@ def add_on_blank(attributes, options = {})
end
end
+ # Will add an error message to each of the attributes in +attributes+ that
+ # is present (using Object#present?).
+ #
+ # person.errors.add_on_present(:name)
+ # person.errors.messages
+ # # => { :name => ["must be blank"] }
+ def add_on_present(attributes, options = {})
+ Array(attributes).flatten.each do |attribute|
+ value = @base.send(:read_attribute_for_validation, attribute)
+ add(attribute, :not_blank, options) if value.present?
+ end
+ end
+
# Returns +true+ if an error on the attribute with the given message is
# present, +false+ otherwise. +message+ is treated the same as for +add+.
#
1  activemodel/lib/active_model/locale/en.yml
View
@@ -13,6 +13,7 @@ en:
accepted: "must be accepted"
empty: "can't be empty"
blank: "can't be blank"
+ not_blank: "must be blank"
too_long: "is too long (maximum is %{count} characters)"
too_short: "is too short (minimum is %{count} characters)"
wrong_length: "is the wrong length (should be %{count} characters)"
31 activemodel/lib/active_model/validations/absence.rb
View
@@ -0,0 +1,31 @@
+module ActiveModel
+ module Validations
+ # == Active Model Absence Validator
+ class AbsenceValidator < EachValidator #:nodoc:
+ def validate(record)
+ record.errors.add_on_present(attributes, options)
+ end
+ end
+
+ module HelperMethods
+ # Validates that the specified attributes are blank (as defined by
+ # Object#blank?). Happens by default on save.
+ #
+ # class Person < ActiveRecord::Base
+ # validates_absence_of :first_name
+ # end
+ #
+ # The first_name attribute must be in the object and it must be blank.
+ #
+ # Configuration options:
+ # * <tt>:message</tt> - A custom error message (default is: "must be blank").
+ #
+ # There is also a list of default options supported by every validator:
+ # +:if+, +:unless+, +:on+ and +:strict+.
+ # See <tt>ActiveModel::Validation#validates</tt> for more information
+ def validates_absence_of(*attr_names)
+ validates_with AbsenceValidator, _merge_attributes(attr_names)
+ end
+ end
+ end
+end
67 activemodel/test/cases/validations/absence_validation_test.rb
View
@@ -0,0 +1,67 @@
+# encoding: utf-8
+require 'cases/helper'
+require 'models/topic'
+require 'models/person'
+require 'models/custom_reader'
+
+class AbsenceValidationTest < ActiveModel::TestCase
+ teardown do

Remove blank line.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+ Topic.reset_callbacks(:validate)
+ Person.reset_callbacks(:validate)
+ CustomReader.reset_callbacks(:validate)
+ end
+
+ def test_validate_absences
+ Topic.validates_absence_of(:title, :content)
+ t = Topic.new
+ t.title = "foo"
+ t.content = "bar"
+ assert t.invalid?
+ assert_equal ["must be blank"], t.errors[:title]
+ assert_equal ["must be blank"], t.errors[:content]
+ t.title = ""
+ t.content = "something"
+ assert t.invalid?
+ assert_equal ["must be blank"], t.errors[:content]
+ t.content = ""
+ assert t.valid?
+ end
+
+ def test_accepts_array_arguments
+ Topic.validates_absence_of %w(title content)
+ t = Topic.new
+ t.title = "foo"

Remove blank line.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+ t.content = "bar"
+ assert t.invalid?
+ assert_equal ["must be blank"], t.errors[:title]
+ assert_equal ["must be blank"], t.errors[:content]
+ end
+
+ def test_validates_acceptance_of_with_custom_error_using_quotes
+ Person.validates_absence_of :karma, :message => "This string contains 'single' and \"double\" quotes"
Francesco Rodríguez
frodsan added a note

Please use 1.9 hash syntax. Thanks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+ p = Person.new
+ p.karma = "good"

Remove blank line.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+ assert p.invalid?
+ assert_equal "This string contains 'single' and \"double\" quotes", p.errors[:karma].last
+ end
+
+ def test_validates_absence_of_for_ruby_class
+ Person.validates_absence_of :karma
+ p = Person.new
+ p.karma = "good"
+ assert p.invalid?
+ assert_equal ["must be blank"], p.errors[:karma]
+ p.karma = nil
+ assert p.valid?
+ end
+
+ def test_validates_absence_of_for_ruby_class_with_custom_reader
+ CustomReader.validates_absence_of :karma
+ p = CustomReader.new
+ p[:karma] = "excellent"
+ assert p.invalid?
+ assert_equal ["must be blank"], p.errors[:karma]
+ p[:karma] = ""
+ assert p.valid?
+ end
+end
1  activerecord/lib/active_record/base.rb
View
@@ -8,7 +8,6 @@
require 'active_support/core_ext/class/delegating_attributes'
require 'active_support/core_ext/array/extract_options'
require 'active_support/core_ext/hash/deep_merge'
-require 'active_support/core_ext/hash/indifferent_access'
require 'active_support/core_ext/hash/slice'
require 'active_support/core_ext/string/behavior'
require 'active_support/core_ext/kernel/singleton_class'
6 activerecord/lib/active_record/relation/finder_methods.rb
View
@@ -1,5 +1,3 @@
-require 'active_support/core_ext/hash/indifferent_access'
-
module ActiveRecord
module FinderMethods
# Find by id - This can either be a specific id (1), a list of ids (1, 5, 6), or an array of ids ([5, 6, 10]).
@@ -225,7 +223,7 @@ def apply_join_dependency(relation, join_dependency)
def construct_limited_ids_condition(relation)
orders = relation.order_values.map { |val| val.presence }.compact
- values = @klass.connection.distinct("#{@klass.connection.quote_table_name table_name}.#{primary_key}", orders)
+ values = @klass.connection.distinct("#{quoted_table_name}.#{primary_key}", orders)
relation = relation.dup
@@ -234,8 +232,6 @@ def construct_limited_ids_condition(relation)
end
def find_with_ids(*ids)
- return to_a.find { |*block_args| yield(*block_args) } if block_given?
-
expects_array = ids.first.kind_of?(Array)
return ids.first if expects_array && ids.first.empty?
11 activerecord/test/cases/nested_attributes_test.rb
View
@@ -185,6 +185,17 @@ def parrot_attributes=(attrs)
assert_equal "James", mean_pirate.parrot.name
assert_equal "blue", mean_pirate.parrot.color
end
+
+ def test_accepts_nested_attributes_for_can_be_overridden_in_subclasses
+ Pirate.accepts_nested_attributes_for(:parrot)
+
+ mean_pirate_class = Class.new(Pirate) do
+ accepts_nested_attributes_for :parrot
+ end
+ mean_pirate = mean_pirate_class.new
+ mean_pirate.parrot_attributes = { :name => "James" }
+ assert_equal "James", mean_pirate.parrot.name
+ end
end
class TestNestedAttributesOnAHasOneAssociation < ActiveRecord::TestCase
7 railties/CHANGELOG.md
View
@@ -1,5 +1,10 @@
## Rails 4.0.0 (unreleased) ##
+* Add dummy app Rake tasks when --skip-test-unit and --dummy-path is passed to the plugin generator.
+ Fix #8121
+
+ *Yves Senn*
+
* Ensure that RAILS_ENV is set when accessing Rails.env *Steve Klabnik*
* Don't eager-load app/assets and app/views *Elia Schito*
@@ -9,7 +14,7 @@
* New test locations `test/models`, `test/helpers`, `test/controllers`, and
`test/mailers`. Corresponding rake tasks added as well. *Mike Moore*
-* Set a different cache per environment for assets pipeline
+* Set a different cache per environment for assets pipeline
through `config.assets.cache`.
*Guillermo Iguaran*
6 railties/lib/rails/generators/rails/plugin_new/plugin_new_generator.rb
View
@@ -225,7 +225,7 @@ def create_test_files
end
def create_test_dummy_files
- return if options[:skip_test_unit] && options[:dummy_path] == 'test/dummy'
+ return unless with_dummy_app?
create_dummy_app
end
@@ -279,6 +279,10 @@ def mountable?
options[:mountable]
end
+ def with_dummy_app?
+ options[:skip_test_unit].blank? || options[:dummy_path] != 'test/dummy'
+ end
+
def self.banner
"rails plugin new #{self.arguments.map(&:usage).join(' ')} [options]"
end
2  railties/lib/rails/generators/rails/plugin_new/templates/Rakefile
View
@@ -14,7 +14,7 @@ RDoc::Task.new(:rdoc) do |rdoc|
rdoc.rdoc_files.include('lib/**/*.rb')
end
-<% if full? && !options[:skip_active_record] && !options[:skip_test_unit] -%>
+<% if full? && !options[:skip_active_record] && with_dummy_app? -%>
APP_RAKEFILE = File.expand_path("../<%= dummy_path -%>/Rakefile", __FILE__)
load 'rails/tasks/engine.rake'
<% end %>
6 railties/test/generators/plugin_new_generator_test.rb
View
@@ -66,6 +66,12 @@ def test_generating_test_files_in_full_mode_without_unit_test_files
assert_no_match(/APP_RAKEFILE/, File.read(File.join(destination_root, "Rakefile")))
end
+ def test_generating_adds_dummy_app_rake_tasks_without_unit_test_files
+ run_generator [destination_root, "-T", "--mountable", '--dummy-path', 'my_dummy_app']
+
+ assert_match(/APP_RAKEFILE/, File.read(File.join(destination_root, "Rakefile")))
+ end
+
def test_ensure_that_plugin_options_are_not_passed_to_app_generator
FileUtils.cd(Rails.root)
assert_no_match(/It works from file!.*It works_from_file/, run_generator([destination_root, "-m", "lib/template.rb"]))
Something went wrong with that request. Please try again.