accept_values_for 0.2.1 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/Readme.textile CHANGED
@@ -2,25 +2,67 @@ h1. Accept values for
2
2
 
3
3
  h2. Description
4
4
 
5
- In order to spec a complex validation for ActiveRecord models.
6
- Implemented accept_values_for custom matcher.
7
- With this matcher you can specify which values of model attribute should be accepted by model validation and which should not.
5
+ In order to spec ActiveRecord models.
6
+ I decided to write a few custom matchers that makes the work match easier:
8
7
 
9
8
 
10
- h2. Usage
9
+ h2. Matchers
11
10
 
12
- <pre>
13
- describe User do
11
+ * accept_values_for
12
+ * discover
14
13
 
15
- subject { User.new(@valid_attributes)}
16
-
14
+ h3. Accept values for
17
15
 
16
+ "Rpec matcher to test the validation":http://gusiev.com/2010/06/ultimate-rspec-matcher-to-test-validation/
18
17
 
19
- it { should accept_values_for(:email, "john@example.com", "lambda@gusiev.com") }
20
- it { should_not accept_values_for(:email, "invalid", nil, "a@b", "john@.com") }
18
+ <pre>
19
+ describe User do
21
20
 
21
+ subject { User.new(@valid_attributes)}
22
+
23
+
24
+
25
+ it { should accept_values_for(:email, "john@example.com", "lambda@gusiev.com") }
26
+ it { should_not accept_values_for(:email, "invalid", nil, "a@b", "john@.com") }
27
+
28
+ end</pre>
29
+
30
+ h3. Discovery matcher
31
+
32
+ "Rspec matcher to test named scopes":http://gusiev.com/2010/07/bdd-rspec-matcher-to-test-named_scope-scoped-rails
33
+
34
+
35
+ <pre><code>describe "#by_category_id named scope" do
36
+ let(:given_category) do
37
+ Factory.create(:given_category)
38
+ end
39
+
40
+
41
+ let(:product_in_given_category) do
42
+ Factory.create(
43
+ :product,
44
+ :categories => [category]
45
+ )
46
+ end
47
+
48
+ let(:product_not_in_given_category) do
49
+ Factory.create(
50
+ :product,
51
+ :categories => [Factory.create(:category)]
52
+ )
22
53
  end
23
- </pre>
54
+
55
+ # This might be tricky to redefine subject as the finder result
56
+ # but in this way we can delegate the matcher to subject and
57
+ # avoid writing test descriptions.
58
+ subject { described_class.by_category_id(given_category.id) }
59
+
60
+ it { should discover(product_in_given_category) }
61
+ it { should_not discover(product_not_in_given_category) }
62
+
63
+ end
64
+ </code></pre>
65
+
24
66
 
25
67
  h2. Dependencies
