remarkable_activerecord 3.0.1 → 3.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. data/CHANGELOG +46 -46
  2. data/LICENSE +1 -1
  3. data/README +64 -64
  4. data/lib/remarkable_activerecord.rb +1 -1
  5. data/lib/remarkable_activerecord/base.rb +40 -40
  6. data/lib/remarkable_activerecord/human_names.rb +24 -24
  7. data/lib/remarkable_activerecord/matchers/allow_mass_assignment_of_matcher.rb +14 -14
  8. data/lib/remarkable_activerecord/matchers/allow_values_for_matcher.rb +70 -70
  9. data/lib/remarkable_activerecord/matchers/association_matcher.rb +197 -197
  10. data/lib/remarkable_activerecord/matchers/have_column_matcher.rb +29 -29
  11. data/lib/remarkable_activerecord/matchers/have_index_matcher.rb +20 -20
  12. data/lib/remarkable_activerecord/matchers/have_readonly_attributes_matcher.rb +7 -7
  13. data/lib/remarkable_activerecord/matchers/have_scope_matcher.rb +34 -34
  14. data/lib/remarkable_activerecord/matchers/validate_acceptance_of_matcher.rb +37 -37
  15. data/lib/remarkable_activerecord/matchers/validate_associated_matcher.rb +75 -75
  16. data/lib/remarkable_activerecord/matchers/validate_confirmation_of_matcher.rb +44 -44
  17. data/lib/remarkable_activerecord/matchers/validate_exclusion_of_matcher.rb +17 -17
  18. data/lib/remarkable_activerecord/matchers/validate_inclusion_of_matcher.rb +20 -20
  19. data/lib/remarkable_activerecord/matchers/validate_numericality_of_matcher.rb +14 -14
  20. data/lib/remarkable_activerecord/matchers/validate_uniqueness_of_matcher.rb +80 -61
  21. data/locale/en.yml +253 -253
  22. data/spec/allow_mass_assignment_of_matcher_spec.rb +50 -50
  23. data/spec/allow_values_for_matcher_spec.rb +45 -45
  24. data/spec/association_matcher_spec.rb +612 -612
  25. data/spec/have_column_matcher_spec.rb +67 -67
  26. data/spec/have_index_matcher_spec.rb +61 -61
  27. data/spec/have_readonly_attributes_matcher_spec.rb +40 -40
  28. data/spec/have_scope_matcher_spec.rb +60 -60
  29. data/spec/model_builder.rb +101 -101
  30. data/spec/spec_helper.rb +25 -25
  31. data/spec/validate_acceptance_of_matcher_spec.rb +64 -64
  32. data/spec/validate_associated_matcher_spec.rb +118 -118
  33. data/spec/validate_confirmation_of_matcher_spec.rb +54 -54
  34. data/spec/validate_exclusion_of_matcher_spec.rb +76 -76
  35. data/spec/validate_inclusion_of_matcher_spec.rb +72 -72
  36. data/spec/validate_numericality_of_matcher_spec.rb +100 -100
  37. data/spec/validate_presence_of_matcher_spec.rb +40 -40
  38. data/spec/validate_uniqueness_of_matcher_spec.rb +158 -139
  39. metadata +3 -3
@@ -8,59 +8,59 @@ module Remarkable
8
8
  optional :primary, :null, :default => true
9
9
 
10
10
  collection_assertions :column_exists?, :options_match?
11
-
12
- before_assert do
13
- @options.each{|k,v| @options[k] = v.to_s}
14
- end
11
+
12
+ before_assert do
13
+ @options.each{|k,v| @options[k] = v.to_s}
14
+ end
15
15
 
16
16
  protected
17
17
 
18
18
  def column_exists?
19
19
  !column_type.nil?
20
20
  end
21
-
22
- def options_match?
23
- actual = get_column_options(column_type, @options.keys)
21
+
22
+ def options_match?
23
+ actual = get_column_options(column_type, @options.keys)
24
24
  return actual == @options, :actual => actual.inspect
