samlown-couchrest 0.35

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.
Files changed (105) hide show
  1. data/LICENSE +176 -0
  2. data/README.md +46 -0
  3. data/Rakefile +67 -0
  4. data/THANKS.md +19 -0
  5. data/examples/model/example.rb +144 -0
  6. data/examples/word_count/markov +38 -0
  7. data/examples/word_count/views/books/chunked-map.js +3 -0
  8. data/examples/word_count/views/books/united-map.js +1 -0
  9. data/examples/word_count/views/markov/chain-map.js +6 -0
  10. data/examples/word_count/views/markov/chain-reduce.js +7 -0
  11. data/examples/word_count/views/word_count/count-map.js +6 -0
  12. data/examples/word_count/views/word_count/count-reduce.js +3 -0
  13. data/examples/word_count/word_count.rb +46 -0
  14. data/examples/word_count/word_count_query.rb +40 -0
  15. data/examples/word_count/word_count_views.rb +26 -0
  16. data/history.txt +114 -0
  17. data/lib/couchrest/commands/generate.rb +71 -0
  18. data/lib/couchrest/commands/push.rb +103 -0
  19. data/lib/couchrest/core/adapters/restclient.rb +35 -0
  20. data/lib/couchrest/core/database.rb +377 -0
  21. data/lib/couchrest/core/design.rb +79 -0
  22. data/lib/couchrest/core/document.rb +84 -0
  23. data/lib/couchrest/core/http_abstraction.rb +48 -0
  24. data/lib/couchrest/core/response.rb +16 -0
  25. data/lib/couchrest/core/rest_api.rb +49 -0
  26. data/lib/couchrest/core/server.rb +88 -0
  27. data/lib/couchrest/core/view.rb +4 -0
  28. data/lib/couchrest/helper/pager.rb +103 -0
  29. data/lib/couchrest/helper/streamer.rb +51 -0
  30. data/lib/couchrest/helper/upgrade.rb +51 -0
  31. data/lib/couchrest/middlewares/logger.rb +263 -0
  32. data/lib/couchrest/mixins/attachments.rb +31 -0
  33. data/lib/couchrest/mixins/attribute_protection.rb +74 -0
  34. data/lib/couchrest/mixins/callbacks.rb +532 -0
  35. data/lib/couchrest/mixins/class_proxy.rb +124 -0
  36. data/lib/couchrest/mixins/collection.rb +260 -0
  37. data/lib/couchrest/mixins/design_doc.rb +103 -0
  38. data/lib/couchrest/mixins/document_queries.rb +80 -0
  39. data/lib/couchrest/mixins/extended_attachments.rb +70 -0
  40. data/lib/couchrest/mixins/extended_document_mixins.rb +9 -0
  41. data/lib/couchrest/mixins/properties.rb +154 -0
  42. data/lib/couchrest/mixins/validation.rb +246 -0
  43. data/lib/couchrest/mixins/views.rb +173 -0
  44. data/lib/couchrest/mixins.rb +4 -0
  45. data/lib/couchrest/monkeypatches.rb +113 -0
  46. data/lib/couchrest/more/casted_model.rb +58 -0
  47. data/lib/couchrest/more/extended_document.rb +310 -0
  48. data/lib/couchrest/more/property.rb +50 -0
  49. data/lib/couchrest/more/typecast.rb +175 -0
  50. data/lib/couchrest/support/blank.rb +42 -0
  51. data/lib/couchrest/support/class.rb +190 -0
  52. data/lib/couchrest/support/rails.rb +42 -0
  53. data/lib/couchrest/validation/auto_validate.rb +157 -0
  54. data/lib/couchrest/validation/contextual_validators.rb +78 -0
  55. data/lib/couchrest/validation/validation_errors.rb +125 -0
  56. data/lib/couchrest/validation/validators/absent_field_validator.rb +74 -0
  57. data/lib/couchrest/validation/validators/confirmation_validator.rb +107 -0
  58. data/lib/couchrest/validation/validators/format_validator.rb +122 -0
  59. data/lib/couchrest/validation/validators/formats/email.rb +66 -0
  60. data/lib/couchrest/validation/validators/formats/url.rb +43 -0
  61. data/lib/couchrest/validation/validators/generic_validator.rb +120 -0
  62. data/lib/couchrest/validation/validators/length_validator.rb +139 -0
  63. data/lib/couchrest/validation/validators/method_validator.rb +89 -0
  64. data/lib/couchrest/validation/validators/numeric_validator.rb +109 -0
  65. data/lib/couchrest/validation/validators/required_field_validator.rb +114 -0
  66. data/lib/couchrest.rb +162 -0
  67. data/spec/couchrest/core/couchrest_spec.rb +184 -0
  68. data/spec/couchrest/core/database_spec.rb +840 -0
  69. data/spec/couchrest/core/design_spec.rb +138 -0
  70. data/spec/couchrest/core/document_spec.rb +275 -0
  71. data/spec/couchrest/core/server_spec.rb +35 -0
  72. data/spec/couchrest/helpers/pager_spec.rb +122 -0
  73. data/spec/couchrest/helpers/streamer_spec.rb +52 -0
  74. data/spec/couchrest/more/attribute_protection_spec.rb +150 -0
  75. data/spec/couchrest/more/casted_extended_doc_spec.rb +79 -0
  76. data/spec/couchrest/more/casted_model_spec.rb +406 -0
  77. data/spec/couchrest/more/extended_doc_attachment_spec.rb +135 -0
  78. data/spec/couchrest/more/extended_doc_inherited_spec.rb +40 -0
  79. data/spec/couchrest/more/extended_doc_spec.rb +797 -0
  80. data/spec/couchrest/more/extended_doc_subclass_spec.rb +98 -0
  81. data/spec/couchrest/more/extended_doc_view_spec.rb +456 -0
  82. data/spec/couchrest/more/property_spec.rb +628 -0
  83. data/spec/fixtures/attachments/README +3 -0
  84. data/spec/fixtures/attachments/couchdb.png +0 -0
  85. data/spec/fixtures/attachments/test.html +11 -0
  86. data/spec/fixtures/more/article.rb +35 -0
  87. data/spec/fixtures/more/card.rb +22 -0
  88. data/spec/fixtures/more/cat.rb +20 -0
  89. data/spec/fixtures/more/course.rb +22 -0
  90. data/spec/fixtures/more/event.rb +8 -0
  91. data/spec/fixtures/more/invoice.rb +17 -0
  92. data/spec/fixtures/more/person.rb +9 -0
  93. data/spec/fixtures/more/question.rb +6 -0
  94. data/spec/fixtures/more/service.rb +12 -0
  95. data/spec/fixtures/more/user.rb +22 -0
  96. data/spec/fixtures/views/lib.js +3 -0
  97. data/spec/fixtures/views/test_view/lib.js +3 -0
  98. data/spec/fixtures/views/test_view/only-map.js +4 -0
  99. data/spec/fixtures/views/test_view/test-map.js +3 -0
  100. data/spec/fixtures/views/test_view/test-reduce.js +3 -0
  101. data/spec/spec.opts +6 -0
  102. data/spec/spec_helper.rb +49 -0
  103. data/utils/remap.rb +27 -0
  104. data/utils/subset.rb +30 -0
  105. metadata +223 -0