26
68
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.1
1
+ 0.3.0
@@ -0,0 +1,57 @@
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{accept_values_for}
8
+ s.version = "0.3.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Bogdan Gusiev"]
12
+ s.date = %q{2010-07-12}
13
+ s.description = %q{Rspec: When you have a complex validation(e.g. regexp or custom method) on ActiveRecord model
14
+ you have to write annoying easy specs on which values should be accepted by your validation method and which should not.
15
+ accepts_values_for rspec matcher simplify the code. See example for more information.
16
+ }
17
+ s.email = %q{agresso@gmail.com}
18
+ s.files = [
19
+ "Gemfile",
20
+ "Rakefile",
21
+ "Readme.textile",
22
+ "VERSION",
23
+ "accept_values_for.gemspec",
24
+ "lib/accept_values_for.rb",
25
+ "lib/discover.rb",
26
+ "spec/accept_values_for_spec.rb",
27
+ "spec/discover_spec.rb",
28
+ "spec/spec_helper.rb"
29
+ ]
30
+ s.homepage = %q{http://github.com/bogdan/accept_values_for}
31
+ s.rdoc_options = ["--charset=UTF-8"]
32
+ s.require_paths = ["lib"]
33
+ s.rubygems_version = %q{1.3.6}
34
+ s.summary = %q{In order to test a complex validation for ActiveRecord models Implemented accept_values_for custom rspec matcher}
35
+ s.test_files = [
36
+ "spec/discover_spec.rb",
37
+ "spec/accept_values_for_spec.rb",
38
+ "spec/spec_helper.rb"
39
+ ]
40
+
41
+ if s.respond_to? :specification_version then
42
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
43
+ s.specification_version = 3
44
+
45
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
46
+ s.add_runtime_dependency(%q<activerecord>, [">= 0"])
47
+ s.add_runtime_dependency(%q<rspec>, [">= 0"])
48
+ else
49
+ s.add_dependency(%q<activerecord>, [">= 0"])
50
+ s.add_dependency(%q<rspec>, [">= 0"])
51
+ end
52
+ else
53
+ s.add_dependency(%q<activerecord>, [">= 0"])
54
+ s.add_dependency(%q<rspec>, [">= 0"])
55
+ end
56
+ end
57
+
data/lib/discover.rb ADDED
@@ -0,0 +1,127 @@
1
+ if defined?(ActiveRecord)
2
+
3
+ # In order to spec ActiveRecord finders
4
+ # Implemented discover custom matcher
5
+ #
6
+ # :call-seq:
7
+ # Class.named_scope.should discover(model1, model2)
8
+ # Class.named_scope.should_not discover(model3, model4)
9
+ #
10
+ # matcher subject should be an instance of ActiverRecord::NamedScope::Scoped or Array
11
+ # models should be an instace of ActiverRecord::Base
12
+ #
13
+ # Use this if you need to check the inclution of objects ActiveRecord finders result
14
+ # and it's order
15
+ #
16
+ # == Examples
17
+ #
18
+ # class Group < AR::Base
19
+ # named_scope :approved, :conditions => "approved = true"
20
+ # named_scope :order_by_name, :order => "name"
21
+ # end
22
+ #
23
+ # Group.approved.should discover(Group.create!(:approved => true))
24
+ # Group.approved.should_not discover(Group.create!(:approved => false))
25
+ # Group.order_by_name.should discover(Group.create!(:name => "apple"), Group.create!(:name => "bear").with_exact_order)
26
+ #
27
+ #
28
+ def discover(*objects)
29
+ Rspec::Discover.new(*objects)
30
+ end
31
+
32
+ end
33
+
34
+ module Rspec
35
+ class Discover #:nodoc:
36
+
37
+ def initialize(*objects)
38
+ @objects = objects
39
+ end
40
+
41
+ def with_exact_order
42
+ @with_exact_order = true
43
+ self
44
+ end
45
+
46
+ def matches?(scope)
47
+ @scope = scope
48
+ return valid_findness? && valid_indexes?
49
+ end
50
+
51
+ def does_not_match?(scope)
52
+ @scope = scope
53
+ if @with_exact_order
54
+ raise "calling #with_exact_order is not allowed with should_not match"
55
+ end
56
+ return valid_not_findness?
57
+ end
58
+
59
+ def failure_message_for_should
60
+ result = @not_found_object_ids.any? ?
61
+ "expected #{@scope.inspect} to include objects: #{@not_found_object_ids.inspect}" :
62
+ "expected #{@scope.inspect} to be ordered as: #{@objects.map(&:id).inspect}"
63
+ result += ", but it was not. "
64
+ if @not_found_object_ids.any?
65
+ result += found_objects_string
66
+ end
67
+ result
68
+ end
69
+
70
+ def failure_message_for_should_not
71
+ result = "expected #{@scope.inspect} to not include objects: #{@found_object_ids.inspect}"
72
+ result += ", but it was. "
73
+ result += found_objects_string
74
+ result
75
+ end
76
+
77
+
78
+ def description
79
+ "discover #{@objects.map(&:inspect).join(', ')} " + ordering_string
80
+ end
81
+
82
+ protected
83
+
84
+ def valid_findness?
85
+ result = true
86
+ @not_found_object_ids = []
87
+ @objects.each do |object|
88
+ unless @scope.include?(object)
89
+ @not_found_object_ids << object.id
90
+ result = false
91
+ end
92
+ end
93
+ result
94
+ end
95
+
96
+
97
+ def valid_indexes?
98
+ return true unless @with_exact_order
99
+ indexes = @objects.map{|o| @scope.index(o)}
100
+ return indexes.sort == indexes
101
+ end
102
+
103
+ def valid_not_findness?
104
+ result = true
105
+ @found_object_ids = []
106
+ @objects.each do |object|
107
+ if @scope.include?(object)
108
+ @found_object_ids << object.id
109
+ result = false
110
+ end
111
+ end
112
+ result
113
+ end
114
+
115
+ def found_objects_string
116
+ "Found objects: " + @scope.map(&:id).inspect
117
+ end
118
+
119
+
120
+ def ordering_string
121
+ @with_exact_order ? "with exact order" : ""
122
+ end
123
+
124
+ end
125
+ end
126
+
127
+
@@ -0,0 +1,68 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Discover" do
4
+
5
+
6
+ let(:named_scope) {
7
+ Group.by_char("j")
8
+ }
9
+ let(:java) { Group.create!(:name => "java") }
10
+ let(:json) { Group.create!(:name => "json") }
11
+ let(:jruby) { Group.create!(:name => "jruby") }
12
+ let(:ruby) { Group.create!(:name => "ruby")}
13
+ let(:python) { Group.create!(:name => "python")}
14
+
15
+ describe "#does_not_match?" do
16
+
17
+ context "if scope contains any of the specified values" do
18
+ subject { discover(java, ruby) }
19
+ before(:each) do
20
+ subject.does_not_match?(named_scope).should be_false
21
+ end
22
+
23
+ it { subject.failure_message_for_should_not.should == "expected #{named_scope.inspect} to not include objects: #{[java.id].inspect}, but it was. Found objects: #{named_scope.map(&:id).inspect}"}
24
+
25
+ end
26
+
27
+ end
28
+
29
+ describe "#matches?" do
30
+ context "if scope doesn't contain any of the specified objects" do
31
+ subject { discover(java, ruby) }
32
+ before(:each) do
33
+ subject.matches?(named_scope).should be_false
34
+ end
35
+
36
+ it {subject.failure_message_for_should.should == "expected #{named_scope.inspect} to include objects: #{[ruby.id].inspect}, but it was not. Found objects: #{named_scope.map(&:id).inspect}"}
37
+ end
38
+
39
+ context "if scope contain all of the specified objects but without correct order" do
40
+ subject { discover(jruby, java).with_exact_order }
41
+ before(:each) do
42
+ subject.matches?(named_scope).should be_false
43
+ end
44
+
45
+ it "should render error message correctly" do
46
+ subject.failure_message_for_should.should == "expected #{named_scope.inspect} to be ordered as: #{[jruby.id, java.id].inspect}, but it was not. "
47
+ end
48
+ end
49
+ end
50
+
51
+ describe "api" do
52
+ subject {
53
+ named_scope
54
+ }
55
+
56
+ it { should discover(java) }
57
+
58
+ it { should discover(json, java, jruby) }
59
+ it { should discover(java, jruby, json).with_exact_order }
60
+ it { should discover(java, json).with_exact_order }
61
+
62
+ it { should_not discover(ruby) }
63
+ it { should_not discover(ruby, python) }
64
+
65
+ end
66
+
67
+
68
+ end
data/spec/spec_helper.rb CHANGED
@@ -3,6 +3,7 @@ require 'spec'
3
3
  require 'spec/autorun'
4
4
  require 'active_record'
5
5
  require 'lib/accept_values_for'
6
+ require 'lib/discover'
6
7
 
7
8
  ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :database => ":memory:")
