mrkurt-mongo_mapper 0.6.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (77) hide show
  1. data/.gitignore +10 -0
  2. data/LICENSE +20 -0
  3. data/README.rdoc +38 -0
  4. data/Rakefile +55 -0
  5. data/VERSION +1 -0
  6. data/bin/mmconsole +60 -0
  7. data/lib/mongo_mapper.rb +139 -0
  8. data/lib/mongo_mapper/associations.rb +72 -0
  9. data/lib/mongo_mapper/associations/base.rb +113 -0
  10. data/lib/mongo_mapper/associations/belongs_to_polymorphic_proxy.rb +26 -0
  11. data/lib/mongo_mapper/associations/belongs_to_proxy.rb +21 -0
  12. data/lib/mongo_mapper/associations/collection.rb +19 -0
  13. data/lib/mongo_mapper/associations/many_documents_as_proxy.rb +26 -0
  14. data/lib/mongo_mapper/associations/many_documents_proxy.rb +115 -0
  15. data/lib/mongo_mapper/associations/many_embedded_polymorphic_proxy.rb +31 -0
  16. data/lib/mongo_mapper/associations/many_embedded_proxy.rb +54 -0
  17. data/lib/mongo_mapper/associations/many_polymorphic_proxy.rb +11 -0
  18. data/lib/mongo_mapper/associations/one_proxy.rb +61 -0
  19. data/lib/mongo_mapper/associations/proxy.rb +111 -0
  20. data/lib/mongo_mapper/callbacks.rb +61 -0
  21. data/lib/mongo_mapper/dirty.rb +117 -0
  22. data/lib/mongo_mapper/document.rb +496 -0
  23. data/lib/mongo_mapper/dynamic_finder.rb +74 -0
  24. data/lib/mongo_mapper/embedded_document.rb +380 -0
  25. data/lib/mongo_mapper/finder_options.rb +145 -0
  26. data/lib/mongo_mapper/key.rb +36 -0
  27. data/lib/mongo_mapper/mongo_mapper.rb +125 -0
  28. data/lib/mongo_mapper/pagination.rb +66 -0
  29. data/lib/mongo_mapper/rails_compatibility/document.rb +15 -0
  30. data/lib/mongo_mapper/rails_compatibility/embedded_document.rb +28 -0
  31. data/lib/mongo_mapper/serialization.rb +54 -0
  32. data/lib/mongo_mapper/serializers/json_serializer.rb +48 -0
  33. data/lib/mongo_mapper/support.rb +192 -0
  34. data/lib/mongo_mapper/validations.rb +39 -0
  35. data/mongo_mapper.gemspec +173 -0
  36. data/specs.watchr +30 -0
  37. data/test/NOTE_ON_TESTING +1 -0
  38. data/test/functional/associations/test_belongs_to_polymorphic_proxy.rb +55 -0
  39. data/test/functional/associations/test_belongs_to_proxy.rb +91 -0
  40. data/test/functional/associations/test_many_documents_as_proxy.rb +246 -0
  41. data/test/functional/associations/test_many_documents_proxy.rb +477 -0
  42. data/test/functional/associations/test_many_embedded_polymorphic_proxy.rb +156 -0
  43. data/test/functional/associations/test_many_embedded_proxy.rb +192 -0
  44. data/test/functional/associations/test_many_polymorphic_proxy.rb +339 -0
  45. data/test/functional/associations/test_one_proxy.rb +131 -0
  46. data/test/functional/test_associations.rb +44 -0
  47. data/test/functional/test_binary.rb +33 -0
  48. data/test/functional/test_callbacks.rb +85 -0
  49. data/test/functional/test_dirty.rb +159 -0
  50. data/test/functional/test_document.rb +1198 -0
  51. data/test/functional/test_embedded_document.rb +135 -0
  52. data/test/functional/test_logger.rb +20 -0
  53. data/test/functional/test_modifiers.rb +242 -0
  54. data/test/functional/test_pagination.rb +95 -0
  55. data/test/functional/test_rails_compatibility.rb +25 -0
  56. data/test/functional/test_string_id_compatibility.rb +72 -0
  57. data/test/functional/test_validations.rb +361 -0
  58. data/test/models.rb +271 -0
  59. data/test/support/custom_matchers.rb +55 -0
  60. data/test/support/timing.rb +16 -0
  61. data/test/test_helper.rb +27 -0
  62. data/test/unit/associations/test_base.rb +182 -0
  63. data/test/unit/associations/test_proxy.rb +91 -0
  64. data/test/unit/serializers/test_json_serializer.rb +189 -0
  65. data/test/unit/test_document.rb +236 -0
  66. data/test/unit/test_dynamic_finder.rb +125 -0
  67. data/test/unit/test_embedded_document.rb +709 -0
  68. data/test/unit/test_finder_options.rb +325 -0
  69. data/test/unit/test_key.rb +172 -0
  70. data/test/unit/test_mongo_mapper.rb +65 -0
  71. data/test/unit/test_pagination.rb +119 -0
  72. data/test/unit/test_rails_compatibility.rb +52 -0
  73. data/test/unit/test_serializations.rb +52 -0
  74. data/test/unit/test_support.rb +346 -0
  75. data/test/unit/test_time_zones.rb +40 -0
  76. data/test/unit/test_validations.rb +503 -0
  77. metadata +239 -0
