dm-spec 0.9.10
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +20 -0
- data/README.textile +35 -0
- data/Rakefile +54 -0
- data/TODO +133 -0
- data/dm-spec.gemspec +23 -0
- data/lib/dm-spec.rb +5 -0
- data/lib/spec/matchers/dm/belongs_to.rb +42 -0
- data/lib/spec/matchers/dm/has_and_belongs_to_many.rb +38 -0
- data/lib/spec/matchers/dm/has_many.rb +48 -0
- data/lib/spec/matchers/dm/has_property.rb +35 -0
- data/lib/spec/matchers/dm/has_timestamps.rb +55 -0
- data/spec/dm/belongs_to_spec.rb +28 -0
- data/spec/dm/has_and_belongs_to_many_spec.rb +29 -0
- data/spec/dm/has_many_spec.rb +0 -0
- data/spec/dm/has_property_spec.rb +0 -0
- data/spec/dm/has_timestamps_spec.rb +28 -0
- data/spec/spec.opts +5 -0
- data/spec/spec_helper.rb +108 -0
- metadata +80 -0
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Jakub Stastny aka Botanicus
|
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.textile
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
h1. About
|
2
|
+
|
3
|
+
RSpec matchers for DataMapper.
|
4
|
+
|
5
|
+
h1. Usage
|
6
|
+
|
7
|
+
You must include DataMapperMatchers to your specs. It can be done in *spec/spec_helper.rb*:
|
8
|
+
|
9
|
+
<pre>
|
10
|
+
$: << File.dirname(__FILE__) + "/../lib"
|
11
|
+
require "dm-core"
|
12
|
+
require "dm-spec"
|
13
|
+
|
14
|
+
DataMapper.setup(:default, "sqlite3::memory")
|
15
|
+
|
16
|
+
Spec::Runner.configure do |config|
|
17
|
+
config.include(DataMapperMatchers)
|
18
|
+
config.before(:each) do
|
19
|
+
DataMapper.auto_migrate!
|
20
|
+
end
|
21
|
+
end
|
22
|
+
</pre>
|
23
|
+
|
24
|
+
That's it. Now just use these matchers in your specs. All the matchers you should call on model class, not on model instance.
|
25
|
+
|
26
|
+
<pre>
|
27
|
+
Post.should has_property(:title)
|
28
|
+
Post.should has_many(:comments)
|
29
|
+
Post.should has_and_belongs_to_many(:tags)
|
30
|
+
Post.should belongs_to(:category)
|
31
|
+
Post.should has_timestamps # created_at, updated_at
|
32
|
+
Post.should has_timestamps(:at) # created_at, updated_at
|
33
|
+
Post.should has_timestamps(:on) # created_on, updated_on
|
34
|
+
Post.should has_timestamps(:created_at)
|
35
|
+
</pre>
|
data/Rakefile
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake/gempackagetask'
|
3
|
+
|
4
|
+
GEM_NAME = "dm-spec"
|
5
|
+
AUTHOR = "Jakub Stastny aka Botanicus"
|
6
|
+
EMAIL = "knava.bestvinensis@gmail.com"
|
7
|
+
HOMEPAGE = "http://101Ideas.cz/"
|
8
|
+
SUMMARY = "RSpec matchers for DataMapper."
|
9
|
+
GEM_VERSION = "0.9.10"
|
10
|
+
|
11
|
+
spec = Gem::Specification.new do |s|
|
12
|
+
s.name = GEM_NAME
|
13
|
+
s.version = GEM_VERSION
|
14
|
+
s.platform = Gem::Platform::RUBY
|
15
|
+
s.has_rdoc = true
|
16
|
+
s.extra_rdoc_files = ["README.textile", "LICENSE"]
|
17
|
+
s.summary = SUMMARY
|
18
|
+
s.description = s.summary
|
19
|
+
s.author = AUTHOR
|
20
|
+
s.email = EMAIL
|
21
|
+
s.homepage = HOMEPAGE
|
22
|
+
s.require_path = 'lib'
|
23
|
+
s.files = %w(LICENSE README.textile Rakefile) + Dir.glob("{lib,spec,app,public,stubs}/**/*")
|
24
|
+
end
|
25
|
+
|
26
|
+
Rake::GemPackageTask.new(spec) do |pkg|
|
27
|
+
pkg.gem_spec = spec
|
28
|
+
end
|
29
|
+
|
30
|
+
desc "Create a gemspec file"
|
31
|
+
task :gemspec do
|
32
|
+
File.open("#{GEM_NAME}.gemspec", "w") do |file|
|
33
|
+
file.puts spec.to_ruby
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def sudo_gem(cmd)
|
38
|
+
sh "#{SUDO} #{RUBY} -S gem #{cmd}", :verbose => false
|
39
|
+
end
|
40
|
+
|
41
|
+
desc "Install #{GEM_NAME} #{GEM_VERSION}"
|
42
|
+
task :install => [ :package ] do
|
43
|
+
sudo_gem "install --local pkg/#{GEM_NAME}-#{GEM_VERSION} --no-update-sources"
|
44
|
+
end
|
45
|
+
|
46
|
+
desc "Uninstall #{GEM_NAME} #{GEM_VERSION}"
|
47
|
+
task :uninstall => [ :clobber ] do
|
48
|
+
sudo_gem "uninstall #{GEM_NAME} -v#{GEM_VERSION} -Ix"
|
49
|
+
end
|
50
|
+
|
51
|
+
require 'spec/rake/spectask'
|
52
|
+
desc 'Default: run spec examples'
|
53
|
+
task :default => 'spec'
|
54
|
+
|
data/TODO
ADDED
@@ -0,0 +1,133 @@
|
|
1
|
+
- check property options
|
2
|
+
- more flexible way for checking associations
|
3
|
+
|
4
|
+
be_timestamped
|
5
|
+
be_key
|
6
|
+
validace
|
7
|
+
Category.should # => Expectation
|
8
|
+
Category.should has_property(:name) # => true
|
9
|
+
Category.my_property.should be_private
|
10
|
+
... soo ...
|
11
|
+
should true => OK
|
12
|
+
=> just return self instead of true => chain
|
13
|
+
@category.should has(1).error_on(:name).with("Nazev...")
|
14
|
+
|
15
|
+
irb(main):012:0> Product.slug
|
16
|
+
=> #<Property:Product:slug>
|
17
|
+
irb(main):014:0> Product.slug.to_yaml_properties
|
18
|
+
=> ["@custom", "@default", "@extra_options", "@field", "@fields", "@getter", "@index", "@instance_variable_name", "@key", "@lazy", "@length", "@model", "@name", "@nullable", "@options", "@primitive", "@reader_visibility", "@serial", "@track", "@type", "@unique_index", "@writer_visibility"]
|
19
|
+
irb(main):015:0> Product.slug.key?
|
20
|
+
=> true
|
21
|
+
irb(main):018:0> Product.slug.serial?
|
22
|
+
=> false
|
23
|
+
irb(main):019:0> Product.slug.default
|
24
|
+
=> nil
|
25
|
+
irb(main):022:0> Product.slug.extra_options
|
26
|
+
=> {:writter=>:private}
|
27
|
+
irb(main):027:0> Product.slug.unique_index
|
28
|
+
=> false
|
29
|
+
rb(main):030:0> Product.slug.reader_visibility
|
30
|
+
=> :public
|
31
|
+
irb(main):032:0> Product.slug.options
|
32
|
+
=> {:key=>true, :nullable=>false, :auto_validation=>false}
|
33
|
+
irb(main):004:0> Product.slug.nullable?
|
34
|
+
=> false
|
35
|
+
irb(main):006:0> Product.slug.args_and_options
|
36
|
+
=> [[], {}]
|
37
|
+
irb(main):007:0> Product.slug.blank?
|
38
|
+
=> false
|
39
|
+
irb(main):008:0> Product.slug.present?
|
40
|
+
=> true
|
41
|
+
irb(main):009:0> Product.slug.index
|
42
|
+
=> false
|
43
|
+
irb(main):010:0> Product.slug.size
|
44
|
+
=> 50
|
45
|
+
irb(main):011:0> Product.slug.primitive
|
46
|
+
=> String
|
47
|
+
irb(main):012:0> Product.slug.unique
|
48
|
+
=> true
|
49
|
+
|
50
|
+
|
51
|
+
irb(main):022:0> OrderItem.field_naming_convention
|
52
|
+
NameError: undefined local variable or method `args' for DataMapper:Module
|
53
|
+
irb(main):023:0> OrderItem.to_yaml_properties
|
54
|
+
=> ["@_valid_relations", "@field_naming_conventions", "@properties", "@relationships", "@storage_names", "@validations"]
|
55
|
+
irb(main):024:0> OrderItem.eager_properties
|
56
|
+
=> [#<Property:OrderItem:id>, #<Property:OrderItem:inverted>, #<Property:OrderItem:count>, #<Property:OrderItem:size>, #<Property:OrderItem:color>, #<Property:OrderItem:unit_price>, #<Property:OrderItem:product_slug>, #<Property:OrderItem:order_variable_symbol>, #<Property:OrderItem:color_id>, #<Property:OrderItem:size_height>, #<Property:OrderItem:size_width>]
|
57
|
+
irb(main):025:0> OrderItem.opts_from_validator_args
|
58
|
+
ArgumentError: wrong number of arguments (0 for 1)
|
59
|
+
irb(main):026:0> OrderItem.registered_as_hook?
|
60
|
+
ArgumentError: wrong number of arguments (0 for 2)
|
61
|
+
irb(main):027:0> OrderItem.first_or_create
|
62
|
+
ArgumentError: wrong number of arguments (0 for 1)
|
63
|
+
irb(main):028:0> OrderItem.property :count
|
64
|
+
ArgumentError: wrong number of arguments (1 for 2)
|
65
|
+
irb(main):029:0> OrderItem.hook_method_name
|
66
|
+
ArgumentError: wrong number of arguments (0 for 3)
|
67
|
+
irb(main):030:0> cattr_reader
|
68
|
+
NameError: undefined local variable or method `cattr_reader' for #<Object:0xb7d1894c>
|
69
|
+
irb(main):031:0> OrderItem.properties_with_subclasses
|
70
|
+
=> #<PropertySet:{#<Property:OrderItem:id>,#<Property:OrderItem:inverted>,#<Property:OrderItem:count>,#<Property:OrderItem:size>,#<Property:OrderItem:color>,#<Property:OrderItem:unit_price>,#<Property:OrderItem:product_slug>,#<Property:OrderItem:order_variable_symbol>,#<Property:OrderItem:color_id>,#<Property:OrderItem:size_height>,#<Property:OrderItem:size_width>}>
|
71
|
+
irb(main):032:0> OrderItem.timestamps
|
72
|
+
ArgumentError: You need to pass at least one argument
|
73
|
+
irb(main):033:0> OrderItem.relationships
|
74
|
+
=> {:product=>#<DataMapper::Associations::Relationship:0xb6cd62c8 @repository_name=:default, @parent_model=Product, @parent_properties=nil, @name=:product, @child_key=#<PropertySet:{#<Property:OrderItem:product_slug>}>, @child_model=OrderItem, @parent_key=#<PropertySet:{#<Property:Product:slug>}>, @options={}, @query={}, @child_properties=nil>, :order=>#<DataMapper::Associations::Relationship:0xb6cd7268 @repository_name=:default, @parent_model=Order, @parent_properties=nil, @name=:order, @child_key=#<PropertySet:{#<Property:OrderItem:order_variable_symbol>}>, @child_model=OrderItem, @parent_key=#<PropertySet:{#<Property:Order:variable_symbol>}>, @options={}, @query={}, @child_properties=nil>, :color=>#<DataMapper::Associations::Relationship:0xb6cd5350 @repository_name=:default, @parent_model=Color, @parent_properties=nil, @name=:color, @child_key=#<PropertySet:{#<Property:OrderItem:color_id>}>, @child_model=OrderItem, @parent_key=#<PropertySet:{#<Property:Color:id>}>, @options={}, @query={}, @child_properties=nil>}
|
75
|
+
irb(main):034:0> OrderItem.name
|
76
|
+
=> "OrderItem"
|
77
|
+
irb(main):035:0> OrderItem.validatable?
|
78
|
+
=> false
|
79
|
+
irb(main):036:0> OrderItem.default_order
|
80
|
+
=> [#<DataMapper::Query::Direction #<Property:OrderItem:id> asc>]
|
81
|
+
irb(main):037:0> OrderItem.class_variables
|
82
|
+
=> []
|
83
|
+
irb(main):038:0> OrderItem.base_model
|
84
|
+
=> OrderItem
|
85
|
+
irb(main):039:0> OrderItem.default_scope
|
86
|
+
=> {}
|
87
|
+
irb(main):040:0> OrderItem.ancestors
|
88
|
+
=> [OrderItem, DataMapper::Validate, DataMapper::Timestamp, #<Module:0xb6cdf0f8>, #<Module:0xb6ce00ac>, #<Module:0xb6ce0f98>, #<Module:0xb6ce1e48>, Extlib::Hook, DataMapper::Hook, DataMapper::Resource, DataMapper::Types, DataMapper::Resource::Transaction, DataMapper::Assertions, Object, Spec::Extensions::Main, Spec::Expectations::ObjectExpectations, DataMapper::Sweatshop::Unique, Base64::Deprecated, Base64, JSON::Ext::Generator::GeneratorMethods::Object, Kernel]
|
89
|
+
irb(main):041:0> OrderItem.generate_attributes
|
90
|
+
DataMapper::Sweatshop::NoFixtureExist: default fixture was not found for class OrderItem
|
91
|
+
irb(main):042:0> OrderItem.paranoid_properties
|
92
|
+
=> {}
|
93
|
+
irb(main):043:0> OrderItem.make
|
94
|
+
DataMapper::Sweatshop::NoFixtureExist: default fixture was not found for class OrderItem
|
95
|
+
irb(main):044:0> OrderItem.to_yaml
|
96
|
+
TypeError: can't dump anonymous class Class
|
97
|
+
irb(main):045:0> OrderItem.chainable
|
98
|
+
=> #<Module:0xb6fdd73c>
|
99
|
+
irb(main):046:0> OrderItem._valid_model
|
100
|
+
=> nil
|
101
|
+
irb(main):047:0> OrderItem.properties
|
102
|
+
=> #<PropertySet:{#<Property:OrderItem:id>,#<Property:OrderItem:inverted>,#<Property:OrderItem:count>,#<Property:OrderItem:size>,#<Property:OrderItem:color>,#<Property:OrderItem:unit_price>,#<Property:OrderItem:product_slug>,#<Property:OrderItem:order_variable_symbol>,#<Property:OrderItem:color_id>,#<Property:OrderItem:size_height>,#<Property:OrderItem:size_width>}>
|
103
|
+
irb(main):048:0> Order.tap
|
104
|
+
LocalJumpError: no block given
|
105
|
+
irb(main):049:0> Order.type
|
106
|
+
(irb):49: warning: Object#type is deprecated; use Object#class
|
107
|
+
=> Class
|
108
|
+
irb(main):050:0> o = Order
|
109
|
+
=> Order
|
110
|
+
irb(main):051:0> o.gen_attrs
|
111
|
+
NoMethodError: undefined method `new_record?' for "#ffff":String
|
112
|
+
from :0irb(main):052:0> o.validators
|
113
|
+
=> #<DataMapper::Validate::ContextualValidators:0xb6fdd020 @contexts={:default=>[#<DataMapper::Validate::RequiredFieldValidator:0xb6fdd084 @if_clause=nil, @options={:message=>"Zákazník musí být vyplněn", :context=>:default}, @unless_clause=nil, @field_name=:customer>, #<DataMapper::Validate::WithinValidator:0xb6fd0eb0 @if_clause=nil, @options={:message=>nil, :context=>:default, :set=>[:paypal, :personally, :cod, :transfer]}, @unless_clause=nil, @field_name=:payment_type>, #<DataMapper::Validate::WithinValidator:0xb6fca6dc @if_clause=nil, @options={:message=>nil, :context=>:default, :set=>[:unprocessed, :processed]}, @unless_clause=nil, @field_name=:status>, #<DataMapper::Validate::PrimitiveValidator:0xb6fc6988 @if_clause=nil, @options={:message=>nil, :context=>:default, :allow_nil=>true}, @unless_clause=nil, @field_name=:created_at>, #<DataMapper::Validate::PrimitiveValidator:0xb6fbbac4 @if_clause=nil, @options={:message=>nil, :context=>:default, :allow_nil=>true}, @unless_clause=nil, @field_name=:updated_at>, #<DataMapper::Validate::LengthValidator:0xb6fb16a0 @range=nil, @if_clause=nil, @min=nil, @equal=nil, @options={:message=>nil, :maximum=>50, :context=>:default, :allow_nil=>true}, @validation_method=:max, @unless_clause=nil, @max=50, @field_name=:referer>, #<DataMapper::Validate::PrimitiveValidator:0xb6faddac @if_clause=nil, @options={:message=>nil, :maximum=>50, :context=>:default, :allow_nil=>true}, @unless_clause=nil, @field_name=:referer>, #<DataMapper::Validate::LengthValidator:0xb6e66430 @range=nil, @if_clause=nil, @min=nil, @equal=nil, @options={:message=>nil, :maximum=>50, :context=>:default, :allow_nil=>true}, @validation_method=:max, @unless_clause=nil, @max=50, @field_name=:customer_email>, #<DataMapper::Validate::PrimitiveValidator:0xb6e6493c @if_clause=nil, @options={:message=>nil, :maximum=>50, :context=>:default, :allow_nil=>true}, @unless_clause=nil, @field_name=:customer_email>]}>
|
114
|
+
irb(main):053:0> o.options
|
115
|
+
=> nil
|
116
|
+
irb(main):054:0> o.to_yaml_style
|
117
|
+
=> nil
|
118
|
+
irb(main):055:0> o.subclass
|
119
|
+
ArgumentError: wrong number of arguments (0 for 1)
|
120
|
+
irb(main):056:0> o.present?
|
121
|
+
=> true
|
122
|
+
irb(main):057:0> o.args_for
|
123
|
+
ArgumentError: wrong number of arguments (0 for 1)
|
124
|
+
irb(main):058:0> o.in?
|
125
|
+
ArgumentError: wrong number of arguments (0 for 1)
|
126
|
+
irb(main):059:0> o.to_query
|
127
|
+
ArgumentError: wrong number of arguments (0 for 2)
|
128
|
+
irb(main):060:0> o.orig_auto_generate_validations
|
129
|
+
ArgumentError: wrong number of arguments (0 for 1)
|
130
|
+
irb(main):061:0> o.many_to_one_relationships
|
131
|
+
=> [#<DataMapper::Associations::Relationship:0xb6fd9d58 @repository_name=:default, @parent_model=Customer, @parent_properties=nil, @name=:customer, @child_key=#<PropertySet:{#<Property:Order:customer_email>}>, @child_model=Order, @parent_key=#<PropertySet:{#<Property:Customer:email>}>, @options={}, @query={}, @child_properties=nil>]
|
132
|
+
irb(main):062:0> o.unique
|
133
|
+
NoMethodError: undefined method `arity' for nil:NilClass
|
data/dm-spec.gemspec
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
begin
|
2
|
+
require "rubygems/specification"
|
3
|
+
rescue SecurityError
|
4
|
+
# http://gems.github.com
|
5
|
+
end
|
6
|
+
|
7
|
+
VERSION = "0.9.10"
|
8
|
+
SPECIFICATION = ::Gem::Specification.new do |s|
|
9
|
+
s.name = "dm-spec"
|
10
|
+
# s.version = Rango::VERSION
|
11
|
+
s.version = VERSION
|
12
|
+
s.authors = ["Jakub Šťastný aka Botanicus"]
|
13
|
+
s.homepage = "http://github.com/botanicus/dm-spec"
|
14
|
+
s.summary = "Some rSpec matchers for DataMapper"
|
15
|
+
# s.description = "" # TODO: long description
|
16
|
+
s.cert_chain = nil
|
17
|
+
s.email = ["knava.bestvinensis", "gmail.com"].join("@")
|
18
|
+
s.files = Dir.glob("**/*") - Dir.glob("pkg/*")
|
19
|
+
s.add_dependency "dm-core"
|
20
|
+
s.require_paths = ["lib"]
|
21
|
+
# s.required_ruby_version = ::Gem::Requirement.new(">= 1.9.1")
|
22
|
+
# s.rubyforge_project = "rango"
|
23
|
+
end
|
data/lib/dm-spec.rb
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
# belongs to
|
2
|
+
# Product.relationships[:category]
|
3
|
+
#<DataMapper::Associations::Relationship @parent_model=Category, @name=:category, @child_key=#<PropertySet:{#<Property:Product:category_slug>}>, @child_model=Product, @parent_key=#<PropertySet:{#<Property:Category:slug>}>, @options={}>
|
4
|
+
|
5
|
+
module DataMapperMatchers
|
6
|
+
class BelongsTo
|
7
|
+
# args<Symbol>: :products
|
8
|
+
def initialize(name)
|
9
|
+
@name = name
|
10
|
+
end
|
11
|
+
|
12
|
+
# args<Model>: Category
|
13
|
+
def matches?(model)
|
14
|
+
@model = model
|
15
|
+
relationship = @model.relationships[@name]
|
16
|
+
return false unless relationship
|
17
|
+
relationship.name == @name && relationship.child_model == @model
|
18
|
+
end
|
19
|
+
|
20
|
+
def description
|
21
|
+
"belongs to"
|
22
|
+
end
|
23
|
+
|
24
|
+
def failure_message
|
25
|
+
belonging = @model.relationships.select { |name, relationship| relationship.name == @name && relationship.child_model == @model }
|
26
|
+
belonging.map! { |array| array.first }
|
27
|
+
if belonging.empty?
|
28
|
+
"expected to belongs to #@name, but does not belongs to any model"
|
29
|
+
else
|
30
|
+
"expected to belongs to #@name, but belongs to just the following relationships: #{belonging.inspect}"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def negative_failure_message
|
35
|
+
"expected not to belongs to #@name, but belonged"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def belongs_to(name)
|
40
|
+
BelongsTo.new(name)
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# habtm
|
2
|
+
# Product.relationships[:colors]
|
3
|
+
#<DataMapper::Associations::RelationshipChain @parent_model=Product, @name=:color_products, @mutable=true, @child_key=#<PropertySet:{#<Property:ColorProduct:product_slug>}>, @child_model="Color", @parent_key=#<PropertySet:{#<Property:Product:slug>}>, @remote_relationship_name="colors", @options={:repository_name=>:default, :near_relationship_name=>:color_products, :parent_key=>nil, :child_key=>nil, :remote_relationship_name=>"colors", :child_model=>"Color", :min=>0, :parent_model=>Product, :max=>Infinity}, @near_relationship_name=:color_products>
|
4
|
+
|
5
|
+
module DataMapperMatchers
|
6
|
+
class HasAndBelongsToMany
|
7
|
+
# args<Symbol>: :products
|
8
|
+
def initialize(name)
|
9
|
+
@name = name
|
10
|
+
end
|
11
|
+
|
12
|
+
# args<Model>: Category
|
13
|
+
def matches?(model)
|
14
|
+
@model = model
|
15
|
+
relationship = @model.relationships[@name]
|
16
|
+
return false unless relationship
|
17
|
+
relationship.class == DataMapper::Associations::RelationshipChain && relationship.child_model == @model
|
18
|
+
end
|
19
|
+
|
20
|
+
def description
|
21
|
+
"has and belongs to many"
|
22
|
+
end
|
23
|
+
|
24
|
+
def failure_message
|
25
|
+
# TODO: filter just n:n relationships
|
26
|
+
relationships = @model.relationships.map { |name, relationship| relationship.name }.inspect
|
27
|
+
"expected to has and belongs to many #@name, but has just the following relationships: #{relationships}"
|
28
|
+
end
|
29
|
+
|
30
|
+
def negative_failure_message
|
31
|
+
"expected not to has and belongs to many #@name, but had"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def has_and_belongs_to_many(name)
|
36
|
+
HasAndBelongsToMany.new(name)
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# has 1
|
2
|
+
# Product.relationships[:item]
|
3
|
+
#<DataMapper::Associations::Relationship @parent_model=Product, @name=:item, @child_key=#<PropertySet:{#<Property:OrderItem:product_slug>}>, @child_model=OrderItem, @parent_key=#<PropertySet:{#<Property:Product:slug>}>, @options={:min=>1, :class_name=>"OrderItem", :max=>1}>
|
4
|
+
|
5
|
+
# has n
|
6
|
+
# Category.relationships[:products]
|
7
|
+
#<DataMapper::Associations::Relationship @parent_model=Category, @name=:products, @child_key=#<PropertySet:{#<Property:Product:category_slug>}>, @child_model=Product, @parent_key=#<PropertySet:{#<Property:Category:slug>}>, @options={:min=>0, :max=>Infinity}>
|
8
|
+
|
9
|
+
module DataMapperMatchers
|
10
|
+
class HasMany
|
11
|
+
def initialize(name, options)
|
12
|
+
@name = name
|
13
|
+
@options = options
|
14
|
+
end
|
15
|
+
|
16
|
+
# args<Model>: Category
|
17
|
+
def matches?(model)
|
18
|
+
@model = model
|
19
|
+
relationship = @model.relationships[@name]
|
20
|
+
return false unless relationship
|
21
|
+
relationship.parent_model == @model && @options.all? { |key, value| relationship.options[key] == value }
|
22
|
+
end
|
23
|
+
|
24
|
+
def description
|
25
|
+
"has many"
|
26
|
+
end
|
27
|
+
|
28
|
+
def failure_message
|
29
|
+
# TODO: filter just has many relationships
|
30
|
+
properties = @model.properties.entries.map { |property| property.name }.inspect
|
31
|
+
"expected to has many #@name, but has just the following properties: #{properties}"
|
32
|
+
end
|
33
|
+
|
34
|
+
def negative_failure_message
|
35
|
+
"expected to not has many #@name, but had"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def has_many(name, options = Hash.new)
|
40
|
+
defaults = {:min => 0, :max => 1.0 / 0}
|
41
|
+
HasMany.new(name, defaults.merge(options))
|
42
|
+
end
|
43
|
+
|
44
|
+
def has_one(name, options = Hash.new)
|
45
|
+
defaults = {:min => 1, :max => 1}
|
46
|
+
HasMany.new(name, defaults.merge(options))
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# Category.properties[:expected]
|
2
|
+
# => #<Property:Category:expected>
|
3
|
+
# Category.properties.has_property? :expected
|
4
|
+
# => true
|
5
|
+
|
6
|
+
module DataMapperMatchers
|
7
|
+
class HasProperty
|
8
|
+
def initialize(expected)
|
9
|
+
@expected = expected
|
10
|
+
end
|
11
|
+
|
12
|
+
# Category.should has_property(:id)
|
13
|
+
def matches?(model)
|
14
|
+
@model = model
|
15
|
+
return @model.properties.has_property?(@expected)
|
16
|
+
end
|
17
|
+
|
18
|
+
def description
|
19
|
+
"has property"
|
20
|
+
end
|
21
|
+
|
22
|
+
def failure_message
|
23
|
+
properties = @model.properties.entries.map { |property| property.name }
|
24
|
+
"expected to has property #@expected, but has just the following properties: #{properties}"
|
25
|
+
end
|
26
|
+
|
27
|
+
def negative_failure_message
|
28
|
+
"expected to not has property #@expected, but had"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def has_property(expected)
|
33
|
+
HasProperty.new(expected)
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# Category.properties[:expected]
|
2
|
+
# => #<Timestamps:Category:expected>
|
3
|
+
# Category.properties.has_timestamps? :expected
|
4
|
+
# => true
|
5
|
+
|
6
|
+
module DataMapperMatchers
|
7
|
+
class HasTimestamps
|
8
|
+
def initialize(*expected)
|
9
|
+
# Post.should has_timestamps => :created_at, :updated_at
|
10
|
+
expected.push(:at) if expected.empty?
|
11
|
+
if [:at, :on].include?(expected.first)
|
12
|
+
# Post.should has_timestamps(:at)
|
13
|
+
@expected = ["created_#{expected}", "updated_#{expected}"]
|
14
|
+
else
|
15
|
+
# Post.should has_timestamps(:created_at)
|
16
|
+
@expected = expected
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# Category.should has_timestamps(:id)
|
21
|
+
def matches?(model)
|
22
|
+
@model = model
|
23
|
+
loaded && included && has_timestamps
|
24
|
+
end
|
25
|
+
|
26
|
+
def loaded
|
27
|
+
$LOADED_FEATURES.any? { |file| File.basename(file).eql?("dm-timestamps.rb") }
|
28
|
+
end
|
29
|
+
|
30
|
+
def included
|
31
|
+
@model.respond_to?(:timestamps) # DataMapper::Timestamp model is included
|
32
|
+
end
|
33
|
+
|
34
|
+
def has_timestamps
|
35
|
+
@expected.all { |method| model.new.respond_to?(method) }
|
36
|
+
end
|
37
|
+
|
38
|
+
def description
|
39
|
+
"has timestamps"
|
40
|
+
end
|
41
|
+
|
42
|
+
def failure_message
|
43
|
+
properties = @model.properties.entries.map { |timestamps| timestamps.name }
|
44
|
+
"expected to has timestamps #@expected, but has just the following properties: #{properties}"
|
45
|
+
end
|
46
|
+
|
47
|
+
def negative_failure_message
|
48
|
+
"expected not to not timestamps #@expected, but had"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def has_timestamps(expected)
|
53
|
+
HasTimestamps.new(expected)
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), '..', "spec_helper")
|
2
|
+
require "spec/matchers/dm/belongs_to"
|
3
|
+
|
4
|
+
class Post
|
5
|
+
include DataMapper::Resource
|
6
|
+
property :id, Serial
|
7
|
+
belongs_to :category
|
8
|
+
end
|
9
|
+
|
10
|
+
class Category
|
11
|
+
include DataMapper::Resource
|
12
|
+
property :id, Serial
|
13
|
+
has n, :posts
|
14
|
+
end
|
15
|
+
|
16
|
+
describe DataMapperMatchers::BelongsTo do
|
17
|
+
it "should pass for working associations" do
|
18
|
+
lambda { Post.should belongs_to(:category) }.should_not fail
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should fail for non existing working associations" do
|
22
|
+
lambda { Post.should_not belongs_to(:category) }.should fail_with("expected not to belongs to category, but belonged")
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should fail for non existing working associations" do
|
26
|
+
lambda { Post.should belongs_to(:items) }.should fail_with("expected to belongs to items, but does not belongs to any model")
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), '..', "spec_helper")
|
2
|
+
require "spec/matchers/dm/has_and_belongs_to_many"
|
3
|
+
|
4
|
+
class Post
|
5
|
+
include DataMapper::Resource
|
6
|
+
property :id, Serial
|
7
|
+
has n, :tags, :through => Resource
|
8
|
+
end
|
9
|
+
|
10
|
+
class Tag
|
11
|
+
include DataMapper::Resource
|
12
|
+
property :id, Serial
|
13
|
+
has n, :posts, :through => Resource
|
14
|
+
end
|
15
|
+
|
16
|
+
describe DataMapperMatchers::BelongsTo do
|
17
|
+
it "should pass for working associations" do
|
18
|
+
Post.should has_and_belongs_to_many(:tags)
|
19
|
+
Tag.should has_and_belongs_to_many(:posts)
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should fail for non existing working associations" do
|
23
|
+
lambda { Post.should_not has_and_belongs_to_many(:tags) }.should fail
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should fail for non existing working associations" do
|
27
|
+
lambda { Post.should has_and_belongs_to_many(:items) }.should fail_with("expected to has and belongs to many items, but has just the following relationships: [:post_tags, :post_tags, :category]")
|
28
|
+
end
|
29
|
+
end
|
File without changes
|
File without changes
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), '..', "spec_helper")
|
2
|
+
require "spec/matchers/dm/has_timestamps"
|
3
|
+
|
4
|
+
class Post
|
5
|
+
include DataMapper::Resource
|
6
|
+
property :id, Serial
|
7
|
+
belongs_to :category
|
8
|
+
end
|
9
|
+
|
10
|
+
class Category
|
11
|
+
include DataMapper::Resource
|
12
|
+
property :id, Serial
|
13
|
+
has n, :posts
|
14
|
+
end
|
15
|
+
|
16
|
+
describe DataMapperMatchers::HasTimestamps do
|
17
|
+
it "should pass for working associations" do
|
18
|
+
Post.should belongs_to(:category)
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should fail for non existing working associations" do
|
22
|
+
lambda { Post.should_not belongs_to(:category) }.should fail_with("expected not to belongs to category, but belonged")
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should fail for non existing working associations" do
|
26
|
+
lambda { Post.should belongs_to(:items) }.should fail_with("expected to belongs to items, but has just the following relationships: [:post_tags, :post_tags, :category]")
|
27
|
+
end
|
28
|
+
end
|
data/spec/spec.opts
ADDED
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,108 @@
|
|
1
|
+
$: << File.dirname(__FILE__) + "/../lib"
|
2
|
+
require "dm-core"
|
3
|
+
|
4
|
+
DataMapper.setup(:default, "sqlite3::memory")
|
5
|
+
|
6
|
+
# must be initialized for include
|
7
|
+
module DataMapperMatchers
|
8
|
+
end
|
9
|
+
|
10
|
+
Spec::Runner.configure do |config|
|
11
|
+
config.include(DataMapperMatchers)
|
12
|
+
config.before(:each) do
|
13
|
+
DataMapper.auto_migrate!
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
##############
|
18
|
+
# from rspec #
|
19
|
+
##############
|
20
|
+
module Spec
|
21
|
+
module Example
|
22
|
+
class NonStandardError < Exception; end
|
23
|
+
end
|
24
|
+
|
25
|
+
module Matchers
|
26
|
+
def fail
|
27
|
+
raise_error(Spec::Expectations::ExpectationNotMetError)
|
28
|
+
end
|
29
|
+
|
30
|
+
def fail_with(message)
|
31
|
+
raise_error(Spec::Expectations::ExpectationNotMetError, message)
|
32
|
+
end
|
33
|
+
|
34
|
+
def exception_from(&block)
|
35
|
+
exception = nil
|
36
|
+
begin
|
37
|
+
yield
|
38
|
+
rescue StandardError => e
|
39
|
+
exception = e
|
40
|
+
end
|
41
|
+
exception
|
42
|
+
end
|
43
|
+
|
44
|
+
def run_with(options)
|
45
|
+
::Spec::Runner::CommandLine.run(options)
|
46
|
+
end
|
47
|
+
|
48
|
+
def with_ruby(version)
|
49
|
+
yield if RUBY_VERSION =~ Regexp.compile("^#{version.to_s}")
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def with_sandboxed_options
|
55
|
+
attr_reader :options
|
56
|
+
|
57
|
+
before(:each) do
|
58
|
+
@original_rspec_options = ::Spec::Runner.options
|
59
|
+
::Spec::Runner.use(@options = ::Spec::Runner::Options.new(StringIO.new, StringIO.new))
|
60
|
+
end
|
61
|
+
|
62
|
+
after(:each) do
|
63
|
+
::Spec::Runner.use(@original_rspec_options)
|
64
|
+
end
|
65
|
+
|
66
|
+
yield
|
67
|
+
end
|
68
|
+
|
69
|
+
def with_sandboxed_config
|
70
|
+
attr_reader :config
|
71
|
+
|
72
|
+
before(:each) do
|
73
|
+
@config = ::Spec::Runner::Configuration.new
|
74
|
+
@original_configuration = ::Spec::Runner.configuration
|
75
|
+
spec_configuration = @config
|
76
|
+
::Spec::Runner.instance_eval {@configuration = spec_configuration}
|
77
|
+
end
|
78
|
+
|
79
|
+
after(:each) do
|
80
|
+
original_configuration = @original_configuration
|
81
|
+
::Spec::Runner.instance_eval {@configuration = original_configuration}
|
82
|
+
::Spec::Example::ExampleGroupFactory.reset
|
83
|
+
end
|
84
|
+
|
85
|
+
yield
|
86
|
+
end
|
87
|
+
|
88
|
+
module Spec
|
89
|
+
module Example
|
90
|
+
module Resettable
|
91
|
+
def reset # :nodoc:
|
92
|
+
@before_all_parts = nil
|
93
|
+
@after_all_parts = nil
|
94
|
+
@before_each_parts = nil
|
95
|
+
@after_each_parts = nil
|
96
|
+
end
|
97
|
+
end
|
98
|
+
class ExampleGroup
|
99
|
+
extend Resettable
|
100
|
+
end
|
101
|
+
class ExampleGroupDouble < ExampleGroup
|
102
|
+
::Spec::Runner.options.remove_example_group self
|
103
|
+
def register_example_group(klass)
|
104
|
+
#ignore
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
metadata
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: dm-spec
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.9.10
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- "Jakub \xC5\xA0\xC5\xA5astn\xC3\xBD aka Botanicus"
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain:
|
11
|
+
date: 2009-11-26 00:00:00 +00:00
|
12
|
+
default_executable:
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: dm-core
|
16
|
+
type: :runtime
|
17
|
+
version_requirement:
|
18
|
+
version_requirements: !ruby/object:Gem::Requirement
|
19
|
+
requirements:
|
20
|
+
- - ">="
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: "0"
|
23
|
+
version:
|
24
|
+
description:
|
25
|
+
email: knava.bestvinensis@gmail.com
|
26
|
+
executables: []
|
27
|
+
|
28
|
+
extensions: []
|
29
|
+
|
30
|
+
extra_rdoc_files: []
|
31
|
+
|
32
|
+
files:
|
33
|
+
- dm-spec.gemspec
|
34
|
+
- lib/dm-spec.rb
|
35
|
+
- lib/spec/matchers/dm/belongs_to.rb
|
36
|
+
- lib/spec/matchers/dm/has_and_belongs_to_many.rb
|
37
|
+
- lib/spec/matchers/dm/has_many.rb
|
38
|
+
- lib/spec/matchers/dm/has_property.rb
|
39
|
+
- lib/spec/matchers/dm/has_timestamps.rb
|
40
|
+
- LICENSE
|
41
|
+
- Rakefile
|
42
|
+
- README.textile
|
43
|
+
- spec/dm/belongs_to_spec.rb
|
44
|
+
- spec/dm/has_and_belongs_to_many_spec.rb
|
45
|
+
- spec/dm/has_many_spec.rb
|
46
|
+
- spec/dm/has_property_spec.rb
|
47
|
+
- spec/dm/has_timestamps_spec.rb
|
48
|
+
- spec/spec.opts
|
49
|
+
- spec/spec_helper.rb
|
50
|
+
- TODO
|
51
|
+
has_rdoc: true
|
52
|
+
homepage: http://github.com/botanicus/dm-spec
|
53
|
+
licenses: []
|
54
|
+
|
55
|
+
post_install_message:
|
56
|
+
rdoc_options: []
|
57
|
+
|
58
|
+
require_paths:
|
59
|
+
- lib
|
60
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
61
|
+
requirements:
|
62
|
+
- - ">="
|
63
|
+
- !ruby/object:Gem::Version
|
64
|
+
version: "0"
|
65
|
+
version:
|
66
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
67
|
+
requirements:
|
68
|
+
- - ">="
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
version: "0"
|
71
|
+
version:
|
72
|
+
requirements: []
|
73
|
+
|
74
|
+
rubyforge_project:
|
75
|
+
rubygems_version: 1.3.5
|
76
|
+
signing_key:
|
77
|
+
specification_version: 3
|
78
|
+
summary: Some rSpec matchers for DataMapper
|
79
|
+
test_files: []
|
80
|
+
|