PageRenderTime 71ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/mongo_mapper/plugins/keys.rb

http://github.com/jnunemaker/mongomapper
Ruby | 469 lines | 386 code | 77 blank | 6 comment | 71 complexity | 6c696eac8cf5f9d77dc8b1e28da7f308 MD5 | raw file
  1. # encoding: UTF-8
  2. require 'mongo_mapper/plugins/keys/key'
  3. require 'mongo_mapper/plugins/keys/static'
  4. module MongoMapper
  5. module Plugins
  6. module Keys
  7. extend ActiveSupport::Concern
  8. IS_RUBY_1_9 = method(:const_defined?).arity == 1
  9. included do
  10. extend ActiveSupport::DescendantsTracker
  11. key :_id, ObjectId, :default => lambda { BSON::ObjectId.new }
  12. end
  13. module ClassMethods
  14. def inherited(descendant)
  15. descendant.instance_variable_set(:@keys, keys.dup)
  16. super
  17. end
  18. def keys
  19. @keys ||= {}
  20. end
  21. def dynamic_keys
  22. @dynamic_keys ||= Hash[*unaliased_keys.select {|k, v| v.dynamic? }.flatten(1)]
  23. end
  24. def defined_keys
  25. @defined_keys ||= Hash[*unaliased_keys.select {|k, v| !v.dynamic? }.flatten(1)]
  26. end
  27. def unaliased_keys
  28. @unaliased_keys ||= Hash[*keys.select {|k, v| k == v.name }.flatten(1)]
  29. end
  30. def dealias_keys(hash)
  31. out = {}
  32. hash.each do |k, v|
  33. key = keys[k.to_s]
  34. name = key && key.abbr || k
  35. out[name] = k.to_s.match(/^\$/) && v.is_a?(Hash) ? dealias_keys(v) : v
  36. end
  37. out
  38. end
  39. def dealias_key(name)
  40. key = keys[name.to_s]
  41. key && key.abbr || k
  42. end
  43. alias_method :dealias, :dealias_keys
  44. alias_method :unalias, :dealias_keys
  45. def key(*args)
  46. Key.new(*args).tap do |key|
  47. keys[key.name] = key
  48. keys[key.abbr] = key if key.abbr
  49. create_accessors_for(key) if key.valid_ruby_name? && !key.reserved_name?
  50. create_key_in_descendants(*args)
  51. create_indexes_for(key)
  52. create_validations_for(key)
  53. @dynamic_keys = @defined_keys = @unaliased_keys = @object_id_keys = nil
  54. end
  55. end
  56. def remove_key(name)
  57. if key = keys[name.to_s]
  58. keys.delete key.name
  59. keys.delete key.abbr
  60. remove_method key.name if respond_to? "#{key.name}"
  61. remove_method "#{key.name}=" if respond_to? "#{key.name}="
  62. remove_method "#{key.name}?" if respond_to? "#{key.name}?"
  63. remove_method "#{key.name}_before_type_cast" if respond_to? "#{key.name}_before_type_cast"
  64. remove_key_in_descendants key.name
  65. remove_validations_for key.name
  66. @dynamic_keys = @defined_keys = @unaliased_keys = @object_id_keys = nil
  67. end
  68. end
  69. def persisted_name(name)
  70. if key = keys[name.to_s]
  71. key.persisted_name
  72. else
  73. name
  74. end
  75. end
  76. alias_method :abbr, :persisted_name
  77. def key?(key)
  78. keys.key? key.to_s
  79. end
  80. def using_object_id?
  81. object_id_key?(:_id)
  82. end
  83. def object_id_keys
  84. @object_id_keys ||= unaliased_keys.keys.select { |key| keys[key].type == ObjectId }.map(&:to_sym)
  85. end
  86. def object_id_key?(name)
  87. object_id_keys.include?(name.to_sym)
  88. end
  89. def to_mongo(instance)
  90. instance && instance.to_mongo
  91. end
  92. def from_mongo(value)
  93. value && (value.instance_of?(self) ? value : load(value))
  94. end
  95. # load is overridden in identity map to ensure same objects are loaded
  96. def load(attrs, with_cast = false)
  97. return nil if attrs.nil?
  98. begin
  99. attrs['_type'] ? attrs['_type'].constantize : self
  100. rescue NameError
  101. self
  102. end.allocate.initialize_from_database(attrs, with_cast)
  103. end
  104. private
  105. def key_accessors_module_defined?
  106. # :nocov:
  107. if IS_RUBY_1_9
  108. const_defined?('MongoMapperKeys')
  109. else
  110. const_defined?('MongoMapperKeys', false)
  111. end
  112. # :nocov:
  113. end
  114. def accessors_module
  115. if key_accessors_module_defined?
  116. const_get 'MongoMapperKeys'
  117. else
  118. const_set 'MongoMapperKeys', Module.new
  119. end
  120. end
  121. def create_accessors_for(key)
  122. accessors = ""
  123. if key.read_accessor?
  124. accessors << <<-end_eval
  125. def #{key.name}
  126. read_key(:#{key.name})
  127. end
  128. def #{key.name}_before_type_cast
  129. read_key_before_type_cast(:#{key.name})
  130. end
  131. end_eval
  132. end
  133. if key.write_accessor?
  134. accessors << <<-end_eval
  135. def #{key.name}=(value)
  136. write_key(:#{key.name}, value)
  137. end
  138. end_eval
  139. end
  140. if key.predicate_accessor?
  141. accessors << <<-end_eval
  142. def #{key.name}?
  143. read_key(:#{key.name}).present?
  144. end
  145. end_eval
  146. end
  147. if block_given?
  148. accessors_module.module_eval do
  149. yield
  150. end
  151. end
  152. accessors_module.module_eval accessors
  153. include accessors_module
  154. end
  155. def create_key_in_descendants(*args)
  156. descendants.each { |descendant| descendant.key(*args) }
  157. end
  158. def remove_key_in_descendants(name)
  159. descendants.each { |descendant| descendant.remove_key(name) }
  160. end
  161. def create_indexes_for(key)
  162. if key.options[:index] && !key.embeddable?
  163. warn "[DEPRECATION] :index option when defining key #{key.name.inspect} is deprecated. Put indexes in `db/indexes.rb`"
  164. ensure_index key.name
  165. end
  166. end
  167. def create_validations_for(key)
  168. attribute = key.name.to_sym
  169. if key.options[:required]
  170. if key.type == Boolean
  171. validates_inclusion_of attribute, :in => [true, false]
  172. else
  173. validates_presence_of(attribute)
  174. end
  175. end
  176. if key.options[:unique]
  177. validates_uniqueness_of(attribute)
  178. end
  179. if key.options[:numeric]
  180. number_options = key.type == Integer ? {:only_integer => true} : {}
  181. validates_numericality_of(attribute, number_options)
  182. end
  183. if key.options[:format]
  184. validates_format_of(attribute, :with => key.options[:format])
  185. end
  186. if key.options[:in]
  187. validates_inclusion_of(attribute, :in => key.options[:in])
  188. end
  189. if key.options[:not_in]
  190. validates_exclusion_of(attribute, :in => key.options[:not_in])
  191. end
  192. if key.options[:length]
  193. length_options = case key.options[:length]
  194. when Integer
  195. {:minimum => 0, :maximum => key.options[:length]}
  196. when Range
  197. {:within => key.options[:length]}
  198. when Hash
  199. key.options[:length]
  200. end
  201. validates_length_of(attribute, length_options)
  202. end
  203. end
  204. def remove_validations_for(name)
  205. name = name.to_sym
  206. a_name = [name]
  207. _validators.reject!{ |key, _| key == name }
  208. remove_validate_callbacks a_name
  209. end
  210. def remove_validate_callbacks(a_name)
  211. chain = _validate_callbacks.dup.reject do |callback|
  212. f = callback.raw_filter
  213. f.respond_to?(:attributes) && f.attributes == a_name
  214. end
  215. reset_callbacks(:validate)
  216. chain.each do |callback|
  217. set_callback 'validate', callback.raw_filter
  218. end
  219. end
  220. end
  221. def initialize(attrs={})
  222. @_new = true
  223. init_ivars
  224. initialize_default_values(attrs)
  225. self.attributes = attrs
  226. yield self if block_given?
  227. end
  228. def initialize_from_database(attrs={}, with_cast = false)
  229. @_new = false
  230. init_ivars
  231. initialize_default_values(attrs)
  232. load_from_database(attrs, with_cast)
  233. self
  234. end
  235. def persisted?
  236. !new? && !destroyed?
  237. end
  238. def attributes=(attrs)
  239. return if attrs == nil || attrs.blank?
  240. attrs.each_pair do |key, value|
  241. if respond_to?(:"#{key}=")
  242. self.send(:"#{key}=", value)
  243. else
  244. self[key] = value
  245. end
  246. end
  247. end
  248. def to_mongo(include_abbreviatons = true)
  249. BSON::OrderedHash.new.tap do |attrs|
  250. self.class.unaliased_keys.each do |name, key|
  251. value = self.read_key(key.name)
  252. if key.type == ObjectId || !value.nil?
  253. attrs[include_abbreviatons && key.persisted_name || name] = key.set(value)
  254. end
  255. end
  256. embedded_associations.each do |association|
  257. if documents = instance_variable_get(association.ivar)
  258. if association.is_a?(Associations::OneAssociation)
  259. attrs[association.name] = documents.to_mongo
  260. else
  261. attrs[association.name] = documents.map(&:to_mongo)
  262. end
  263. end
  264. end
  265. end
  266. end
  267. def attributes
  268. to_mongo(false).with_indifferent_access
  269. end
  270. def assign(attrs={})
  271. warn "[DEPRECATION] #assign is deprecated, use #attributes="
  272. self.attributes = attrs
  273. end
  274. def update_attributes(attrs={})
  275. self.attributes = attrs
  276. save
  277. end
  278. def update_attributes!(attrs={})
  279. self.attributes = attrs
  280. save!
  281. end
  282. def update_attribute(name, value)
  283. self.send(:"#{name}=", value)
  284. save(:validate => false)
  285. end
  286. def id
  287. self[:_id]
  288. end
  289. def id=(value)
  290. if self.class.using_object_id?
  291. value = ObjectId.to_mongo(value)
  292. end
  293. self[:_id] = value
  294. end
  295. def keys
  296. self.class.keys
  297. end
  298. def read_key(key_name)
  299. key_name_sym = key_name.to_sym
  300. if @_dynamic_attributes && @_dynamic_attributes.key?(key_name_sym)
  301. @_dynamic_attributes[key_name_sym]
  302. elsif key = keys[key_name.to_s]
  303. if key.ivar && instance_variable_defined?(key.ivar)
  304. value = instance_variable_get(key.ivar)
  305. else
  306. if key.ivar
  307. instance_variable_set key.ivar, key.get(nil)
  308. else
  309. @_dynamic_attributes[key_name_sym] = key.get(nil)
  310. end
  311. end
  312. end
  313. end
  314. def [](key_name); read_key(key_name); end
  315. def attribute(key_name); read_key(key_name); end
  316. def []=(name, value)
  317. write_key(name, value)
  318. end
  319. def key_names
  320. @key_names ||= keys.keys
  321. end
  322. def non_embedded_keys
  323. @non_embedded_keys ||= keys.values.select { |key| !key.embeddable? }
  324. end
  325. def embedded_keys
  326. @embedded_keys ||= keys.values.select(&:embeddable?)
  327. end
  328. protected
  329. def unalias_key(name)
  330. name = name.to_s
  331. if key = keys[name]
  332. key.name
  333. else
  334. name
  335. end
  336. end
  337. private
  338. def init_ivars
  339. @__mm_keys = self.class.keys # Not dumpable
  340. @__mm_default_keys = @__mm_keys.values.select(&:default?) # Not dumpable
  341. @_dynamic_attributes = {} # Dumpable
  342. end
  343. def load_from_database(attrs, with_cast = false)
  344. return if attrs == nil || attrs.blank?
  345. attrs.each do |key, value|
  346. if !@__mm_keys.key?(key) && respond_to?(:"#{key}=")
  347. self.send(:"#{key}=", value)
  348. else
  349. internal_write_key key, value, with_cast
  350. end
  351. end
  352. end
  353. def set_parent_document(key, value)
  354. if key.type and value.instance_of?(key.type) && key.embeddable? && value.respond_to?(:_parent_document)
  355. value._parent_document = self
  356. end
  357. end
  358. # This exists to be patched over by plugins, while letting us still get to the undecorated
  359. # version of the method.
  360. def write_key(name, value)
  361. init_ivars unless @__mm_keys
  362. internal_write_key(name.to_s, value)
  363. end
  364. def internal_write_key(name, value, cast = true)
  365. key = @__mm_keys[name] || dynamic_key(name)
  366. as_mongo = cast ? key.set(value) : value
  367. as_typecast = key.get(as_mongo)
  368. if key.ivar
  369. if key.embeddable?
  370. set_parent_document(key, value)
  371. set_parent_document(key, as_typecast)
  372. end
  373. instance_variable_set key.ivar, as_typecast
  374. else
  375. @_dynamic_attributes[key.name.to_sym] = as_typecast
  376. end
  377. @attributes = nil
  378. value
  379. end
  380. def dynamic_key(name)
  381. self.class.key(name, :__dynamic => true)
  382. end
  383. def initialize_default_values(except = {})
  384. @__mm_default_keys.each do |key|
  385. if !(except && except.key?(key.name))
  386. internal_write_key key.name, key.default_value, false
  387. end
  388. end
  389. end
  390. end
  391. end
  392. end