remarkable_activerecord 3.0.1 → 3.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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