25
- end
26
-
27
- def column_type
28
- subject_class.columns.detect {|c| c.name == @column.to_s }
29
- end
25
+ end
26
+
27
+ def column_type
28
+ subject_class.columns.detect {|c| c.name == @column.to_s }
29
+ end
30
30
 
31
31
  def get_column_options(column, keys)
32
- keys.inject({}) do |hash, key|
33
- hash[key] = column.instance_variable_get("@#{key}").to_s
34
- hash
32
+ keys.inject({}) do |hash, key|
33
+ hash[key] = column.instance_variable_get("@#{key}").to_s
34
+ hash
35
35
  end
36
- end
37
-
38
- def interpolation_options
39
- { :options => @options.inspect }
40
- end
36
+ end
37
+
38
+ def interpolation_options
39
+ { :options => @options.inspect }
40
+ end
41
41
 
42
42
  end
43
43
 
44
44
  # Ensures that a column of the database actually exists.
45
45
  #
46
46
  # == Options
47
- #
48
- # * All options available in migrations are available:
47
+ #
48
+ # * All options available in migrations are available:
49
49
  #
50
50
  # :type, :default, :precision, :limit, :scale, :sql_type, :primary, :null
51
51
  #
52
- # == Examples
53
- #
52
+ # == Examples
53
+ #
54
54
  # should_have_column :name, :type => :string, :default => ''
55
- #
55
+ #
56
56
  # it { should have_column(:name, :type => :string) }
57
- # it { should have_column(:name).type(:string) }
57
+ # it { should have_column(:name).type(:string) }
58
58
  #
59
59
  def have_column(*args)
60
60
  HaveColumnMatcher.new(*args).spec(self)
61
- end
62
- alias :have_columns :have_column
63
- alias :have_db_column :have_column
61
+ end
62
+ alias :have_columns :have_column
63
+ alias :have_db_column :have_column
64
64
  alias :have_db_columns :have_column
65
65
 
66
66
  end
@@ -2,14 +2,14 @@ module Remarkable
2
2
  module ActiveRecord
3
3
  module Matchers
4
4
  class HaveIndexMatcher < Remarkable::ActiveRecord::Base #:nodoc:
5
- arguments :collection => :columns, :as => :column
6
-
7
- optional :unique, :default => true
8
-
9
- collection_assertions :index_exists?, :is_unique?
10
-
11
- protected
12
-
5
+ arguments :collection => :columns, :as => :column
6
+
7
+ optional :unique, :default => true
8
+
9
+ collection_assertions :index_exists?, :is_unique?
10
+
11
+ protected
12
+
13
13
  def index_exists?
14
14
  !matched_index.nil?
15
15
  end
@@ -26,31 +26,31 @@ module Remarkable
26
26
 
27
27
  def indexes
28
28
  @indexes ||= ::ActiveRecord::Base.connection.indexes(subject_class.table_name)
29
- end
30
-
31
- def interpolation_options
32
- @subject ? { :table_name => subject_class.table_name } : {}
33
29
  end
34
30
 
35
- end
36
-
31
+ def interpolation_options
32
+ @subject ? { :table_name => subject_class.table_name } : {}
33
+ end
34
+
35
+ end
36
+
37
37
  # Ensures the database column has specified index.
38
38
  #
39
- # == Options
39
+ # == Options
40
40
  #
41
41
  # * <tt>unique</tt> - when supplied, tests if the index is unique or not
42
42
  #
43
- # == Examples
43
+ # == Examples
44
44
  #
45
- # it { should have_index(:ssn).unique(true) }
45
+ # it { should have_index(:ssn).unique(true) }
46
46
  # it { should have_index([:name, :email]).unique(true) }
47
47
  #
48
48
  def have_index(*args)
49
49
  HaveIndexMatcher.new(*args).spec(self)
50
50
  end
