jnunemaker-mongomapper 0.1.2 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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