@@ -0,0 +1,190 @@
1
+ # Copyright (c) 2006-2009 David Heinemeier Hansson
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.
21
+ #
22
+ # Extracted From
23
+ # http://github.com/rails/rails/commit/971e2438d98326c994ec6d3ef8e37b7e868ed6e2
24
+
25
+ # Extends the class object with class and instance accessors for class attributes,
26
+ # just like the native attr* accessors for instance attributes.
27
+ #
28
+ # class Person
29
+ # cattr_accessor :hair_colors
30
+ # end
31
+ #
32
+ # Person.hair_colors = [:brown, :black, :blonde, :red]
33
+ class Class
34
+ def cattr_reader(*syms)
35
+ syms.flatten.each do |sym|
36
+ next if sym.is_a?(Hash)
37
+ class_eval(<<-EOS, __FILE__, __LINE__ + 1)
38
+ unless defined? @@#{sym} # unless defined? @@hair_colors
39
+ @@#{sym} = nil # @@hair_colors = nil
40
+ end # end
41
+ #
42
+ def self.#{sym} # def self.hair_colors
43
+ @@#{sym} # @@hair_colors
44
+ end # end
45
+ #
46
+ def #{sym} # def hair_colors
47
+ @@#{sym} # @@hair_colors
48
+ end # end
49
+ EOS
50
+ end
51
+ end unless Class.respond_to?(:cattr_reader)
52
+
53
+ def cattr_writer(*syms)
54
+ options = syms.extract_options!
55
+ syms.flatten.each do |sym|
56
+ class_eval(<<-EOS, __FILE__, __LINE__ + 1)
57
+ unless defined? @@#{sym} # unless defined? @@hair_colors
58
+ @@#{sym} = nil # @@hair_colors = nil
59
+ end # end
60
+ #
61
+ def self.#{sym}=(obj) # def self.hair_colors=(obj)
62
+ @@#{sym} = obj # @@hair_colors = obj
63
+ end # end
64
+ #
65
+ #{" #
66
+ def #{sym}=(obj) # def hair_colors=(obj)
67
+ @@#{sym} = obj # @@hair_colors = obj
68
+ end # end
69
+ " unless options[:instance_writer] == false } # # instance writer above is generated unless options[:instance_writer] == false
70
+ EOS
71
+ end
72
+ end unless Class.respond_to?(:cattr_writer)
73
+
74
+ def cattr_accessor(*syms)
75
+ cattr_reader(*syms)
76
+ cattr_writer(*syms)
77
+ end unless Class.respond_to?(:cattr_accessor)
78
+
79
+ # Defines class-level inheritable attribute reader. Attributes are available to subclasses,
80
+ # each subclass has a copy of parent's attribute.
81
+ #
82
+ # @param *syms<Array[#to_s]> Array of attributes to define inheritable reader for.
83
+ # @return <Array[#to_s]> Array of attributes converted into inheritable_readers.
84
+ #
85
+ # @api public
86
+ #
87
+ # @todo Do we want to block instance_reader via :instance_reader => false
88
+ # @todo It would be preferable that we do something with a Hash passed in
89
+ # (error out or do the same as other methods above) instead of silently
90
+ # moving on). In particular, this makes the return value of this function
91
+ # less useful.
92
+ def extlib_inheritable_reader(*ivars)
93
+ instance_reader = ivars.pop[:reader] if ivars.last.is_a?(Hash)
94
+
95
+ ivars.each do |ivar|
96
+ self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
97
+ def self.#{ivar}
98
+ return @#{ivar} if self.object_id == #{self.object_id} || defined?(@#{ivar})
99
+ ivar = superclass.#{ivar}
100
+ return nil if ivar.nil? && !#{self}.instance_variable_defined?("@#{ivar}")
101
+ @#{ivar} = ivar && !ivar.is_a?(Module) && !ivar.is_a?(Numeric) && !ivar.is_a?(TrueClass) && !ivar.is_a?(FalseClass) ? ivar.dup : ivar
102
+ end
103
+ RUBY
104
+ unless instance_reader == false
105
+ self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
106
+ def #{ivar}
107
+ self.class.#{ivar}
108
+ end
109
+ RUBY
110
+ end
111
+ end
112
+ end unless Class.respond_to?(:extlib_inheritable_reader)
113
+
114
+ # Defines class-level inheritable attribute writer. Attributes are available to subclasses,
115
+ # each subclass has a copy of parent's attribute.
116
+ #
117
+ # @param *syms<Array[*#to_s, Hash{:instance_writer => Boolean}]> Array of attributes to
118
+ # define inheritable writer for.
119
+ # @option syms :instance_writer<Boolean> if true, instance-level inheritable attribute writer is defined.
120
+ # @return <Array[#to_s]> An Array of the attributes that were made into inheritable writers.
121
+ #
122
+ # @api public
123
+ #
124
+ # @todo We need a style for class_eval <<-HEREDOC. I'd like to make it
125
+ # class_eval(<<-RUBY, __FILE__, __LINE__), but we should codify it somewhere.
126
+ def extlib_inheritable_writer(*ivars)
127
+ instance_writer = ivars.pop[:writer] if ivars.last.is_a?(Hash)
128
+ ivars.each do |ivar|
129
+ self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
130
+ def self.#{ivar}=(obj)
131
+ @#{ivar} = obj
132
+ end
133
+ RUBY
134
+ unless instance_writer == false
135
+ self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
136
+ def #{ivar}=(obj) self.class.#{ivar} = obj end
137
+ RUBY
138
+ end
139
+
140
+ self.send("#{ivar}=", yield) if block_given?
141
+ end
142
+ end unless Class.respond_to?(:extlib_inheritable_writer)
143
+
144
+ # Defines class-level inheritable attribute accessor. Attributes are available to subclasses,
145
+ # each subclass has a copy of parent's attribute.
146
+ #
147
+ # @param *syms<Array[*#to_s, Hash{:instance_writer => Boolean}]> Array of attributes to
148
+ # define inheritable accessor for.
149
+ # @option syms :instance_writer<Boolean> if true, instance-level inheritable attribute writer is defined.
150
+ # @return <Array[#to_s]> An Array of attributes turned into inheritable accessors.
151
+ #
152
+ # @api public
153
+ def extlib_inheritable_accessor(*syms, &block)
154
+ extlib_inheritable_reader(*syms)
155
+ extlib_inheritable_writer(*syms, &block)
156
+ end unless Class.respond_to?(:extlib_inheritable_accessor)
157
+ end
158
+
159
+ class Array
160
+ # Extracts options from a set of arguments. Removes and returns the last
161
+ # element in the array if it's a hash, otherwise returns a blank hash.
162
+ #
163
+ # def options(*args)
164
+ # args.extract_options!
165
+ # end
166
+ #
167
+ # options(1, 2) # => {}
168
+ # options(1, 2, :a => :b) # => {:a=>:b}
169
+ def extract_options!
170
+ last.is_a?(::Hash) ? pop : {}
171
+ end unless Array.new.respond_to?(:extract_options!)
172
+
173
+ # Wraps the object in an Array unless it's an Array. Converts the
174
+ # object to an Array using #to_ary if it implements that.
175
+ def self.wrap(object)
176
+ case object
177
+ when nil
178
+ []
179
+ when self
180
+ object
181
+ else
182
+ if object.respond_to?(:to_ary)
183
+ object.to_ary
184
+ else
185
+ [object]
186
+ end
187
+ end
188
+ end unless Array.respond_to?(:wrap)
189
+ end
190
+
@@ -0,0 +1,42 @@
1
+ # This file contains various hacks for Rails compatibility.
2
+ class Hash
3
+ # Hack so that CouchRest::Document, which descends from Hash,
4
+ # doesn't appear to Rails routing as a Hash of options
5
+ def self.===(other)
6
+ return false if self == Hash && other.is_a?(CouchRest::Document)
7
+ super
8
+ end
9
+ end
10
+
11
+ CouchRest::Document.class_eval do
12
+ # Need this when passing doc to a resourceful route
13
+ alias_method :to_param, :id
14
+
15
+ # Hack so that CouchRest::Document, which descends from Hash,
16
+ # doesn't appear to Rails routing as a Hash of options
17
+ def is_a?(o)
18
+ return false if o == Hash
19
+ super
20
+ end
21
+ alias_method :kind_of?, :is_a?
22
+ end
23
+
24
+ CouchRest::CastedModel.class_eval do
25
+ # The to_param method is needed for rails to generate resourceful routes.
26
+ # In your controller, remember that it's actually the id of the document.
27
+ def id
28
+ return nil if base_doc.nil?
29
+ base_doc.id
30
+ end
31
+ alias_method :to_param, :id
32
+ end
33
+
34
+ require Pathname.new(File.dirname(__FILE__)).join('..', 'validation', 'validation_errors')
35
+
36
+ CouchRest::Validation::ValidationErrors.class_eval do
37
+ # Returns the total number of errors added. Two errors added to the same attribute will be counted as such.
38
+ # This method is called by error_messages_for
39
+ def count
40
+ errors.values.inject(0) { |error_count, errors_for_attribute| error_count + errors_for_attribute.size }
41
+ end
42
+ end
@@ -0,0 +1,157 @@
1
+ # Ported from dm-migrations
2
+ require File.join(File.dirname(__FILE__), '..', 'support', 'class')
3
+
4
+ module CouchRest
5
+
6
+ class Property
7
+ # flag letting us know if we already checked the autovalidation settings
8
+ attr_accessor :autovalidation_check
9
+ @autovalidation_check = false
10
+ end
11
+
12
+ module Validation
13
+ module AutoValidate
14
+
15
+ # # Force the auto validation for the class properties
16
+ # # This feature is still not fully ported over,
17
+ # # test are lacking, so please use with caution
18
+ # def auto_validate!
19
+ # auto_validation = true
20
+ # end
21
+
22
+ # adds message for validator
23
+ def options_with_message(base_options, property, validator_name)
24
+ options = base_options.clone
25
+ opts = property.options
26
+ options[:message] = if opts[:messages]
27
+ if opts[:messages].is_a?(Hash) and msg = opts[:messages][validator_name]
28
+ msg
29
+ else
30
+ nil
31
+ end
32
+ elsif opts[:message]
33
+ opts[:message]
34
+ else
35
+ nil
36
+ end
37
+ options
38
+ end
39
+
40
+
41
+ ##
42
+ # Auto-generate validations for a given property. This will only occur
43
+ # if the option :auto_validation is either true or left undefined.
44
+ #
45
+ # @details [Triggers]
46
+ # Triggers that generate validator creation
47
+ #
48
+ # :nullable => false
49
+ # Setting the option :nullable to false causes a
50
+ # validates_presence_of validator to be automatically created on
51
+ # the property
52
+ #
53
+ # :size => 20 or :length => 20
54
+ # Setting the option :size or :length causes a validates_length_of
55
+ # validator to be automatically created on the property. If the
56
+ # value is a Integer the validation will set :maximum => value if
57
+ # the value is a Range the validation will set :within => value
58
+ #
59
+ # :format => :predefined / lambda / Proc
60
+ # Setting the :format option causes a validates_format_of
61
+ # validator to be automatically created on the property
62
+ #
63
+ # :set => ["foo", "bar", "baz"]
64
+ # Setting the :set option causes a validates_within
65
+ # validator to be automatically created on the property
66
+ #
67
+ # Integer type
68
+ # Using a Integer type causes a validates_numericality_of
69
+ # validator to be created for the property. integer_only
70
+ # is set to true
71
+ #
72
+ # Float type
73
+ # Using a Integer type causes a validates_is_number
74
+ # validator to be created for the property. integer_only
75
+ # is set to false, and precision/scale match the property
76
+ #
77
+ #
78
+ # Messages
79
+ #
80
+ # :messages => {..}
81
+ # Setting :messages hash replaces standard error messages
82
+ # with custom ones. For instance:
83
+ # :messages => {:presence => "Field is required",
84
+ # :format => "Field has invalid format"}
85
+ # Hash keys are: :presence, :format, :length, :is_unique,
86
+ # :is_number, :is_primitive
87
+ #
88
+ # :message => "Some message"
89
+ # It is just shortcut if only one validation option is set
90
+ #
91
+ def auto_generate_validations(property)
92
+ return unless ((property.autovalidation_check != true) && self.auto_validation)
93
+ return if (property.options && (property.options.has_key?(:auto_validation) && !property.options[:auto_validation]) || property.read_only)
94
+ # value is set by the storage system
95
+ opts = {}
96
+ opts[:context] = property.options[:validates] if property.options.has_key?(:validates)
97
+
98
+ # presence
99
+ if opts[:allow_nil] == false
100
+ validates_presence_of property.name, options_with_message(opts, property, :presence)
101
+ end
102
+
103
+ # length
104
+ if property.type == String
105
+ # XXX: maybe length should always return a Range, with the min defaulting to 1
106
+ # 52 being the max set
107
+ len = property.options.fetch(:length, property.options.fetch(:size, 52))
108
+ if len.is_a?(Range)
109
+ opts[:within] = len
110
+ else
111
+ opts[:maximum] = len
112
+ end
113
+ validates_length_of property.name, options_with_message(opts, property, :length)
114
+ end
115
+
116
+ # format
117
+ if property.options.has_key?(:format)
118
+ opts[:with] = property.options[:format]
119
+ # validates_format property.name, opts
120
+ validates_format property.name, options_with_message(opts, property, :format)
121
+ end
122
+
123
+ # uniqueness validator
124
+ if property.options.has_key?(:unique)
125
+ value = property.options[:unique]
126
+ if value.is_a?(Array) || value.is_a?(Symbol)
127
+ # validates_is_unique property.name, :scope => Array(value)
128
+ validates_is_unique property.name, options_with_message({:scope => Array(value)}, property, :is_unique)
129
+ elsif value.is_a?(TrueClass)
130
+ # validates_is_unique property.name
131
+ validates_is_unique property.name, options_with_message({}, property, :is_unique)
132
+ end
133
+ end
134
+
135
+ # within validator
136
+ if property.options.has_key?(:set)
137
+ validates_within property.name, options_with_message({:set => property.options[:set]}, property, :within)
138
+ end
139
+
140
+ # numeric validator
141
+ if "Integer" == property.type
142
+ opts[:integer_only] = true
143
+ validates_numericality_of property.name, options_with_message(opts, property, :is_number)
144
+ elsif Float == property.type
145
+ opts[:precision] = property.precision
146
+ opts[:scale] = property.scale
147
+ validates_numericality_of property.name, options_with_message(opts, property, :is_number)
148
+ end
149
+
150
+ # marked the property has checked
151
+ property.autovalidation_check = true
152
+
153
+ end
154
+
155
+ end # module AutoValidate
156
+ end # module Validation
157
+ end # module CouchRest
@@ -0,0 +1,78 @@
1
+ # Extracted from dm-validations 0.9.10
2
+ #
3
+ # Copyright (c) 2007 Guy van den Berg
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining
6
+ # a copy of this software and associated documentation files (the
7
+ # "Software"), to deal in the Software without restriction, including
8
+ # without limitation the rights to use, copy, modify, merge, publish,
9
+ # distribute, sublicense, and/or sell copies of the Software, and to
10
+ # permit persons to whom the Software is furnished to do so, subject to
11
+ # the following conditions:
12
+ #
13
+ # The above copyright notice and this permission notice shall be
14
+ # included in all copies or substantial portions of the Software.
15
+ #
16
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
+
24
+ module CouchRest
25
+ module Validation
26
+
27
+ ##
28
+ #
29
+ # @author Guy van den Berg
30
+ # @since 0.9
31
+ class ContextualValidators
32
+
33
+ def dump
34
+ contexts.each_pair do |key, context|
35
+ puts "Key=#{key} Context: #{context}"
36
+ end
37
+ end
38
+
39
+ # Get a hash of named context validators for the resource
40
+ #
41
+ # @return <Hash> a hash of validators <GenericValidator>
42
+ def contexts
43
+ @contexts ||= {}
44
+ end
45
+
46
+ # Return an array of validators for a named context
47
+ #
48
+ # @return <Array> An array of validators
49
+ def context(name)
50
+ contexts[name] ||= []
51
+ end
52
+
53
+ # Clear all named context validators off of the resource
54
+ #
55
+ def clear!
56
+ contexts.clear
57
+ end
58
+
59
+ # Execute all validators in the named context against the target
60
+ #
61
+ # @param <Symbol> named_context the context we are validating against
62
+ # @param <Object> target the resource that we are validating
63
+ # @return <Boolean> true if all are valid, otherwise false
64
+ def execute(named_context, target)
65
+ raise(ArgumentError, 'invalid context specified') if !named_context || (contexts.length > 0 && !contexts[named_context])
66
+ target.errors.clear!
67
+ result = true
68
+ context(named_context).each do |validator|
69
+ next unless validator.execute?(target)
70
+ result = false unless validator.call(target)
71
+ end
72
+
73
+ result
74
+ end
75
+
76
+ end # module ContextualValidators
77
+ end # module Validation
78
+ end # module CouchRest
@@ -0,0 +1,125 @@
1
+ # Extracted from dm-validations 0.9.10
2
+ #
3
+ # Copyright (c) 2007 Guy van den Berg
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining
6
+ # a copy of this software and associated documentation files (the
7
+ # "Software"), to deal in the Software without restriction, including
8
+ # without limitation the rights to use, copy, modify, merge, publish,
9
+ # distribute, sublicense, and/or sell copies of the Software, and to
10
+ # permit persons to whom the Software is furnished to do so, subject to
11
+ # the following conditions:
12
+ #
13
+ # The above copyright notice and this permission notice shall be
14
+ # included in all copies or substantial portions of the Software.
15
+ #
16
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
+
24
+ module CouchRest
25
+ module Validation
26
+
27
+ ##
28
+ #
29
+ # @author Guy van den Berg
30
+ # @since 0.9
31
+ class ValidationErrors
32
+
33
+ include Enumerable
34
+
35
+ @@default_error_messages = {
36
+ :absent => '%s must be absent',
37
+ :inclusion => '%s must be one of [%s]',
38
+ :invalid => '%s has an invalid format',
39
+ :confirmation => '%s does not match the confirmation',
40
+ :accepted => "%s is not accepted",
41
+ :nil => '%s must not be nil',
42
+ :blank => '%s must not be blank',
43
+ :length_between => '%s must be between %s and %s characters long',
44
+ :too_long => '%s must be less than %s characters long',
45
+ :too_short => '%s must be more than %s characters long',
46
+ :wrong_length => '%s must be %s characters long',
47
+ :taken => '%s is already taken',
48
+ :not_a_number => '%s must be a number',
49
+ :not_an_integer => '%s must be an integer',
50
+ :greater_than => '%s must be greater than %s',
51
+ :greater_than_or_equal_to => "%s must be greater than or equal to %s",
52
+ :equal_to => "%s must be equal to %s",
53
+ :less_than => '%s must be less than %s',
54
+ :less_than_or_equal_to => "%s must be less than or equal to %s",
55
+ :value_between => '%s must be between %s and %s',
56
+ :primitive => '%s must be of type %s'
57
+ }
58
+
59
+ # Holds a hash with all the default error messages that can be replaced by your own copy or localizations.
60
+ cattr_writer :default_error_messages
61
+
62
+ def self.default_error_message(key, field, *values)
63
+ field = CouchRest.humanize(field)
64
+ @@default_error_messages[key] % [field, *values].flatten
65
+ end
66
+
67
+ # Clear existing validation errors.
68
+ def clear!
69
+ errors.clear
70
+ end
71
+
72
+ # Add a validation error. Use the field_name :general if the errors does
73
+ # not apply to a specific field of the Resource.
74
+ #
75
+ # @param <Symbol> field_name the name of the field that caused the error
76
+ # @param <String> message the message to add
77
+ def add(field_name, message)
78
+ (errors[field_name.to_sym] ||= []) << message
79
+ end
80
+
81
+ # Collect all errors into a single list.
82
+ def full_messages
83
+ errors.inject([]) do |list, pair|
84
+ list += pair.last
85
+ end
86
+ end
87
+
88
+ # Return validation errors for a particular field_name.
89
+ #
90
+ # @param <Symbol> field_name the name of the field you want an error for
91
+ def on(field_name)
92
+ errors_for_field = errors[field_name.to_sym]
93
+ errors_for_field.blank? ? nil : errors_for_field
94
+ end
95
+
96
+ def each
97
+ errors.map.each do |k, v|
98
+ next if v.blank?
99
+ yield(v)
100
+ end
101
+ end
102
+
103
+ def empty?
104
+ entries.empty?
105
+ end
106
+
107
+ # Return size of errors hash
108
+ #
109
+ # Allows us to play nicely with Rails' helpers
110
+ def count
111
+ errors.size
112
+ end
113
+
114
+ def method_missing(meth, *args, &block)
115
+ errors.send(meth, *args, &block)
116
+ end
117
+
118
+ private
119
+ def errors
120
+ @errors ||= {}
121
+ end
122
+
123
+ end # class ValidationErrors
124
+ end # module Validation
125
+ end # module CouchRest
@@ -0,0 +1,74 @@
1
+ # Extracted from dm-validations 0.9.10
2
+ #
3
+ # Copyright (c) 2007 Guy van den Berg
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining
6
+ # a copy of this software and associated documentation files (the
7
+ # "Software"), to deal in the Software without restriction, including
8
+ # without limitation the rights to use, copy, modify, merge, publish,
9
+ # distribute, sublicense, and/or sell copies of the Software, and to
10
+ # permit persons to whom the Software is furnished to do so, subject to
11
+ # the following conditions:
12
+ #
13
+ # The above copyright notice and this permission notice shall be
14
+ # included in all copies or substantial portions of the Software.
15
+ #
16
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
+
24
+ module CouchRest
25
+ module Validation
26
+
27
+ ##
28
+ #
29
+ # @author Guy van den Berg
30
+ class AbsentFieldValidator < GenericValidator
31
+
32
+ def initialize(field_name, options={})
33
+ super
34
+ @field_name, @options = field_name, options
35
+ end
36
+
37
+ def call(target)
38
+ value = target.send(field_name)
39
+ return true if (value.nil? || (value.respond_to?(:empty?) && value.empty?))
40
+
41
+ error_message = @options[:message] || ValidationErrors.default_error_message(:absent, field_name)
42
+ add_error(target, error_message, field_name)
43
+
44
+ return false
45
+ end
46
+ end # class AbsentFieldValidator
47
+
48
+ module ValidatesAbsent
49
+
50
+ ##
51
+ #
52
+ # @example [Usage]
53
+ #
54
+ # class Page
55
+ #
56
+ # property :unwanted_attribute, String
57
+ # property :another_unwanted, String
58
+ # property :yet_again, String
59
+ #
60
+ # validates_absent :unwanted_attribute
61
+ # validates_absent :another_unwanted, :yet_again
62
+ #
63
+ # # a call to valid? will return false unless
64
+ # # all three attributes are blank
65
+ # end
66
+ #
67
+ def validates_absent(*fields)
68
+ opts = opts_from_validator_args(fields)
69
+ add_validator_to_context(opts, fields, CouchRest::Validation::AbsentFieldValidator)
70
+ end
71
+
72
+ end # module ValidatesAbsent
73
+ end # module Validation
74
+ end # module CouchRest