51
- alias :have_indices :have_index
52
- alias :have_db_index :have_index
53
- alias :have_db_indices :have_index
51
+ alias :have_indices :have_index
52
+ alias :have_db_index :have_index
53
+ alias :have_db_indices :have_index
54
54
 
55
55
  end
56
56
  end
@@ -1,9 +1,9 @@
1
1
  module Remarkable
2
2
  module ActiveRecord
3
3
  module Matchers
4
- class HaveReadonlyAttributesMatcher < Remarkable::ActiveRecord::Base #:nodoc:
4
+ class HaveReadonlyAttributesMatcher < Remarkable::ActiveRecord::Base #:nodoc:
5
5
  arguments :collection => :attributes, :as => :attribute
6
- collection_assertions :is_readonly?
6
+ collection_assertions :is_readonly?
7
7
 
8
8
  private
9
9
 
@@ -13,17 +13,17 @@ module Remarkable
13
13
  end
14
14
  end
15
15
 
16
- # Ensures that the attribute cannot be changed once the record has been
17
- # created.
18
- #
16
+ # Ensures that the attribute cannot be changed once the record has been
17
+ # created.
18
+ #
19
19
  # == Examples
20
20
  #
21
21
  # it { should have_readonly_attributes(:password, :admin_flag) }
22
22
  #
23
23
  def have_readonly_attributes(*attributes)
24
24
  HaveReadonlyAttributesMatcher.new(*attributes).spec(self)
25
- end
26
- alias :have_readonly_attribute :have_readonly_attributes
25
+ end
26
+ alias :have_readonly_attribute :have_readonly_attributes
27
27
 
28
28
  end
29
29
  end
@@ -1,48 +1,48 @@
1
1
  module Remarkable
2
2
  module ActiveRecord
3
3
  module Matchers
4
- class HaveScopeMatcher < Remarkable::ActiveRecord::Base #:nodoc:
5
- arguments :scope_name
6
- assertions :is_scope?, :options_match?
7
-
8
- optionals :with, :select, :conditions, :order, :limit, :offset
9
-
10
- protected
11
-
12
- def is_scope?
13
- @scope_object = if @options[:with]
14
- subject_class.send(@scope_name, *@options[:with])
15
- else
16
- subject_class.send(@scope_name)
17
- end
18
-
19
- @scope_object.class == ::ActiveRecord::NamedScope::Scope
20
- end
21
-
4
+ class HaveScopeMatcher < Remarkable::ActiveRecord::Base #:nodoc:
5
+ arguments :scope_name
6
+ assertions :is_scope?, :options_match?
7
+
8
+ optionals :with, :select, :conditions, :order, :limit, :offset
9
+
10
+ protected
11
+
12
+ def is_scope?
13
+ @scope_object = if @options[:with]
14
+ subject_class.send(@scope_name, *@options[:with])
15
+ else
16
+ subject_class.send(@scope_name)
17
+ end
18
+
19
+ @scope_object.class == ::ActiveRecord::NamedScope::Scope
20
+ end
21
+
22
22
  def options_match?
23
23
  @options.empty? || @scope_object.proxy_options == @options.except(:with)
24
24
  end
25
25
 
26
- def interpolation_options
27
- { :options => @options.except(:with).inspect,
28
- :actual => (@scope_object ? @scope_object.proxy_options.inspect : '{}')
29
- }
30
- end
26
+ def interpolation_options
27
+ { :options => @options.except(:with).inspect,
28
+ :actual => (@scope_object ? @scope_object.proxy_options.inspect : '{}')
29
+ }
30
+ end
31
31
 
32
32
  end
33
33
 
34
- # Ensures that the model has a method named scope that returns a NamedScope
34
+ # Ensures that the model has a method named scope that returns a NamedScope
35
35
  # object with the supplied proxy options.
36
36
  #
