jnunemaker-mongomapper 0.1.2 → 0.2.0

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.
@@ -2,6 +2,33 @@ module MongoMapper
2
2
  class FinderOptions
3
3
  attr_reader :options
4
4
 
5
+ def self.to_mongo_criteria(conditions)
6
+ conditions = conditions.dup
7
+ criteria = {}
8
+ conditions.each_pair do |field, value|
9
+ case value
10
+ when Array
11
+ criteria[field] = {'$in' => value}
12
+ when Hash
13
+ criteria[field] = to_mongo_criteria(value)
14
+ else
15
+ criteria[field] = value
16
+ end
17
+ end
18
+
19
+ criteria
20
+ end
21
+
22
+ def self.to_mongo_options(options)
23
+ options = options.dup
24
+ {
25
+ :fields => to_mongo_fields(options.delete(:fields) || options.delete(:select)),
26
+ :offset => (options.delete(:offset) || 0).to_i,
27
+ :limit => (options.delete(:limit) || 0).to_i,
28
+ :sort => to_mongo_sort(options.delete(:order))
29
+ }
30
+ end
31
+
5
32
  def initialize(options)
6
33
  raise ArgumentError, "FinderOptions must be a hash" unless options.is_a?(Hash)
7
34
  @options = options.symbolize_keys
@@ -9,11 +36,11 @@ module MongoMapper
9
36
  end
10
37
 
11
38
  def criteria
12
- convert_conditions(@conditions.dup)
39
+ self.class.to_mongo_criteria(@conditions)
13
40
  end
14
41
 
15
42
  def options
16
- convert_options(@options.dup)
43
+ self.class.to_mongo_options(@options)
17
44
  end
18
45
 
19
46
  def to_a
@@ -21,46 +48,21 @@ module MongoMapper
21
48
  end
22
49
 
23
50
  private
24
- def convert_conditions(conditions)
25
- criteria = {}
26
- conditions.each_pair do |field, value|
27
- case value
28
- when Array
29
- criteria[field] = {'$in' => value}
30
- when Hash
31
- criteria[field] = convert_conditions(value)
32
- else
33
- criteria[field] = value
34
- end
35
- end
36
-
37
- criteria
38
- end
39
-
40
- def convert_options(options)
41
- {
42
- :fields => convert_fields(options.delete(:fields) || options.delete(:select)),
43
- :offset => (options.delete(:offset) || 0).to_i,
44
- :limit => (options.delete(:limit) || 0).to_i,
45
- :sort => convert_sort(options.delete(:order))
46
- }
47
- end
48
-
49
- def convert_fields(fields)
51
+ def self.to_mongo_fields(fields)
50
52
  return if fields.blank?
51
-
53
+
52
54
  if fields.is_a?(String)
53
55
  fields.split(',').map { |field| field.strip }
54
56
  else
55
57
  fields.flatten.compact
56
58
  end
57
59
  end
58
-
59
- def convert_sort(sort)
60
+
61
+ def self.to_mongo_sort(sort)
60
62
  return if sort.blank?
61
63
  pieces = sort.split(',')
62
- pairs = pieces.map { |s| convert_sort_piece(s) }
63
-
64
+ pairs = pieces.map { |s| to_mongo_sort_piece(s) }
65
+
64
66
  hash = OrderedHash.new
65
67
  pairs.each do |pair|
66
68
  field, sort_direction = pair
@@ -68,8 +70,8 @@ module MongoMapper
68
70
  end
69
71
  hash.symbolize_keys
70
72
  end
71
-
72
- def convert_sort_piece(str)
73
+
74
+ def self.to_mongo_sort_piece(str)
73
75
  field, direction = str.strip.split(' ')
74
76
  direction ||= 'ASC'
75
77
  direction = direction.upcase == 'ASC' ? 1 : -1
@@ -1,34 +1,35 @@
1
1
  class Boolean; end
2
+ class Ref; end
2
3
 
3
4
  module MongoMapper
4
5
  class Key
5
6
  # DateTime and Date are currently not supported by mongo's bson so just use Time
6
- NativeTypes = [String, Float, Time, Integer, Boolean, Array, Hash]
7
-
7
+ NativeTypes = [String, Float, Time, Integer, Boolean, Array, Hash, Ref]
8
+
8
9
  attr_accessor :name, :type, :options, :default_value
