benhutton-remarkable_activerecord 4.0.0.alpha6

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG ADDED
@@ -0,0 +1,147 @@
1
+ * allow_values_for checks for any message unless otherwise given [#90]
2
+
3
+ * Allow booleans to be given to validate_inclusion_of [#89]
4
+
5
+ * Allow allow_values_for and allow_mass_assignment_of matchers to be executed in
6
+ the negative form by including Remarkable::Negative module [#85]
7
+
8
+ * Ensure quick subject bypass protected attributes [#87]
9
+
10
+ * Added :token and :separator to deal with :tokenizer in validates_length_of [#77]
11
+
12
+ * Deprecated validate_format_of. It does not have the same API as the respective
13
+ ActiveRecord macro, raising questions frequentely about its usage [#76]
14
+
15
+ * allow_mass_assignment_of when called without arguments checks if any mass
16
+ assignment is possible [#80]
17
+
18
+ * Add :table_name option to have_index (thanks to Lawrence Pit) [#79]
19
+
20
+ * Allow default subject attributes to be given [#74]
21
+ You can even mix with a fixture replacement tool and still use quick subjects:
22
+
23
+ describe Post
24
+ # Fixjour example
25
+ subject_attributes { valid_post_attributes }
26
+
27
+ describe :published => true do
28
+ should_validate_presence_of :published_at
29
+ end
30
+ end
31
+
32
+ * Bug fix when a symbol is given has join table to habtm association [#75]
33
+
34
+ * Association matchers now searches in the right database for tables [#73]
35
+
36
+ * validate_length_of accepts :with_kind_of to enable it to work with associations [#69]
37
+ In your Post specs now you can write:
38
+
39
+ should_validate_length_of :comments, :maximum => 10, :with_kind_of => Comment
40
+
41
+ # v3.1
42
+
43
+ * Allow validate_presence_of to work with associations [#63]
44
+
45
+ * Allow validate_uniqueness_of to work when scopes are not stringfiable values.
46
+ You can now give timestamps, datetime, date and boolean as scopes [#60]
47
+
48
+ * Allow subjects to be overwriten quickly (quick subjects):
49
+
50
+ describe Post
51
+ should_validate_presente_of :title
52
+
53
+ describe :published => true do
54
+ should_validate_presence_of :published_at
55
+ end
56
+ end
57
+
58
+ Is the same as:
59
+
60
+ describe Post
61
+ should_validate_presente_of :title
62
+
63
+ describe "when published is true" do
64
+ subject { Post.new(:published => true) }
65
+ should_validate_presence_of :published_at
66
+ end
67
+ end
68
+
69
+ And the string can be also localized using I18n [#57]
70
+
71
+ [COMPATIBILITY] validate_associated no longer accepts a block to confgure the
72
+ builder:
73
+
74
+ should_validate_associated(:tasks){ |p| p.tasks.build(:captcha => 'i_am_a_robot') }
75
+
76
+ The right way to do this is by giving an option called builder and a proc:
77
+
78
+ should_validate_associated :tasks, :builder => proc{ |p| p.tasks.build(:captcha => 'i_am_a_robot') }
79
+
80
+ * validate_uniqueness_of and accept_nested_attributes_for now use the new
81
+ interpolation option {{sentence}} [#58]
82
+
83
+ * Added accept_nested_attributes_for matcher [#39]
84
+
85
+ * Added have_default_scope matcher [#38]
86
+
87
+ * Allow :conditions, :include, :joins, :limit, :offset, :order, :select, :readonly,
88
+ :group, :having, :from, :lock as quick accessors to have_scope matcher
89
+
90
+ * Allow all kind of objects to be sent to have_scope (including datetimes, arrays,
91
+ booleans and nil) (thanks to Szymon Nowak and Nolan Eakins) [#53]
92
+
93
+ * Added support to sql options in association_matcher: select, conditions, include,
94
+ group, having, order, limit and offset, plus finder_sql and counter_sql. [#48]
95
+
96
+ * :source and :source_type are now supported by association matcher [#47]
97
+
98
+ * validate_inclusion_of became smarter since it now tests invalid values too [#36]
99
+
100
+ * Fixed three bugs in validate_uniqueness_of matcher [#42] [#40] [#37]
101
+
102
+ # v3.0
103
+
104
+ * Added more options to associations matcher. Previously it was handling just
105
+ :dependent and :through options. Now it deals with:
106
+
107
+ :through, :class_name, :foreign_key, :dependent, :join_table, :uniq,
108
+ :readonly, :validate, :autosave, :counter_cache, :polymorphic
109
+
110
+ And they are much smarter! In :join_table and :through cases, they also test if
111
+ the table exists or not. :counter_cache and :foreign_key also checks if the
112
+ column exists or not.
113
+
114
+ [COMPATIBILITY] Removed callback, have_instance_method and have_class_method
115
+ matchers. They don't lead to a good TDD since you should test they behavior
116
+ and not wether they exist or not.
117
+
118
+ [COMPATIBILITY] ActiveRecord matches does not pick the instance variable from
119
+ the spec environment. So we should target only rspec versions that supports
120
+ subjects (>= 1.1.12).
121
+
122
+ Previously, when we are doing this:
123
+
124
+ describe Product
125
+ before(:each){ @product = Product.new(:tangible => true) }
126
+ should_validate_presence_of :size
127
+ end
128
+
129
+ It was validating the @product instance variable. However this might be not
130
+ clear. The right way to do that (with subjects) is:
131
+
132
+ describe Product
133
+ subject{ Product.new(:tangible => true) }
134
+ should_validate_presence_of :size
135
+ end
136
+
137
+ Is also valid to remember that previous versions of Remarkable were overriding
138
+ subject definitions on rspec. This was also fixed.
139
+
140
+ # v2.x
141
+
142
+ * Added associations, allow_mass_assignment, allow_values_for, have_column,
143
+ have_index, have_scope, have_readonly_attributes, validate_acceptance_of,
144
+ validate_associated, validate_confirmation_of, validate_exclusion_of,
145
+ validate_inclusion_of, validate_length_of, validate_numericality_of,
146
+ validate_presence_of and validate_uniqueness_of matchers.
147
+
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Carlos Brando
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README ADDED
@@ -0,0 +1,103 @@
1
+ = Remarkable ActiveRecord
2
+
3
+ Remarkable ActiveRecord is a collection of matchers to ActiveRecord. Why use
4
+ Remarkable?
5
+
6
+ * Matchers for all ActiveRecord validations, with support to all options. The only
7
+ exceptions are validate_format_of (which should be invoked as allow_values_for)
8
+ and the :on option;
9
+
10
+ * Matchers for all ActiveRecord associations. The only one which supports all
11
+ these options:
12
+
13
+ :through, :source, :source_type, :class_name, :foreign_key, :dependent,
14
+ :join_table, :uniq, :readonly, :validate, :autosave, :counter_cache, :polymorphic
15
+
16
+ Plus Arel scopes:
17
+
18
+ :select, :where, :include, :group, :having, :order, :limit, :offset
19
+
20
+ Besides in Remarkable 3.0 matchers became much smarter. Whenever :join_table
21
+ or :through is given as option, it checks if the given table exists. Whenever
22
+ :foreign_key or :counter_cache is given, it checks if the given column exists;
23
+
24
+ * Tests and more tests. We have a huge tests suite ready to run and tested in
25
+ ActiveRecord 2.1.2, 2.2.2 and 2.3.2;
26
+
27
+ * Great documentation;
28
+
29
+ * I18n.
30
+
31
+ == Installation
32
+
33
+ sudo gem install remarkable_activerecord --pre
34
+
35
+ Add this to your Gemfile:
36
+
37
+ group :test do
38
+ gem 'remarkable_activerecord', '>=4.0.0.alpha2'
39
+ end
40
+
41
+ Drop this into your spec/spec_helper.rb or spec/support/remarkable.rb file:
42
+
43
+ require 'remarkable/active_record'
44
+
45
+ This will also automatically load remarkable/active_model and add those matchers as well.
46
+
47
+ == Examples
48
+
49
+ All Remarkable macros can be accessed in two different ways. For those who prefer the Shoulda style, let’s look at some model tests:
50
+
51
+ describe Post do
52
+ should_belong_to :user
53
+ should_have_many :comments
54
+ should_have_and_belong_to_many :tags
55
+
56
+ should_validate_presence_of :body
57
+ should_validate_presence_of :title
58
+ should_validate_uniqueness_of :title, :allow_blank => true
59
+ end
60
+
61
+ For those who likes more the RSpec way can simply do:
62
+
63
+ describe Post do
64
+ it { should belong_to(:user) }
65
+ it { should have_many(:comments) }
66
+ it { should have_and_belong_to_many(:tags) }
67
+
68
+ it { should validate_presence_of(:body) }
69
+ it { should validate_presence_of(:title) }
70
+ it { should validate_uniqueness_of(:title, :allow_blank => true) }
71
+ end
72
+
73
+ == I18n
74
+
75
+ All matchers come with I18n support. If you want to translate your matchers,
76
+ grab make a copy of locale/en.yml and start to translate it.
77
+
78
+ Then add your new locale file to Remarkable in spec/support/remarkable.rb:
79
+
80
+ Remarkable.add_locale 'path/to/my_locale.yml'
81
+
82
+ And then:
83
+
84
+ Remarkable.locale = :my_locale
85
+
86
+ == Using it outside Rails
87
+
88
+ If you want to use Remarkable ActiveRecord outside Rails, you have to remember
89
+ a few things:
90
+
91
+ 1. Internationalization is powered by the I18n gem. If you are using it with Rails,
92
+ it will use the built in gem, otherwise you will have to install the gem by hand:
93
+
94
+ gem sources -a http://gems.github.com
95
+ sudo gem install svenfuchs-i18n
96
+
97
+ 2. Include the matchers. Remarkable Rails gem is the responsable to add
98
+ ActiveRecord matchers to rspec. If you are not using it, you have to do:
99
+
100
+ Remarkable.include_matchers!(Remarkable::ActiveRecord, RSpec::Core::ExampleGroup)
101
+
102
+ This will make ActiveRecord matchers available in all rspec example groups.
103
+
@@ -0,0 +1,66 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{benhutton-remarkable_activerecord}
8
+ s.version = "4.0.0.alpha6"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new("> 1.3.1") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Ho-Sheng Hsiao", "Carlos Brando", "José Valim", "Diego Carrion"]
12
+ s.date = %q{2010-11-15}
13
+ s.description = %q{Remarkable ActiveRecord: collection of matchers and macros with I18n for ActiveRecord}
14
+ s.email = ["hosh@sparkfly.com", "eduardobrando@gmail.com", "jose.valim@gmail.com", "dc.rec1@gmail.com"]
15
+ s.extra_rdoc_files = [
16
+ "CHANGELOG",
17
+ "LICENSE",
18
+ "README"
19
+ ]
20
+ s.files = [
21
+ "CHANGELOG",
22
+ "LICENSE",
23
+ "README",
24
+ "benhutton-remarkable_activerecord.gemspec",
25
+ "lib/remarkable/active_record.rb",
26
+ "lib/remarkable/active_record/base.rb",
27
+ "lib/remarkable/active_record/matchers/accept_nested_attributes_for_matcher.rb",
28
+ "lib/remarkable/active_record/matchers/allow_mass_assignment_of_matcher.rb",
29
+ "lib/remarkable/active_record/matchers/association_matcher.rb",
30
+ "lib/remarkable/active_record/matchers/have_column_matcher.rb",
31
+ "lib/remarkable/active_record/matchers/have_default_scope_matcher.rb",
32
+ "lib/remarkable/active_record/matchers/have_index_matcher.rb",
33
+ "lib/remarkable/active_record/matchers/have_readonly_attributes_matcher.rb",
34
+ "lib/remarkable/active_record/matchers/have_scope_matcher.rb",
35
+ "lib/remarkable/active_record/matchers/validate_associated_matcher.rb",
36
+ "lib/remarkable/active_record/matchers/validate_uniqueness_of_matcher.rb",
37
+ "locale/en.yml",
38
+ "remarkable_activerecord.gemspec"
39
+ ]
40
+ s.homepage = %q{http://github.com/benhutton/remarkable}
41
+ s.rdoc_options = ["--charset=UTF-8"]
42
+ s.require_paths = ["lib"]
43
+ s.rubyforge_project = %q{remarkable}
44
+ s.rubygems_version = %q{1.3.7}
45
+ s.summary = %q{Remarkable ActiveRecord: collection of matchers and macros with I18n for ActiveRecord}
46
+
47
+ if s.respond_to? :specification_version then
48
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
49
+ s.specification_version = 3
50
+
51
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
52
+ s.add_runtime_dependency(%q<rspec>, [">= 2.0.0"])
53
+ s.add_runtime_dependency(%q<remarkable>, ["~> 4.0.0.alpha4"])
54
+ s.add_runtime_dependency(%q<remarkable_activemodel>, ["~> 4.0.0.alpha4"])
55
+ else
56
+ s.add_dependency(%q<rspec>, [">= 2.0.0"])
57
+ s.add_dependency(%q<remarkable>, ["~> 4.0.0.alpha4"])
58
+ s.add_dependency(%q<remarkable_activemodel>, ["~> 4.0.0.alpha4"])
59
+ end
60
+ else
61
+ s.add_dependency(%q<rspec>, [">= 2.0.0"])
62
+ s.add_dependency(%q<remarkable>, ["~> 4.0.0.alpha4"])
63
+ s.add_dependency(%q<remarkable_activemodel>, ["~> 4.0.0.alpha4"])
64
+ end
65
+ end
66
+
@@ -0,0 +1,14 @@
1
+ # Load Remarkable
2
+ require 'remarkable/active_model'
3
+ require 'remarkable/active_record/base'
4
+
5
+ # Add default locale
6
+ dir = File.dirname(__FILE__)
7
+ Dir["#{dir}/../../locale/*yml"].each {|f| Remarkable.add_locale(f) }
8
+
9
+ # Add matchers
10
+ Dir[File.join(dir, 'active_record', 'matchers', '*.rb')].each do |file|
11
+ require file
12
+ end
13
+
14
+ Remarkable.include_matchers!(Remarkable::ActiveRecord, RSpec::Core::ExampleGroup)
@@ -0,0 +1,6 @@
1
+ module Remarkable
2
+ module ActiveRecord
3
+ class Base < Remarkable::ActiveModel::Base
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,138 @@
1
+ module Remarkable
2
+ module ActiveRecord
3
+ module Matchers
4
+ class AcceptNestedAttributesForMatcher < Remarkable::ActiveRecord::Base #:nodoc:
5
+ arguments :collection => :associations, :as => :association
6
+
7
+ collection_assertions :association_exists?, :is_autosave?, :responds_to_attributes?,
8
+ :allows_destroy?, :accepts?, :rejects?
9
+
10
+ optionals :allow_destroy, :default => true
11
+ optionals :accept, :reject, :splat => true
12
+
13
+ protected
14
+
15
+ def association_exists?
16
+ reflection
17
+ end
18
+
19
+ def is_autosave?
20
+ reflection.options[:autosave] == true
21
+ end
22
+
23
+ def responds_to_attributes?
24
+ @subject.respond_to?(:"#{@association}_attributes=", true)
25
+ end
26
+
27
+ def allows_destroy?
28
+ return true unless @options.key?(:allow_destroy)
29
+
30
+ @subject.instance_eval <<-ALLOW_DESTROY
31
+ def assign_nested_attributes_for_#{reflection_type}_association(association, *args)
32
+ if self.respond_to?(:nested_attributes_options)
33
+ self.nested_attributes_options[association.to_sym][:allow_destroy]
34
+ else
35
+ args.last
36
+ end
37
+ end
38
+ ALLOW_DESTROY
39
+
40
+ actual = @subject.send(:"#{@association}_attributes=", {})
41
+ return actual == @options[:allow_destroy], :actual => actual
42
+ end
43
+
44
+ def accepts?
45
+ return true unless @options.key?(:accept)
46
+
47
+ [@options[:accept]].flatten.each do |attributes|
48
+ return false, :attributes => attributes.inspect if reject_if_proc.call(attributes)
49
+ end
50
+
51
+ true
52
+ end
53
+
54
+ def rejects?
55
+ return true unless @options.key?(:reject)
56
+
57
+ [@options[:reject]].flatten.each do |attributes|
58
+ return false, :attributes => attributes.inspect unless reject_if_proc.call(attributes)
59
+ end
60
+
61
+ true
62
+ end
63
+
64
+ private
65
+
66
+ def reflection
67
+ @reflection ||= subject_class.reflect_on_association(@association.to_sym)
68
+ end
69
+
70
+ def reflection_type
71
+ case reflection.macro
72
+ when :has_one, :belongs_to
73
+ :one_to_one
74
+ when :has_many, :has_and_belongs_to_many
75
+ :collection
76
+ end
77
+ end
78
+
79
+ def reject_if_proc
80
+ if subject_class.respond_to?(:nested_attributes_options)
81
+ subject_class.nested_attributes_options[@association.to_sym][:reject_if]
82
+ else
83
+ subject_class.reject_new_nested_attributes_procs[@association.to_sym]
84
+ end
85
+ end
86
+
87
+ end
88
+
89
+ # Ensures that the model accepts nested attributes for the given associations.
90
+ #
91
+ # == Options
92
+ #
93
+ # * <tt>allow_destroy</tt> - When true allows the association to be destroyed
94
+ # * <tt>accept</tt> - attributes that should be accepted by the :reject_if proc
95
+ # * <tt>reject</tt> - attributes that should be rejected by the :reject_if proc
96
+ #
97
+ # == Examples
98
+ #
99
+ # should_accept_nested_attributes_for :tasks
100
+ # should_accept_nested_attributes_for :tasks, :allow_destroy => true
101
+ #
102
+ # :accept and :reject takes objects that are verified against the proc. So
103
+ # having a model:
104
+ #
105
+ # class Projects < ActiveRecord::Base
106
+ # has_many :tasks
107
+ # accepts_nested_attributes_for :tasks, :reject_if => proc { |a| a[:title].blank? }
108
+ # end
109
+ #
110
+ # You can have the following specs:
111
+ #
112
+ # should_accept_nested_attributes_for :tasks, :reject => { :title => '' } # Passes
113
+ # should_accept_nested_attributes_for :tasks, :accept => { :title => 'My task' } # Passes
114
+ #
115
+ # should_accept_nested_attributes_for :tasks, :accept => { :title => 'My task' },
116
+ # :reject => { :title => '' } # Passes
117
+ #
118
+ # should_accept_nested_attributes_for :tasks, :accept => { :title => '' } # Fail
119
+ # should_accept_nested_attributes_for :tasks, :reject => { :title => 'My task' } # Fail
120
+ #
121
+ # You can also give arrays to :accept and :reject to verify multiple attributes.
122
+ # In such cases the block syntax is more recommended for readability:
123
+ #
124
+ # should_accept_nested_attributes_for :tasks do
125
+ # m.allow_destroy(false)
126
+ # m.accept :title => 'My task'
127
+ # m.accept :title => 'Another task'
128
+ # m.reject :title => nil
129
+ # m.reject :title => ''
130
+ # end
131
+ #
132
+ def accept_nested_attributes_for(*args, &block)
133
+ AcceptNestedAttributesForMatcher.new(*args, &block).spec(self)
134
+ end
135
+
136
+ end
137
+ end
138
+ end