37
- # == Options
38
- #
39
- # * <tt>with</tt> - Options to be sent to the named scope
40
- #
41
- # And all other options that the named scope would pass on to find.
42
- #
37
+ # == Options
38
+ #
39
+ # * <tt>with</tt> - Options to be sent to the named scope
40
+ #
41
+ # And all other options that the named scope would pass on to find.
42
+ #
43
43
  # == Examples
44
44
  #
45
- # it { should have_scope(:visible, :conditions => {:visible => true}) }
45
+ # it { should have_scope(:visible, :conditions => {:visible => true}) }
46
46
  # it { should have_scope(:visible).conditions(:visible => true) }
47
47
  #
48
48
  # Passes for
@@ -60,7 +60,7 @@ module Remarkable
60
60
  # it { should have_scope(:recent, :with => 5) }
61
61
  # it { should have_scope(:recent, :with => 1) }
62
62
  #
63
- # Passes for
63
+ # Passes for
64
64
  #
65
65
  # named_scope :recent, lambda {|c| {:limit => c}}
66
66
  #
@@ -72,8 +72,8 @@ module Remarkable
72
72
  #
73
73
  def have_scope(*args)
74
74
  HaveScopeMatcher.new(*args).spec(self)
75
- end
76
- alias :have_named_scope :have_scope
75
+ end
76
+ alias :have_named_scope :have_scope
77
77
 
78
78
  end
79
79
  end
@@ -1,50 +1,50 @@
1
- module Remarkable
2
- module ActiveRecord
3
- module Matchers
4
- class ValidateAcceptanceOfMatcher < Remarkable::ActiveRecord::Base #:nodoc:
5
- arguments :collection => :attributes, :as => :attribute
6
-
7
- optional :accept, :message
8
- optional :allow_nil, :default => true
9
-
10
- collection_assertions :requires_acceptance?, :accept_is_valid?, :allow_nil?
11
-
12
- default_options :message => :accepted
13
-
14
- protected
15
-
1
+ module Remarkable
2
+ module ActiveRecord
3
+ module Matchers
4
+ class ValidateAcceptanceOfMatcher < Remarkable::ActiveRecord::Base #:nodoc:
5
+ arguments :collection => :attributes, :as => :attribute
6
+
7
+ optional :accept, :message
8
+ optional :allow_nil, :default => true
9
+
10
+ collection_assertions :requires_acceptance?, :accept_is_valid?, :allow_nil?
11
+
12
+ default_options :message => :accepted
13
+
14
+ protected
15
+
16
16
  def requires_acceptance?
17
17
  bad?(false)
18
- end
19
-
20
- def accept_is_valid?
21
- return true unless @options.key?(:accept)
22
- good?(@options[:accept])
23
- end
24
-
25
- end
26
-
18
+ end
19
+
20
+ def accept_is_valid?
21
+ return true unless @options.key?(:accept)
22
+ good?(@options[:accept])
23
+ end
24
+
25
+ end
26
+
27
27
  # Ensures that the model cannot be saved if one of the attributes listed is not accepted.
28
28
  #
29
- # == Options
30
- #
31
- # * <tt>:accept</tt> - the expected value to be accepted.
29
+ # == Options
30
+ #
31
+ # * <tt>:accept</tt> - the expected value to be accepted.
32
32
  # * <tt>:allow_nil</tt> - when supplied, validates if it allows nil or not.
33
33
  # * <tt>:message</tt> - value the test expects to find in <tt>errors.on(:attribute)</tt>.
34
34
  # Regexp, string or symbol. Default = <tt>I18n.translate('activerecord.errors.messages.accepted')</tt>
35
35
  #
36
- # == Examples
37
- #
38
- # should_validate_acceptance_of :eula, :terms
39
- # should_validate_acceptance_of :eula, :terms, :accept => true
36
+ # == Examples
37
+ #
38
+ # should_validate_acceptance_of :eula, :terms
39
+ # should_validate_acceptance_of :eula, :terms, :accept => true
40
40
  #