@@ -0,0 +1,145 @@
1
+ module MongoMapper
2
+ # = Important Note
3
+ # This class is private to MongoMapper and should not be considered part of
4
+ # MongoMapper's public API.
5
+ #
6
+ class FinderOptions
7
+ OptionKeys = [:fields, :select, :skip, :offset, :limit, :sort, :order]
8
+
9
+ def self.normalized_field(field)
10
+ field.to_s == 'id' ? :_id : field
11
+ end
12
+
13
+ def self.normalized_order_direction(direction)
14
+ direction ||= 'ASC'
15
+ direction.upcase == 'ASC' ? 1 : -1
16
+ end
17
+
18
+ def initialize(model, options)
19
+ raise ArgumentError, "Options must be a hash" unless options.is_a?(Hash)
20
+ options = options.symbolize_keys
21
+
22
+ @model = model
23
+ @options = {}
24
+ @conditions = options.delete(:conditions) || {}
25
+
26
+ options.each_pair do |key, value|
27
+ if OptionKeys.include?(key)
28
+ @options[key] = value
29
+ else
30
+ @conditions[key] = value
31
+ end
32
+ end
33
+
34
+ add_sci_scope
35
+ end
36
+
37
+ def criteria
38
+ to_mongo_criteria(@conditions)
39
+ end
40
+
41
+ def options
42
+ fields = @options.delete(:fields) || @options.delete(:select)
43
+ skip = @options.delete(:skip) || @options.delete(:offset) || 0
44
+ limit = @options.delete(:limit) || 0
45
+ sort = @options.delete(:sort) || convert_order_to_sort(@options.delete(:order))
46
+
47
+ {:fields => to_mongo_fields(fields), :skip => skip.to_i, :limit => limit.to_i, :sort => sort}
48
+ end
49
+
50
+ def to_a
51
+ [criteria, options]
52
+ end
53
+
54
+ private
55
+ def to_mongo_criteria(conditions, parent_key=nil)
56
+ criteria = {}
57
+
58
+ conditions.each_pair do |field, value|
59
+ field = self.class.normalized_field(field)
60
+
61
+ if @model.object_id_key?(field) && value.is_a?(String)
62
+ value = Mongo::ObjectID.from_string(value)
63
+ end
64
+
65
+ if field.is_a?(FinderOperator)
66
+ criteria.update(field.to_criteria(value))
67
+ next
68
+ end
69
+
70
+ case value
71
+ when Array
72
+ criteria[field] = operator?(field) ? value : {'$in' => value}
73
+ when Hash
74
+ criteria[field] = to_mongo_criteria(value, field)
75
+ when Time
76
+ criteria[field] = value.utc
77
+ else
78
+ criteria[field] = value
79
+ end
80
+ end
81
+
82
+ criteria
83
+ end
84
+
85
+ def operator?(field)
86
+ field.to_s =~ /^\$/
87
+ end
88
+
89
+ # adds _type single collection inheritance scope for models that need it
90
+ def add_sci_scope
91
+ if @model.single_collection_inherited?
92
+ @conditions[:_type] = @model.to_s
93
+ end
94
+ end
95
+
96
+ def to_mongo_fields(fields)
97
+ return if fields.blank?
98
+
99
+ if fields.respond_to?(:flatten, :compact)
100
+ fields.flatten.compact
101
+ else
102
+ fields.split(',').map { |field| field.strip }
103
+ end
104
+ end
105
+
106
+ def convert_order_to_sort(sort)
107
+ return if sort.blank?
108
+
109
+ if sort.respond_to?(:all?) && sort.all? { |s| s.respond_to?(:to_order) }
110
+ sort.map { |s| s.to_order }
111
+ elsif sort.respond_to?(:to_order)
112
+ [sort.to_order]
113
+ else
114
+ pieces = sort.split(',')
115
+ pieces.map { |s| to_mongo_sort_piece(s) }
116
+ end
117
+ end
118
+
119
+ def to_mongo_sort_piece(str)
120
+ field, direction = str.strip.split(' ')
121
+ direction = FinderOptions.normalized_order_direction(direction)
122
+ [field, direction]
123
+ end
124
+ end
125
+
126
+ class FinderOperator
127
+ def initialize(field, operator)
128
+ @field, @operator = field, operator
129
+ end
130
+
131
+ def to_criteria(value)
132
+ {FinderOptions.normalized_field(@field) => {@operator => value}}
133
+ end
134
+ end
135
+
136
+ class OrderOperator
137
+ def initialize(field, direction)
138
+ @field, @direction = field, direction
139
+ end
140
+
141
+ def to_order
142
+ [@field.to_s, FinderOptions.normalized_order_direction(@direction)]
143
+ end
144
+ end
145
+ end
@@ -0,0 +1,36 @@
1
+ module MongoMapper
2
+ class Key
3
+ attr_accessor :name, :type, :options, :default_value
4
+
5
+ def initialize(*args)
6
+ options = args.extract_options!
7
+ @name, @type = args.shift.to_s, args.shift
8
+ self.options = (options || {}).symbolize_keys
9
+ self.default_value = self.options.delete(:default)
10
+ end
11
+
12
+ def ==(other)
13
+ @name == other.name && @type == other.type
14
+ end
15
+
16
+ def set(value)
17
+ type.to_mongo(value)
18
+ end
19
+
20
+ def embeddable?
21
+ type.respond_to?(:embeddable?) && type.embeddable? ? true : false
22
+ end
23
+
24
+ def number?
25
+ [Integer, Float].include?(type)
26
+ end
27
+
28
+ def get(value)
29
+ if value.nil? && !default_value.nil?
30
+ return default_value
31
+ end
32
+
33
+ type.from_mongo(value)
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,125 @@
1
+ # if Gem is defined i'll assume you are using rubygems and lock specific versions
2
+ # call me crazy but a plain old require will just get the latest version you have installed
3
+ # so i want to make sure that if you are using gems you do in fact have the correct versions
4
+ # if there is a better way to do this, please enlighten me!
5
+ if self.class.const_defined?(:Gem)
6
+ gem 'activesupport', '>= 2.3'
7
+ gem 'mongo', '0.18.2'
8
+ gem 'jnunemaker-validatable', '1.8.1'
9
+ end
10
+
11
+ require 'active_support'
12
+ require 'mongo'
13
+ require 'validatable'
14
+
15
+ module MongoMapper
16
+ # generic MM error
17
+ class MongoMapperError < StandardError; end
18
+
19
+ # raised when key expected to exist but not found
20
+ class KeyNotFound < MongoMapperError; end
21
+
22
+ # raised when document expected but not found
23
+ class DocumentNotFound < MongoMapperError; end
24
+
25
+ # raised when document not valid and using !
26
+ class DocumentNotValid < MongoMapperError
27
+ def initialize(document)
28
+ super("Validation failed: #{document.errors.full_messages.join(", ")}")
29
+ end
30
+ end
31
+
32
+ # @api public
33
+ def self.connection
34
+ @@connection ||= Mongo::Connection.new
35
+ end
36
+
37
+ # @api public
38
+ def self.connection=(new_connection)
39
+ @@connection = new_connection
40
+ end
41
+
42
+ # @api public
43
+ def self.logger
44
+ connection.logger
45
+ end
46
+
47
+ # @api public
48
+ def self.database=(name)
49
+ @@database = nil
50
+ @@database_name = name
51
+ end
52
+
53
+ # @api public
54
+ def self.database
55
+ if @@database_name.blank?
56
+ raise 'You forgot to set the default database name: MongoMapper.database = "foobar"'
57
+ end
58
+
59
+ @@database ||= MongoMapper.connection.db(@@database_name)
60
+ end
61
+
62
+ # @api private
63
+ def self.ensured_indexes
64
+ @@ensured_indexes ||= []
65
+ end
66
+
67
+ # @api private
68
+ def self.ensured_indexes=(value)
69
+ @@ensured_indexes = value
70
+ end
71
+
72
+ # @api private
73
+ def self.ensure_index(klass, keys, options={})
74
+ ensured_indexes << {:klass => klass, :keys => keys, :options => options}
75
+ end
76
+
77
+ # @api public
78
+ def self.ensure_indexes!
79
+ ensured_indexes.each do |index|
80
+ unique = index[:options].delete(:unique)
81
+ index[:klass].collection.create_index(index[:keys], unique)
82
+ end
83
+ end
84
+
85
+ # @api private
86
+ def self.use_time_zone?
87
+ Time.respond_to?(:zone) && Time.zone ? true : false
88
+ end
89
+
90
+ # @api private
91
+ def self.time_class
92
+ use_time_zone? ? Time.zone : Time
93
+ end
94
+
95
+ # @api private
96
+ def self.normalize_object_id(value)
97
+ value.is_a?(String) ? Mongo::ObjectID.from_string(value) : value
98
+ end
99
+ end
100
+
101
+ require 'mongo_mapper/support'
102
+ require 'mongo_mapper/callbacks'
103
+ require 'mongo_mapper/finder_options'
104
+ require 'mongo_mapper/dirty'
105
+ require 'mongo_mapper/dynamic_finder'
106
+ require 'mongo_mapper/key'
107
+ require 'mongo_mapper/pagination'
108
+ require 'mongo_mapper/serialization'
109
+ require 'mongo_mapper/validations'
110
+ require 'mongo_mapper/rails_compatibility/document'
111
+ require 'mongo_mapper/rails_compatibility/embedded_document'
112
+ require 'mongo_mapper/embedded_document'
113
+ require 'mongo_mapper/document'
114
+ require 'mongo_mapper/associations'
115
+ require 'mongo_mapper/associations/base'
116
+ require 'mongo_mapper/associations/proxy'
117
+ require 'mongo_mapper/associations/collection'
118
+ require 'mongo_mapper/associations/many_documents_proxy'
119
+ require 'mongo_mapper/associations/belongs_to_proxy'
120
+ require 'mongo_mapper/associations/belongs_to_polymorphic_proxy'
121
+ require 'mongo_mapper/associations/many_polymorphic_proxy'
122
+ require 'mongo_mapper/associations/many_embedded_proxy'
123
+ require 'mongo_mapper/associations/many_embedded_polymorphic_proxy'
124
+ require 'mongo_mapper/associations/many_documents_as_proxy'
125
+ require 'mongo_mapper/associations/one_proxy'
@@ -0,0 +1,66 @@
1
+ module MongoMapper
2
+ module Pagination
3
+ class PaginationProxy
4
+ instance_methods.each { |m| undef_method m unless m =~ /(^__|^nil\?$|^send$|proxy_|^object_id$)/ }
5
+
6
+ attr_accessor :subject
7
+ attr_reader :total_entries, :per_page, :current_page
8
+ alias limit per_page
9
+
10
+ def initialize(total_entries, current_page, per_page=nil)
11
+ @total_entries = total_entries.to_i
12
+ self.per_page = per_page
13
+ self.current_page = current_page
14
+ end
15
+
16
+ def total_pages
17
+ (total_entries / per_page.to_f).ceil
18
+ end
19
+
20
+ def out_of_bounds?
21
+ current_page > total_pages
22
+ end
23
+
24
+ def previous_page
25
+ current_page > 1 ? (current_page - 1) : nil
26
+ end
27
+
28
+ def next_page
29
+ current_page < total_pages ? (current_page + 1) : nil
30
+ end
31
+
32
+ def skip
33
+ (current_page - 1) * per_page
34
+ end
35
+ alias offset skip # for will paginate support
36
+
37
+ def send(method, *args, &block)
38
+ if respond_to?(method)
39
+ super
40
+ else
41
+ subject.send(method, *args, &block)
42
+ end
43
+ end
44
+
45
+ def ===(other)
46
+ other === subject
47
+ end
48
+
49
+ def method_missing(name, *args, &block)
50
+ @subject.send(name, *args, &block)
51
+ end
52
+
53
+ private
54
+ def per_page=(value)
55
+ value = 25 if value.blank?
56
+ @per_page = value.to_i
57
+ end
58
+
59
+ def current_page=(value)
60
+ value = value.to_i
61
+ value = 1 if value < 1
62
+ @current_page = value
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,15 @@
1
+ module MongoMapper
2
+ module RailsCompatibility
3
+ module Document
4
+ def self.included(model)
5
+ model.class_eval do
6
+ alias_method :new_record?, :new?
7
+
8
+ def human_name
9
+ self.name.demodulize.titleize
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,28 @@
1
+ module MongoMapper
2
+ module RailsCompatibility
3
+ module EmbeddedDocument
4
+ def self.included(model)
5
+ model.class_eval do
6
+ extend ClassMethods
7
+
8
+ alias_method :new_record?, :new?
9
+ end
10
+
11
+ class << model
12
+ alias has_many many
13
+ alias has_one one
14
+ end
15
+ end
16
+
17
+ module ClassMethods
18
+ def column_names
19
+ keys.keys
20
+ end
21
+
22
+ def human_name
23
+ self.name.demodulize.titleize
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,54 @@
1
+ require 'active_support/json'
2
+
3
+ module MongoMapper
4
+ module Serialization
5
+ class Serializer
6
+ attr_reader :options
7
+
8
+ def initialize(record, options={})
9
+ @record, @options = record, options.dup
10
+ end
11
+
12
+ def serializable_key_names
13
+ key_names = @record.attributes.keys
14
+
15
+ if options[:only]
16
+ options.delete(:except)
17
+ key_names = key_names & Array(options[:only]).collect { |n| n.to_s }
18
+ else
19
+ options[:except] = Array(options[:except])
20
+ key_names = key_names - options[:except].collect { |n| n.to_s }
21
+ end
22
+
23
+ key_names
24
+ end
25
+
26
+ def serializable_method_names
27
+ Array(options[:methods]).inject([]) do |method_attributes, name|
28
+ method_attributes << name if @record.respond_to?(name.to_s)
29
+ method_attributes
30
+ end
31
+ end
32
+
33
+ def serializable_names
34
+ serializable_key_names + serializable_method_names
35
+ end
36
+
37
+ def serializable_record
38
+ returning(serializable_record = {}) do
39
+ serializable_names.each { |name| serializable_record[name] = @record.send(name) }
40
+ end
41
+ end
42
+
43
+ def serialize
44
+ # overwrite to implement
45
+ end
46
+
47
+ def to_s(&block)
48
+ serialize(&block)
49
+ end
50
+ end
51
+ end
52
+ end
53
+
54
+ require 'mongo_mapper/serializers/json_serializer'