Key abbreviation #353

Closed
wants to merge 10 commits into
from
@@ -0,0 +1,44 @@
+$LOAD_PATH.unshift(File.expand_path('../../lib', __FILE__))
+require 'mongo_mapper'
+require 'pp'
+
+MongoMapper.database = 'testing'
+
+class Goat
+ include MongoMapper::Document
+ belongs_to :user
+
+ key :user_id, ObjectId, :abbr => :g_id
+end
+
+class User
+ include MongoMapper::Document
+ key :email_address, String, :abbr => :e
+ key :hate_to_store_this_long_key_for_each_document, String, :abbr => :'l'
+
+ many :goats, :class_name => 'Goat', :foreign_key => :g_id
+end
+
+# create, update objects like normal
+user = User.create(:hate_to_store_this_long_key_for_each_document => "tiny")
+puts MongoMapper.connection['testing']['users'].find({"_id" => user.id}).first
+
+user.update_attribute(:email_address, 'IHeartSmallDB@gmail.com')
+puts MongoMapper.connection['testing']['users'].find({"_id" => user.id}).first
+
+# query like normal, object returned will respond to human readable keys
+u = User.find(user.id)
+puts "email address is: #{u.email_address}"
+puts "long-key is: #{u.hate_to_store_this_long_key_for_each_document}"
+puts "obj inspect looks like: #{u.inspect}"
+puts "raw mongo looks like: #{MongoMapper.connection['testing']['users'].find({"_id" => user.id}).first}"
+
+#dynamic finders work as expected
+u2 = User.find_by_email_address('IHeartSmallDB@gmail.com')
+puts u2.inspect
+
+#abbreviations are useful for associations, just need to set the foreign_key in the parent
+u2.goats << Goat.new()
+u2.reload
+puts "the users goats: #{u2.goats.inspect}"
+puts "and the goats' owner: #{u2.goats[0].user.inspect}"
@@ -13,7 +13,7 @@ def dynamic_find(finder, args)
finder.attributes.each_with_index do |attr, index|
attributes[attr] = args[index]
end
-
+
options = args.extract_options!.merge(attributes)
if result = send(finder.finder, options)
@@ -16,16 +16,25 @@ module Keys
module ClassMethods
def inherited(descendant)
descendant.instance_variable_set(:@keys, keys.dup)
+ descendant.instance_variable_set(:@keys_by_abbr, keys_by_abbr.dup)
super
end
def keys
@keys ||= {}
end
+
+ # Added To Master by DS.
+ def keys_by_abbr
+ @keys_by_abbr ||= {}
+ end
+ # Altered From Master by DS.
+ # Need to store abbreviation map
def key(*args)
Key.new(*args).tap do |key|
keys[key.name] = key
+ keys_by_abbr[key.abbr.to_s] = key if key.abbr
create_accessors_for(key)
create_key_in_descendants(*args)
create_indexes_for(key)
@@ -66,6 +75,22 @@ def load(attrs)
self
end.allocate.initialize_from_database(attrs)
end
+
+ def key_name_for_abbr(key_abbr)
+ if key = keys_by_abbr[key_abbr.to_s]
+ key.name
+ else
+ key_abbr
+ end
+ end
+
+ def abbr_for_key_name(key_name)
+ if key = keys[key_name.to_s]
+ key.abbr || key.name
+ else
+ key_name
+ end
+ end
private
def key_accessors_module_defined?
@@ -210,7 +235,30 @@ def attributes
end
end
end
- alias :to_mongo :attributes
@brianhempel

brianhempel Mar 14, 2012

Contributor

Is #attributes still around?

@brianhempel

brianhempel Mar 14, 2012

Contributor

nm I was confused, #attributes is still around.