41
- # it { should validate_acceptance_of(:eula, :terms) }
41
+ # it { should validate_acceptance_of(:eula, :terms) }
42
42
  # it { should validate_acceptance_of(:eula, :terms, :accept => true) }
43
43
  #
44
44
  def validate_acceptance_of(*attributes)
45
45
  ValidateAcceptanceOfMatcher.new(*attributes).spec(self)
46
- end
47
-
48
- end
49
- end
50
- end
46
+ end
47
+
48
+ end
49
+ end
50
+ end
@@ -6,91 +6,91 @@ module Remarkable
6
6
  optional :message, :builder
7
7
 
8
8
  collection_assertions :find_association?, :is_valid?
9
- default_options :message => :invalid
10
-
11
- protected
12
-
13
- def find_association?
14
- reflection = @subject.class.reflect_on_association(@association)
15
-
16
- raise ScriptError, "Could not find association #{@association} on #{subject_class}." unless reflection
17
-
18
- associated_object = if builder = @options[:builder] || @block
19
- builder.call(@subject)
20
- elsif [:belongs_to, :has_one].include?(reflection.macro)
21
- @subject.send(:"build_#{@association}") rescue nil
22
- else
23
- @subject.send(@association).build rescue nil
24
- end
25
-
26
- raise ScriptError, "The association object #{@association} could not be built. You can give me " <<
27
- ":builder as option or a block which returns an association." unless associated_object
28
-
29
- raise ScriptError, "The associated object #{@association} is not invalid. You can give me " <<
30
- ":builder as option or a block which returns an invalid association." if associated_object.save
31
-
32
- return true
33
- end
34
-
35
- def is_valid?
36
- return false if @subject.valid?
37
-
38
- error_message_to_expect = error_message_from_model(@subject, :base, @options[:message])
39
-
40
- # In Rails 2.1.2, the error on association returns a symbol (:invalid)
41
- # instead of the message, so we check this case here.
9
+ default_options :message => :invalid
10
+
11
+ protected
12
+
13
+ def find_association?
14
+ reflection = @subject.class.reflect_on_association(@association)
15
+
16
+ raise ScriptError, "Could not find association #{@association} on #{subject_class}." unless reflection
17
+
18
+ associated_object = if builder = @options[:builder] || @block
19
+ builder.call(@subject)
20
+ elsif [:belongs_to, :has_one].include?(reflection.macro)
21
+ @subject.send(:"build_#{@association}") rescue nil
22
+ else
23
+ @subject.send(@association).build rescue nil
24
+ end
25
+
26
+ raise ScriptError, "The association object #{@association} could not be built. You can give me " <<
27
+ ":builder as option or a block which returns an association." unless associated_object
28
+
29
+ raise ScriptError, "The associated object #{@association} is not invalid. You can give me " <<
30
+ ":builder as option or a block which returns an invalid association." if associated_object.save
31
+
32
+ return true
33
+ end
34
+
35
+ def is_valid?
36
+ return false if @subject.valid?
37
+
38
+ error_message_to_expect = error_message_from_model(@subject, :base, @options[:message])
39
+
40
+ # In Rails 2.1.2, the error on association returns a symbol (:invalid)
41
+ # instead of the message, so we check this case here.
42
42
  @subject.errors.on(@association) == @options[:message] ||
43
- assert_contains(@subject.errors.on(@association), error_message_to_expect)
43
+ assert_contains(@subject.errors.on(@association), error_message_to_expect)
44
44
  end
45
45
  end
46
46
 
