remarkable_mongo_ign 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +2 -0
- data/LICENSE +20 -0
- data/README.md +23 -0
- data/Rakefile +31 -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/remarkable_mongo.gemspec +79 -0
- data/remarkable_mongo_ign.gemspec +81 -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 +126 -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::Plugins::Keys::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
|