8
9
  ActiveRecord::Base.configurations = true
@@ -13,6 +14,7 @@ ActiveRecord::Schema.define(:version => 1) do
13
14
  create_table :people do |t|
14
15
  t.string :gender
15
16
  t.integer :group_id
17
+ t.string :name
16
18
  end
17
19
 
18
20
  create_table :groups do |t|
@@ -23,7 +25,14 @@ end
23
25
  Spec::Runner.configure do |config|
24
26
  config.before(:each) do
25
27
  class ::Group < ActiveRecord::Base
26
-
28
+ has_many :people
29
+
30
+ named_scope :by_char, lambda { |char|
31
+ {
32
+ :conditions => ["name like ?", char + "%"],
33
+ :order => "name"
34
+ }
35
+ }
27
36
  end
28
37
 
29
38
  class ::Person < ActiveRecord::Base
metadata CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
4
4
  prerelease: false
5
5
  segments:
6
6
  - 0
7
- - 2
8
- - 1
9
- version: 0.2.1
7
+ - 3
8
+ - 0
9
+ version: 0.3.0
10
10
  platform: ruby
11
11
  authors:
12
12
  - Bogdan Gusiev
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2010-06-29 00:00:00 +03:00
17
+ date: 2010-07-12 00:00:00 +03:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
@@ -58,8 +58,11 @@ files:
58
58
  - Rakefile
59
59
  - Readme.textile
60
60
  - VERSION
61
+ - accept_values_for.gemspec
61
62
  - lib/accept_values_for.rb
63
+ - lib/discover.rb
62
64
  - spec/accept_values_for_spec.rb
65
+ - spec/discover_spec.rb
63
66
  - spec/spec_helper.rb
64
67
  has_rdoc: true
65
68
  homepage: http://github.com/bogdan/accept_values_for
@@ -92,5 +95,6 @@ signing_key:
92
95
  specification_version: 3
93
96
  summary: In order to test a complex validation for ActiveRecord models Implemented accept_values_for custom rspec matcher
94
97
  test_files:
98
+ - spec/discover_spec.rb
95
99
  - spec/accept_values_for_spec.rb
96
100
  - spec/spec_helper.rb