47
- # Ensures that the model is invalid if one of the associations given is
48
- # invalid. It tries to build the association automatically. In has_one
49
- # and belongs_to cases, it will build it like this:
50
- #
51
- # @model.build_association
52
- # @project.build_manager
53
- #
54
- # In has_many and has_and_belongs_to_many to cases it will build it like
55
- # this:
56
- #
57
- # @model.association.build
58
- # @project.tasks.build
59
- #
60
- # The object returned MUST be invalid and it's likely the case, since the
61
- # associated object is empty when calling build. However, if the associated
62
- # object has to be manipulated to be invalid, you will have to give :builder
63
- # as option or a block to manipulate it:
64
- #
65
- # should_validate_associated(:tasks) do |project|
66
- # project.tasks.build(:captcha => 'i_am_a_bot')
67
- # end
68
- #
69
- # In the case above, the associated object task is only invalid when the
70
- # captcha attribute is set. So we give a block to the matcher that tell
71
- # exactly how to build an invalid object.
72
- #
73
- # The example above can also be written as:
74
- #
75
- # should_validate_associated :tasks, :builder => proc{ |p| p.tasks.build(:captcha => 'i_am_a_bot') }
47
+ # Ensures that the model is invalid if one of the associations given is
48
+ # invalid. It tries to build the association automatically. In has_one
49
+ # and belongs_to cases, it will build it like this:
50
+ #
51
+ # @model.build_association
52
+ # @project.build_manager
53
+ #
54
+ # In has_many and has_and_belongs_to_many to cases it will build it like
55
+ # this:
56
+ #
57
+ # @model.association.build
58
+ # @project.tasks.build
59
+ #
60
+ # The object returned MUST be invalid and it's likely the case, since the
61
+ # associated object is empty when calling build. However, if the associated
62
+ # object has to be manipulated to be invalid, you will have to give :builder
63
+ # as option or a block to manipulate it:
64
+ #
65
+ # should_validate_associated(:tasks) do |project|
66
+ # project.tasks.build(:captcha => 'i_am_a_bot')
67
+ # end
68
+ #
69
+ # In the case above, the associated object task is only invalid when the
70
+ # captcha attribute is set. So we give a block to the matcher that tell
71
+ # exactly how to build an invalid object.
72
+ #
73
+ # The example above can also be written as:
74
+ #
75
+ # should_validate_associated :tasks, :builder => proc{ |p| p.tasks.build(:captcha => 'i_am_a_bot') }
76
+ #
77
+ # == Options
78
+ #
79
+ # * <tt>:builder</tt> - a proc to build the association
76
80
  #
77
- # == Options
78
- #
79
- # * <tt>:builder</tt> - a proc to build the association
80
- #
81
81
  # * <tt>:message</tt> - value the test expects to find in <tt>errors.on(:attribute)</tt>.
82
82
  # Regexp, string or symbol. Default = <tt>I18n.translate('activerecord.errors.messages.invalid')</tt>
83
83
  #
84
- # == Examples
85
- #
86
- # should_validate_associated :tasks
87
- # should_validate_associated(:tasks){ |p| p.tasks.build(:captcha => 'i_am_a_bot') }
88
- # should_validate_associated :tasks, :builder => proc{ |p| p.tasks.build(:captcha => 'i_am_a_bot') }
84
+ # == Examples
89
85
  #
90
- # it { should validate_associated(:tasks) }
91
- # it { should validate_associated(:tasks){ |p| p.tasks.build(:captcha => 'i_am_a_bot') } }
86
+ # should_validate_associated :tasks
87
+ # should_validate_associated(:tasks){ |p| p.tasks.build(:captcha => 'i_am_a_bot') }
88
+ # should_validate_associated :tasks, :builder => proc{ |p| p.tasks.build(:captcha => 'i_am_a_bot') }
89
+ #
90
+ # it { should validate_associated(:tasks) }
91
+ # it { should validate_associated(:tasks){ |p| p.tasks.build(:captcha => 'i_am_a_bot') } }
92
92
  # it { should validate_associated(:tasks, :builder => proc{ |p| p.tasks.build(:captcha => 'i_am_a_bot') }) }
93
- #
93
+ #
94
94
  def validate_associated(*args, &block)
95
95
  ValidateAssociatedMatcher.new(*args, &block).spec(self)
96
96
  end