+
+ # Altered From Master by DS.
+ # MongoMapper aliases this to attributes. But when storing, we need to store using abbreviation.
+ # So there is a small tweak when creating the Hash for simple keys.
+ def to_mongo
+ HashWithIndifferentAccess.new.tap do |attrs|
+ keys.each do |name, key|
+ if key.type == ObjectId || !self[key.name].nil?
+ value = key.set(self[key.name])
+ attrs[key.abbr || name] = value
+ end
+ end
+
+ embedded_associations.each do |association|
+ if documents = instance_variable_get(association.ivar)
+ if association.is_a?(Associations::OneAssociation)
+ attrs[association.name] = documents.to_mongo
+ else
+ attrs[association.name] = documents.map &:to_mongo
+ end
+ end
+ end
+ end
+ end
def assign(attrs={})
warn "[DEPRECATION] #assign is deprecated, use #attributes="
@@ -243,7 +291,7 @@ def id=(value)
self[:_id] = value
end
-
+
def read_key(key_name)
instance_key = :"@#{key_name}"
if instance_variable_defined? instance_key
@@ -264,6 +312,11 @@ def []=(name, value)
def keys
self.class.keys
end
+
+ # Added To Master by DS.
+ def keys_by_abbr
+ self.class.keys_by_abbr
+ end
def key_names
@key_names ||= keys.keys
@@ -278,9 +331,12 @@ def embedded_keys
end
private
+ # Altered From Master by DS.
+ # The key read from the DB is the abbreviation. Need to store it as the full key in the model.
def load_from_database(attrs)
return if attrs == nil or attrs.blank?
attrs.each do |key, value|
+ key = key_name_for_abbr(key)
if respond_to?(:"#{key}=") && !self.class.key?(key)
self.send(:"#{key}=", value)
else
@@ -299,6 +355,16 @@ def set_parent_document(key, value)
value._parent_document = self
end
end
+
+ # Added To Master by DS.
+ def key_name_for_abbr(key)
+ self.class.key_name_for_abbr(key)
+ end
+
+ # Added To Master by DS.
+ def abbr_for_key_name(key)
+ self.class.abbr_for_key_name(key)
+ end
def write_key(name, value)
key = keys[name.to_s]
@@ -3,10 +3,15 @@ module MongoMapper
module Plugins
module Keys
class Key
- attr_accessor :name, :type, :options, :default
+
+ # Altered From Master by DS.
+ # Storing :abbr, the key's abbreviation when stored in the DB
+ attr_accessor :name, :type, :options, :default, :abbr
ID_STR = '_id'
+ # Altered From Master by DS.
+ # Need to set self.abbr if included in key's optoins.
def initialize(*args)
options_from_args = args.extract_options!
@name, @type = args.shift.to_s, args.shift
@@ -15,10 +20,14 @@ def initialize(*args)
if options.key?(:default)
self.default = self.options[:default]
end
+
+ if options.key?(:abbr)
+ self.abbr = self.options[:abbr]
+ end
end
def ==(other)
- @name == other.name && @type == other.type
+ @name == other.name && @type == other.type && @abbr == other.abbr
end
def embeddable?
@@ -38,7 +38,34 @@ def find!(*ids)
end
end
end
-
+
+ # Added To Master by DS.
+ # These methods interact with driver, need to use abbreviation, not full attribute name.
+ # Plucky performs the following aliases (we don't need the aliased method):
+ # alias_method :each, :find_each
+ # alias_method :first, :find_one
+ # alias_method :size, :count
+ # alias_method :exist?, :exists?
+ # alias_method :filter, :where
+ # alias_method :to_a, :all
+ %w(last all where find_one find_each exists? count).each do |meth|
+ define_method(meth.to_sym) do |*args, &block|
+ rewrite_keys_with_abbr(args)
+ super(*args, &block)
+ end
+ end
+
+ # Added To Master by DS.
+ # These methods interact with driver, need to use abbreviation, not full attribute name.
+ # Plucky performs the following aliases (we don't need the aliased method):
+ # alias_method :order, :sort
+ %w(fields sort).each do |meth|
+ define_method(meth.to_sym) do |*args, &block|
+ args = rewrite_args_with_abbr(args)
+ super(*args, &block)
+ end
+ end
+
private
def method_missing(method, *args, &block)
return super unless model.respond_to?(method)
@@ -49,6 +76,38 @@ def method_missing(method, *args, &block)
result
end
end
+
+ # Added To Master by DS.
+ # These methods interact with driver, need to use abbreviation, not full attribute name.
+ def rewrite_keys_with_abbr(args)
+ if args[0].is_a? Hash and model.respond_to? :abbr_for_key_name
+ abbreviated_attrs = {}
+ args[0].each do |k,v|
+ if k.is_a? SymbolOperator
+ abbreviated_attrs[SymbolOperator.new( model.abbr_for_key_name(k.field.to_s).to_sym, k.operator )] = v
+ else
+ abbreviated_attrs[model.abbr_for_key_name(k)] = v
+ end
+ end
+ args[0] = abbreviated_attrs
+ end
+ end
+
+ # Added To Master by DS.
+ # These methods interact with driver, need to use abbreviation, not full attribute name.
+ def rewrite_args_with_abbr(args)
+ if args.is_a? Array and model.respond_to? :abbr_for_key_name
+ abbreviated_args = []
+ args.each do |a|
+ if a.is_a? SymbolOperator
+ abbreviated_args << SymbolOperator.new( model.abbr_for_key_name(a.field.to_s).to_sym, a.operator )
+ else
+ abbreviated_args << model.abbr_for_key_name(a)
+ end
+ end
+ end
+ abbreviated_args
+ end
end
end
end
@@ -43,4 +43,36 @@ class AwesomePost
post1 = post1.reload
post1.tags.should == [tag1]
end
+
+ should "interoperate with abbreviated keys" do
+ class AnotherUser
+ include MongoMapper::Document
+
+ many :goats, :class_name => 'AssociationsTest::AwesomeGoat', :foreign_key => :u_id
+ end
+ AnotherUser.collection.remove
+
+ class AwesomeGoat
+ include MongoMapper::Document
+
+ key :name, String, :abbr => :n
+ key :user_id, ObjectId, :abbr => :u_id
+
+ belongs_to :user, :class_name => 'AssociationsTest::AnotherUser'
+ end
+ AwesomeGoat.collection.remove
+
+ user = AnotherUser.create
+ user.goats << goat1 = AwesomeGoat.new(:name => "g6")
+ user.goats << goat2 = AwesomeGoat.new(:name => "g7")
+
+ user.reload
+ user.goats.should == [goat1, goat2]
+
+ goat1.reload
+ goat1.user.should == user
+
+ goat2.reload
+ goat2.user.should == user
+ end
end
Oops, something went wrong.