9
-
10
+
10
11
  def initialize(name, type, options={})
11
12
  @name, @type = name.to_s, type
12
13
  self.options = options.symbolize_keys
13
14
  self.default_value = options.delete(:default)
14
15
  end
15
-
16
+
16
17
  def ==(other)
17
18
  @name == other.name && @type == other.type
18
19
  end
19
-
20
+
20
21
  def set(value)
21
22
  typecast(value)
22
23
  end
23
-
24
+
24
25
  def native?
25
26
  @native ||= NativeTypes.include?(type)
26
27
  end
27
-
28
+
28
29
  def embedded_document?
29
30
  type.ancestors.include?(EmbeddedDocument) && !type.ancestors.include?(Document)
30
31
  end
31
-
32
+
32
33
  def get(value)
33
34
  return default_value if value.nil? && !default_value.nil?
34
35
  if type == Array
@@ -39,7 +40,7 @@ module MongoMapper
39
40
  value
40
41
  end
41
42
  end
42
-
43
+
43
44
  private
44
45
  def typecast(value)
45
46
  return HashWithIndifferentAccess.new(value) if value.is_a?(Hash) && type == Hash
@@ -73,7 +74,7 @@ module MongoMapper
73
74
  value
74
75
  end
75
76
  end
76
-
77
+
77
78
  def typecast_embedded_document(value)
78
79
  value.is_a?(type) ? value : type.new(value)
79
80
  end
@@ -0,0 +1,90 @@
1
+ require 'observer'
2
+ require 'singleton'
3
+ require 'set'
4
+
5
+ module MongoMapper
6
+ module Observing #:nodoc:
7
+ def self.included(model)
8
+ model.class_eval do
9
+ extend Observable
10
+ extend ClassMethods
11
+ end
12
+ end
13
+
14
+ module ClassMethods
15
+ def observers=(*observers)
16
+ @observers = observers.flatten
17
+ end
18
+
19
+ def observers
20
+ @observers ||= []
21
+ end
22
+
23
+ def instantiate_observers
24
+ return if @observers.blank?
25
+ @observers.each do |observer|
26
+ if observer.respond_to?(:to_sym) # Symbol or String
27
+ observer.to_s.camelize.constantize.instance
28
+ elsif observer.respond_to?(:instance)
29
+ observer.instance
30
+ else
31
+ raise ArgumentError, "#{observer} must be a lowercase, underscored class name (or an instance of the class itself) responding to the instance method. Example: Person.observers = :big_brother # calls BigBrother.instance"
32
+ end
33
+ end
34
+ end
35
+
36
+ protected
37
+ def inherited(subclass)
38
+ super
39
+ changed
40
+ notify_observers :observed_class_inherited, subclass
41
+ end
42
+ end
43
+ end
44
+
45
+ class Observer
46
+ include Singleton
47
+
48
+ class << self
49
+ def observe(*models)
50
+ models.flatten!
51
+ models.collect! { |model| model.is_a?(Symbol) ? model.to_s.camelize.constantize : model }
52
+ define_method(:observed_classes) { Set.new(models) }
53
+ end
54
+
55
+ def observed_class
56
+ if observed_class_name = name[/(.*)Observer/, 1]
57
+ observed_class_name.constantize
58
+ else
59
+ nil
60
+ end
61
+ end
62
+ end
63
+
64
+ def initialize
65
+ Set.new(observed_classes + observed_subclasses).each { |klass| add_observer! klass }
66
+ end
67
+
68
+ def update(observed_method, object) #:nodoc:
69
+ send(observed_method, object) if respond_to?(observed_method)
70
+ end
71
+
72
+ def observed_class_inherited(subclass) #:nodoc:
73
+ self.class.observe(observed_classes + [subclass])
74
+ add_observer!(subclass)
75
+ end
76
+
77
+ protected
78
+ def observed_classes
79
+ Set.new([self.class.observed_class].compact.flatten)
80
+ end
81
+
82
+ def observed_subclasses
83
+ observed_classes.sum([]) { |klass| klass.send(:subclasses) }
84
+ end
85
+
86
+ def add_observer!(klass)
87
+ klass.add_observer(self)
88
+ end
89
+ end
90
+ end
@@ -5,14 +5,17 @@ module MongoMapper
5
5
  alias_method :new_record?, :new?
