remarkable_mongo 0.1.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.
- data/.gitignore +1 -0
- data/LICENSE +20 -0
- data/README.md +23 -0
- data/Rakefile +28 -0
- data/VERSION +1 -0
- data/lib/remarkable/mongo_mapper.rb +30 -0
- data/lib/remarkable/mongo_mapper/base.rb +223 -0
- data/lib/remarkable/mongo_mapper/describe.rb +199 -0
- data/lib/remarkable/mongo_mapper/human_names.rb +37 -0
- data/lib/remarkable/mongo_mapper/matchers/allow_values_for_matcher.rb +86 -0
- data/lib/remarkable/mongo_mapper/matchers/association_matcher.rb +105 -0
- data/lib/remarkable/mongo_mapper/matchers/have_key_matcher.rb +38 -0
- data/lib/remarkable/mongo_mapper/matchers/validate_confirmation_of_matcher.rb +44 -0
- data/lib/remarkable/mongo_mapper/matchers/validate_length_of_matcher.rb +106 -0
- data/lib/remarkable/mongo_mapper/matchers/validate_presence_of_matcher.rb +37 -0
- data/locales/en.yml +135 -0
- data/spec/matchers/allow_values_for_matcher_spec.rb +71 -0
- data/spec/matchers/association_matcher_spec.rb +104 -0
- data/spec/matchers/have_key_matcher_spec.rb +32 -0
- data/spec/matchers/validate_confirmation_of_matcher_spec.rb +64 -0
- data/spec/matchers/validate_length_of_matcher_spec.rb +147 -0
- data/spec/matchers/validate_presence_of_matcher_spec.rb +33 -0
- data/spec/model_builder.rb +64 -0
- data/spec/models.rb +42 -0
- data/spec/spec.opts +4 -0
- data/spec/spec_helper.rb +17 -0
- metadata +108 -0
@@ -0,0 +1,37 @@
|
|
1
|
+
if defined?(Spec)
|
2
|
+
module Spec #:nodoc:
|
3
|
+
module Example #:nodoc:
|
4
|
+
module ExampleGroupMethods #:nodoc:
|
5
|
+
|
6
|
+
# This allows "describe User" to use the I18n human name of User.
|
7
|
+
#
|
8
|
+
def self.build_description_with_i18n(*args)
|
9
|
+
args.inject("") do |description, arg|
|
10
|
+
arg = if arg.respond_to?(:human_name)
|
11
|
+
arg.human_name(:locale => Remarkable.locale)
|
12
|
+
else
|
13
|
+
arg.to_s
|
14
|
+
end
|
15
|
+
|
16
|
+
description << " " unless (description == "" || arg =~ /^(\s|\.|#)/)
|
17
|
+
description << arg
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# This is for rspec <= 1.1.12.
|
22
|
+
#
|
23
|
+
def self.description_text(*args)
|
24
|
+
self.build_description_with_i18n(*args)
|
25
|
+
end
|
26
|
+
|
27
|
+
# This is for rspec >= 1.2.0.
|
28
|
+
#
|
29
|
+
def self.build_description_from(*args)
|
30
|
+
text = ExampleGroupMethods.build_description_with_i18n(*args)
|
31
|
+
text == "" ? nil : text
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
module Remarkable
|
2
|
+
module MongoMapper
|
3
|
+
module Matchers
|
4
|
+
class AllowValuesForMatcher < Remarkable::MongoMapper::Base #:nodoc:
|
5
|
+
include Remarkable::Negative
|
6
|
+
arguments :collection => :attributes, :as => :attribute
|
7
|
+
|
8
|
+
optional :message
|
9
|
+
optional :in, :splat => true
|
10
|
+
optional :allow_nil, :allow_blank, :default => true
|
11
|
+
|
12
|
+
collection_assertions :is_valid?, :is_invalid?, :allow_nil?, :allow_blank?
|
13
|
+
|
14
|
+
default_options :message => "is invalid"
|
15
|
+
|
16
|
+
before_assert do
|
17
|
+
first_value = @options[:in].is_a?(Array) ? @options[:in].first : @options[:in]
|
18
|
+
@in_range = first_value.is_a?(Range)
|
19
|
+
|
20
|
+
@options[:in] = if @in_range
|
21
|
+
first_value.to_a[0,2] + first_value.to_a[-2,2]
|
22
|
+
else
|
23
|
+
[*@options[:in]].compact
|
24
|
+
end
|
25
|
+
|
26
|
+
@options[:in].uniq!
|
27
|
+
end
|
28
|
+
|
29
|
+
protected
|
30
|
+
|
31
|
+
def is_valid?
|
32
|
+
assert_collection :value, valid_values do |value|
|
33
|
+
good?(value)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def is_invalid?
|
38
|
+
assert_collection :value, invalid_values do |value|
|
39
|
+
bad?(value)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def valid_values
|
44
|
+
@options[:in]
|
45
|
+
end
|
46
|
+
|
47
|
+
def invalid_values
|
48
|
+
[]
|
49
|
+
end
|
50
|
+
|
51
|
+
def interpolation_options
|
52
|
+
options = if @in_range
|
53
|
+
{ :in => (@options[:in].first..@options[:in].last).inspect }
|
54
|
+
elsif @options[:in].is_a?(Array)
|
55
|
+
{ :in => array_to_sentence(@options[:in], true, '[]') }
|
56
|
+
else
|
57
|
+
{ :in => @options[:in].inspect }
|
58
|
+
end
|
59
|
+
|
60
|
+
options.merge!(:behavior => @behavior.to_s)
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
# Ensures that the attribute can be set to the given values.
|
66
|
+
#
|
67
|
+
# == Options
|
68
|
+
#
|
69
|
+
# * <tt>:allow_nil</tt> - when supplied, validates if it allows nil or not.
|
70
|
+
# * <tt>:allow_blank</tt> - when supplied, validates if it allows blank or not.
|
71
|
+
# * <tt>:message</tt> - value the test expects to find in <tt>errors.on(:attribute)</tt>.
|
72
|
+
# Regexp, string or symbol. Default = <tt>I18n.translate('activerecord.errors.messages.invalid')</tt>
|
73
|
+
#
|
74
|
+
# == Examples
|
75
|
+
#
|
76
|
+
# should_allow_values_for :isbn, "isbn 1 2345 6789 0", "ISBN 1-2345-6789-0"
|
77
|
+
# it { should allow_values_for(:isbn, "isbn 1 2345 6789 0", "ISBN 1-2345-6789-0") }
|
78
|
+
#
|
79
|
+
def allow_values_for(attribute, *args, &block)
|
80
|
+
options = args.extract_options!
|
81
|
+
AllowValuesForMatcher.new(attribute, options.merge!(:in => args), &block).spec(self)
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
module Remarkable
|
2
|
+
module MongoMapper
|
3
|
+
module Matchers
|
4
|
+
class AssociationMatcher < Remarkable::MongoMapper::Base
|
5
|
+
arguments :type, :collection => :associations, :as => :association
|
6
|
+
|
7
|
+
optionals :class_name
|
8
|
+
|
9
|
+
collection_assertions :association_exists?, :type_matches?, :klass_exists?, :options_match?
|
10
|
+
|
11
|
+
protected
|
12
|
+
|
13
|
+
def association_exists?
|
14
|
+
reflection
|
15
|
+
end
|
16
|
+
|
17
|
+
def type_matches?
|
18
|
+
reflection.type == @type
|
19
|
+
end
|
20
|
+
|
21
|
+
def klass_exists?
|
22
|
+
return true if @options[:polymorphic]
|
23
|
+
reflection.klass rescue nil
|
24
|
+
end
|
25
|
+
|
26
|
+
def options_match?
|
27
|
+
actual_options = {}
|
28
|
+
|
29
|
+
@options.keys.each do |key|
|
30
|
+
method = :"reflection_#{key}"
|
31
|
+
|
32
|
+
@options[key] = @options[key].to_s
|
33
|
+
actual_options[key] = (respond_to?(method, true) ? send(method) : reflection.options[key]).to_s
|
34
|
+
end
|
35
|
+
|
36
|
+
return @options == actual_options, :actual => actual_options.inspect
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def reflection
|
42
|
+
@reflection ||= subject_class.associations[@association]
|
43
|
+
end
|
44
|
+
|
45
|
+
def interpolation_options
|
46
|
+
options = {}
|
47
|
+
options[:type] = Remarkable.t(@type, :scope => matcher_i18n_scope, :default => @type.to_s.gsub("_", ""))
|
48
|
+
options[:options] = @options.inspect
|
49
|
+
|
50
|
+
if @subject && reflection
|
51
|
+
options.merge!(
|
52
|
+
:actual_type => Remarkable.t(reflection.type, :scope => matcher_i18n_scope, :default => reflection.type.to_s)
|
53
|
+
)
|
54
|
+
end
|
55
|
+
|
56
|
+
options
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
|
61
|
+
# Ensures that the many relationship exists. Will also test that the
|
62
|
+
# associated table has the required columns.
|
63
|
+
#
|
64
|
+
# == Options
|
65
|
+
#
|
66
|
+
# * <tt>:class_name</tt> - the expected associted class name.
|
67
|
+
# * <tt>:polymorphic</tt> - if the association should be polymorphic or not.
|
68
|
+
# When true it also checks for the association_type column in the subject table.
|
69
|
+
#
|
70
|
+
# == Examples
|
71
|
+
#
|
72
|
+
# should_have_many :addresses
|
73
|
+
# should_have_many :users, :class_name => 'Person'
|
74
|
+
#
|
75
|
+
# it { should have_many(:addresses) }
|
76
|
+
# it { should have_many(:users, :class_name => 'Person') }
|
77
|
+
#
|
78
|
+
def have_many(*associations, &block)
|
79
|
+
AssociationMatcher.new(:many, *associations, &block).spec(self)
|
80
|
+
end
|
81
|
+
|
82
|
+
# Ensures that the many relationship exists. Will also test that the
|
83
|
+
# associated table has the required columns.
|
84
|
+
#
|
85
|
+
# == Options
|
86
|
+
#
|
87
|
+
# * <tt>:class_name</tt> - the expected associted class name.
|
88
|
+
# * <tt>:polymorphic</tt> - if the association should be polymorphic or not.
|
89
|
+
# When true it also checks for the association_type column in the subject table.
|
90
|
+
#
|
91
|
+
# == Examples
|
92
|
+
#
|
93
|
+
# should_belong_to :user
|
94
|
+
# should_belong_to :user, :class_name => 'Person'
|
95
|
+
#
|
96
|
+
# it { should belong_to(:user) }
|
97
|
+
# it { should belong_to(:user, :class_name => 'Person') }
|
98
|
+
#
|
99
|
+
def belong_to(*associations, &block)
|
100
|
+
AssociationMatcher.new(:belongs_to, *associations, &block).spec(self)
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Remarkable
|
2
|
+
module MongoMapper
|
3
|
+
module Matchers
|
4
|
+
class HaveKeyMatcher < Remarkable::MongoMapper::Base
|
5
|
+
|
6
|
+
arguments :type, :collection => :attributes, :as => :attribute
|
7
|
+
|
8
|
+
collection_assertions :has_key?
|
9
|
+
|
10
|
+
# before_assert do
|
11
|
+
# @type = @options[:type]
|
12
|
+
# end
|
13
|
+
|
14
|
+
protected
|
15
|
+
|
16
|
+
def has_key?
|
17
|
+
@subject.respond_to?(@attribute) && @subject.class.keys[@attribute] == ::MongoMapper::Key.new(@attribute, @type)
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
# Ensures that a key of the database actually exists.
|
23
|
+
#
|
24
|
+
# == Examples
|
25
|
+
#
|
26
|
+
# should_have_key :name, String
|
27
|
+
#
|
28
|
+
# it { should have_key(:name, String) }
|
29
|
+
# it { should have_keys(:name, :phone_number, String) }
|
30
|
+
#
|
31
|
+
def have_key(*args, &block)
|
32
|
+
HaveKeyMatcher.new(args.pop, *args, &block).spec(self)
|
33
|
+
end
|
34
|
+
alias :have_keys :have_key
|
35
|
+
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Remarkable
|
2
|
+
module MongoMapper
|
3
|
+
module Matchers
|
4
|
+
class ValidateConfirmationOfMatcher < Remarkable::MongoMapper::Base #:nodoc:
|
5
|
+
arguments :collection => :attributes, :as => :attribute
|
6
|
+
|
7
|
+
optional :message
|
8
|
+
collection_assertions :responds_to_confirmation?, :confirms?
|
9
|
+
|
10
|
+
default_options :message => "doesn't match confirmation"
|
11
|
+
|
12
|
+
protected
|
13
|
+
|
14
|
+
def responds_to_confirmation?
|
15
|
+
@subject.respond_to?(:"#{@attribute}_confirmation=")
|
16
|
+
end
|
17
|
+
|
18
|
+
def confirms?
|
19
|
+
@subject.send(:"#{@attribute}_confirmation=", 'something')
|
20
|
+
bad?('different')
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
# Ensures that the model cannot be saved if one of the attributes is not confirmed.
|
26
|
+
#
|
27
|
+
# == Options
|
28
|
+
#
|
29
|
+
# * <tt>:message</tt> - value the test expects to find in <tt>errors.on(:attribute)</tt>.
|
30
|
+
# Regexp, string or symbol. Default = "doesn't match confirmation"
|
31
|
+
#
|
32
|
+
# == Examples
|
33
|
+
#
|
34
|
+
# should_validate_confirmation_of :email, :password
|
35
|
+
#
|
36
|
+
# it { should validate_confirmation_of(:email, :password) }
|
37
|
+
#
|
38
|
+
def validate_confirmation_of(*attributes, &block)
|
39
|
+
ValidateConfirmationOfMatcher.new(*attributes, &block).spec(self)
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
module Remarkable
|
2
|
+
module MongoMapper
|
3
|
+
module Matchers
|
4
|
+
class ValidateLengthOfMatcher < Remarkable::MongoMapper::Base #:nodoc:
|
5
|
+
arguments :collection => :attributes, :as => :attribute
|
6
|
+
|
7
|
+
optional :within, :minimum, :maximum, :is
|
8
|
+
optional :allow_nil, :allow_blank, :default => true
|
9
|
+
optional :message
|
10
|
+
|
11
|
+
default_options :message => "is invalid"
|
12
|
+
|
13
|
+
collection_assertions :less_than_min_length?, :exactly_min_length?,
|
14
|
+
:more_than_max_length?, :exactly_max_length?,
|
15
|
+
:allow_nil?, :allow_blank?
|
16
|
+
|
17
|
+
before_assert do
|
18
|
+
if @options[:is]
|
19
|
+
@min_value, @max_value = @options[:is], @options[:is]
|
20
|
+
elsif @options[:within]
|
21
|
+
@min_value, @max_value = @options[:within].first, @options[:within].last
|
22
|
+
elsif @options[:maximum]
|
23
|
+
@min_value, @max_value = nil, @options[:maximum]
|
24
|
+
elsif @options[:minimum]
|
25
|
+
@min_value, @max_value = @options[:minimum], nil
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
protected
|
30
|
+
def allow_nil?
|
31
|
+
super(default_message_for(:too_short))
|
32
|
+
end
|
33
|
+
|
34
|
+
def allow_blank?
|
35
|
+
super(default_message_for(:too_short))
|
36
|
+
end
|
37
|
+
|
38
|
+
def less_than_min_length?
|
39
|
+
@min_value.nil? || @min_value <= 1 || bad?(@min_value - 1, default_message_for(:too_short))
|
40
|
+
end
|
41
|
+
|
42
|
+
def exactly_min_length?
|
43
|
+
@min_value.nil? || @min_value <= 0 || good?(@min_value, default_message_for(:too_short))
|
44
|
+
end
|
45
|
+
|
46
|
+
def more_than_max_length?
|
47
|
+
@max_value.nil? || bad?(@max_value + 1, default_message_for(:too_long))
|
48
|
+
end
|
49
|
+
|
50
|
+
def exactly_max_length?
|
51
|
+
@max_value.nil? || @min_value == @max_value || good?(@max_value, default_message_for(:too_long))
|
52
|
+
end
|
53
|
+
|
54
|
+
def interpolation_options
|
55
|
+
{ :minimum => @min_value, :maximum => @max_value }
|
56
|
+
end
|
57
|
+
|
58
|
+
# Returns the default message for the validation type.
|
59
|
+
# If user supplied :message, it will return it. Otherwise it will return
|
60
|
+
# wrong_length on :is validation and :too_short or :too_long in the other
|
61
|
+
# types.
|
62
|
+
#
|
63
|
+
def default_message_for(validation_type)
|
64
|
+
return :message if @options[:message]
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# Validates the length of the given attributes. You have also to supply
|
69
|
+
# one of the following options: minimum, maximum, is or within.
|
70
|
+
#
|
71
|
+
# Note: this method is also aliased as <tt>validate_size_of</tt>.
|
72
|
+
#
|
73
|
+
# == Options
|
74
|
+
#
|
75
|
+
# * <tt>:minimum</tt> - The minimum size of the attribute.
|
76
|
+
# * <tt>:maximum</tt> - The maximum size of the attribute.
|
77
|
+
# * <tt>:is</tt> - The exact size of the attribute.
|
78
|
+
# * <tt>:within</tt> - A range specifying the minimum and maximum size of the attribute.
|
79
|
+
# * <tt>:allow_nil</tt> - when supplied, validates if it allows nil or not.
|
80
|
+
# * <tt>:allow_blank</tt> - when supplied, validates if it allows blank or not.
|
81
|
+
# * <tt>:message</tt> - value the test expects to find in <tt>errors.on(:attribute)</tt>.
|
82
|
+
# Regexp, string or symbol. Default = "is invalid"</tt>
|
83
|
+
#
|
84
|
+
# == Examples
|
85
|
+
#
|
86
|
+
# it { should validate_length_of(:password).within(6..20) }
|
87
|
+
# it { should validate_length_of(:password).maximum(20) }
|
88
|
+
# it { should validate_length_of(:password).minimum(6) }
|
89
|
+
# it { should validate_length_of(:age).is(18) }
|
90
|
+
#
|
91
|
+
# should_validate_length_of :password, :within => 6..20
|
92
|
+
# should_validate_length_of :password, :maximum => 20
|
93
|
+
# should_validate_length_of :password, :minimum => 6
|
94
|
+
# should_validate_length_of :age, :is => 18
|
95
|
+
#
|
96
|
+
# should_validate_length_of :password do |m|
|
97
|
+
# m.minimum 6
|
98
|
+
# m.maximum 20
|
99
|
+
# end
|
100
|
+
#
|
101
|
+
def validate_length_of(*attributes, &block)
|
102
|
+
ValidateLengthOfMatcher.new(*attributes, &block).spec(self)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Remarkable
|
2
|
+
module MongoMapper
|
3
|
+
module Matchers
|
4
|
+
class ValidatePresenceOfMatcher < Remarkable::MongoMapper::Base
|
5
|
+
arguments :collection => :attributes, :as => :attribute
|
6
|
+
optional :message
|
7
|
+
|
8
|
+
collection_assertions :allow_nil?
|
9
|
+
default_options :message => "can't be empty"
|
10
|
+
|
11
|
+
protected
|
12
|
+
|
13
|
+
def allow_nil?
|
14
|
+
bad?(nil, :message)
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
# Ensures that the model cannot be saved if one of the attributes listed is not present.
|
20
|
+
#
|
21
|
+
# == Options
|
22
|
+
#
|
23
|
+
# * <tt>:message</tt> - value the test expects to find in <tt>errors.on(:attribute)</tt>.
|
24
|
+
# Regexp, string or symbol. Default = "can't be empty"
|
25
|
+
#
|
26
|
+
# == Examples
|
27
|
+
#
|
28
|
+
# should_validate_presence_of :name, :phone_number
|
29
|
+
# it { should validate_presence_of(:name, :phone_number) }
|
30
|
+
#
|
31
|
+
def validate_presence_of(*args, &block)
|
32
|
+
ValidatePresenceOfMatcher.new(*args, &block).spec(self)
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|