6
6
  extend ClassMethods
7
7
  end
8
+ class << model
9
+ alias_method :has_many, :many
10
+ end
8
11
  end
9
-
12
+
10
13
  module ClassMethods
11
14
  def column_names
12
15
  keys.keys
13
16
  end
14
17
  end
15
-
18
+
16
19
  def to_param
17
20
  id
18
21
  end
@@ -2,48 +2,18 @@ module MongoMapper
2
2
  module SaveWithValidation
3
3
  def self.included(base)
4
4
  base.class_eval do
5
- alias_method_chain :valid?, :callbacks
6
5
  alias_method_chain :save, :validation
7
-
8
- define_callbacks :before_validation_on_create, :before_validation_on_update,
9
- :before_validation, :after_validation,
10
- :validate, :validate_on_create, :validate_on_update
6
+ alias_method_chain :save!, :validation
11
7
  end
12
8
  end
13
9
 
14
- def save!
15
- save_with_validation || raise(DocumentNotValid.new(self))
16
- end
17
-
18
10
  private
19
- def save_with_validation
20
- if valid?
21
- save_without_validation
22
- else
23
- false
24
- end
11
+ def save_with_validation
12
+ valid? ? save_without_validation : false
25
13
  end
26
-
27
- def valid_with_callbacks?
28
- run_callbacks(:before_validation)
29
-
30
- if new?
31
- run_callbacks(:before_validation_on_create)
32
- else
33
- run_callbacks(:before_validation_on_update)
34
- end
35
-
36
- run_callbacks(:validate)
37
-
38
- if new?
39
- run_callbacks(:validate_on_create)
40
- else
41
- run_callbacks(:validate_on_update)
42
- end
43
-
44
- is_valid = valid_without_callbacks?
45
- run_callbacks(:after_validation) if is_valid
46
- is_valid
14
+
15
+ def save_with_validation!
16
+ valid? ? save_without_validation! : raise(DocumentNotValid.new(self))
47
17
  end
48
18
  end
49
19
  end
@@ -0,0 +1,47 @@
1
+ module MongoMapper
2
+ module Validations
3
+ class ValidatesUniquenessOf < Validatable::ValidationBase
4
+ def valid?(instance)
5
+ # TODO: scope
6
+ doc = instance.class.find(:first, :conditions => {self.attribute => instance[attribute]}, :limit => 1)
7
+ doc.nil? || instance.id == doc.id
8
+ end
9
+
10
+ def message(instance)
11
+ super || "has already been taken"
12
+ end
13
+ end
14
+
15
+ class ValidatesExclusionOf < Validatable::ValidationBase
16
+ required_option :within
17
+
18
+ def valid?(instance)
19
+ value = instance[attribute]
20
+ return true if allow_nil && value.nil?
21
+ return true if allow_blank && value.blank?
22
+
23
+ !within.include?(instance[attribute])
24
+ end
25
+
26
+ def message(instance)
27
+ super || "is reserved"
28
+ end
29
+ end
30
+
31
+ class ValidatesInclusionOf < Validatable::ValidationBase
32
+ required_option :within
33
+
34
+ def valid?(instance)
35
+ value = instance[attribute]
36
+ return true if allow_nil && value.nil?
37
+ return true if allow_blank && value.blank?
38
+
39
+ within.include?(value)
40
+ end
41
+
42
+ def message(instance)
43
+ super || "is not in the list"
44
+ end
45
+ end
46
+ end
47
+ end
data/mongomapper.gemspec CHANGED
@@ -2,11 +2,11 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = %q{mongomapper}
5
- s.version = "0.1.2"
5
+ s.version = "0.2.0"
6
6
 
7
7
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
8
  s.authors = ["John Nunemaker"]
9
- s.date = %q{2009-07-03}
9
+ s.date = %q{2009-07-07}
10
10
  s.email = %q{nunemaker@gmail.com}
11
11
  s.extra_rdoc_files = [
12
12
  "LICENSE",
@@ -20,14 +20,25 @@ Gem::Specification.new do |s|
20
20
  "Rakefile",
21
21
  "VERSION",
22
22
  "lib/mongomapper.rb",
23
+ "lib/mongomapper/associations.rb",
24
+ "lib/mongomapper/associations/array_proxy.rb",
25
+ "lib/mongomapper/associations/base.rb",
26
+ "lib/mongomapper/associations/belongs_to_proxy.rb",
27
+ "lib/mongomapper/associations/has_many_embedded_proxy.rb",
28
+ "lib/mongomapper/associations/has_many_proxy.rb",
29
+ "lib/mongomapper/associations/polymorphic_belongs_to_proxy.rb",
30
+ "lib/mongomapper/associations/proxy.rb",
31
+ "lib/mongomapper/callbacks.rb",
23
32
  "lib/mongomapper/document.rb",
24
33
  "lib/mongomapper/embedded_document.rb",
25
34
  "lib/mongomapper/finder_options.rb",
26
35
  "lib/mongomapper/key.rb",
36
+ "lib/mongomapper/observing.rb",
27
37
  "lib/mongomapper/rails_compatibility.rb",
28
38
  "lib/mongomapper/save_with_validation.rb",
29
39
  "lib/mongomapper/serialization.rb",
30
40
  "lib/mongomapper/serializers/json_serializer.rb",
41
+ "lib/mongomapper/validations.rb",
31
42
  "mongomapper.gemspec",
32
43
  "test/serializers/test_json_serializer.rb",
33
44
  "test/test_associations.rb",
@@ -38,6 +49,7 @@ Gem::Specification.new do |s|
38
49
  "test/test_helper.rb",
39
50
  "test/test_key.rb",
40
51
  "test/test_mongomapper.rb",
52
+ "test/test_observing.rb",
41
53
  "test/test_rails_compatibility.rb",
42
54
  "test/test_serializations.rb",
43
55
  "test/test_validations.rb"
@@ -59,6 +71,7 @@ Gem::Specification.new do |s|
59
71
  "test/test_helper.rb",
60
72
  "test/test_key.rb",
61
73
  "test/test_mongomapper.rb",
74
+ "test/test_observing.rb",
62
75
  "test/test_rails_compatibility.rb",
63
76
  "test/test_serializations.rb",
64
77
  "test/test_validations.rb"
@@ -71,20 +84,20 @@ Gem::Specification.new do |s|
71
84
  if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
72
85
  s.add_runtime_dependency(%q<activesupport>, [">= 0"])
73
86
  s.add_runtime_dependency(%q<mongodb-mongo>, ["= 0.9"])
74
- s.add_runtime_dependency(%q<jnunemaker-validatable>, ["= 1.7.0"])
87
+ s.add_runtime_dependency(%q<jnunemaker-validatable>, ["= 1.7.1"])
75
88
  s.add_development_dependency(%q<mocha>, ["= 0.9.4"])
76
89
  s.add_development_dependency(%q<jnunemaker-matchy>, ["= 0.4.0"])
77
90
  else
78
91
  s.add_dependency(%q<activesupport>, [">= 0"])
79
92
  s.add_dependency(%q<mongodb-mongo>, ["= 0.9"])
80
- s.add_dependency(%q<jnunemaker-validatable>, ["= 1.7.0"])
93
+ s.add_dependency(%q<jnunemaker-validatable>, ["= 1.7.1"])
81
94
  s.add_dependency(%q<mocha>, ["= 0.9.4"])
82
95
  s.add_dependency(%q<jnunemaker-matchy>, ["= 0.4.0"])
83
96
  end
84
97
  else
85
98
  s.add_dependency(%q<activesupport>, [">= 0"])
86
99
  s.add_dependency(%q<mongodb-mongo>, ["= 0.9"])
87
- s.add_dependency(%q<jnunemaker-validatable>, ["= 1.7.0"])
100
+ s.add_dependency(%q<jnunemaker-validatable>, ["= 1.7.1"])
88
101
  s.add_dependency(%q<mocha>, ["= 0.9.4"])
89
102
  s.add_dependency(%q<jnunemaker-matchy>, ["= 0.4.0"])
90
103
  end