Skip to content

Commit

Permalink
fix issue with mongoid 2.0.2 when using custom metadata + upgrade rak…
Browse files Browse the repository at this point in the history
…e / rubygems version
  • Loading branch information
did committed Jun 4, 2011
1 parent 6f5bf01 commit ea4f635
Show file tree
Hide file tree
Showing 10 changed files with 304 additions and 65 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
pkg/
spec/tmp
rdoc/
3 changes: 2 additions & 1 deletion Gemfile
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
source :rubygems

gem 'rake'
gem 'rdoc'

gem 'bson_ext', '~> 1.3.0'
gem 'mongoid', '2.0.1'
gem 'mongoid', '2.0.2'
gem 'activesupport', '>= 3.0.7'
gem 'locomotive_carrierwave', :require => 'carrierwave'
gem 'SystemTimer', :platforms => :ruby_18
Expand Down
12 changes: 6 additions & 6 deletions Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
GIT
remote: git://github.com/floehopper/mocha.git
revision: 6da1242f26b12a24c4fcf67bf5921a25bc1bc88dadeb8ae0189963cf3024f2b37d5348a7feaae11e
revision: 6da1242f26b12a24c4fcf67bf5921a25bc1bc88d
specs:
mocha (0.9.12.20110213002255)

Expand Down Expand Up @@ -28,12 +28,12 @@ GEM
activesupport (~> 3.0)
mongo (1.3.1)
bson (>= 1.3.1)
mongoid (2.0.1)
mongoid (2.0.2)
activemodel (~> 3.0)
mongo (~> 1.3)
tzinfo (~> 0.3.22)
will_paginate (~> 3.0.pre)
rake (0.9.0)
rake (0.9.1)
rdoc (3.6.1)
rspec (2.0.1)
rspec-core (~> 2.0.1)
rspec-expectations (~> 2.0.1)
Expand All @@ -60,7 +60,6 @@ GEM
ruby_core_source (0.1.5)
archive-tar-minitar (>= 0.5.2)
tzinfo (0.3.27)
will_paginate (3.0.pre2)

PLATFORMS
ruby
Expand All @@ -72,8 +71,9 @@ DEPENDENCIES
database_cleaner
locomotive_carrierwave
mocha!
mongoid (= 2.0.1)
mongoid (= 2.0.2)
rake
rdoc
rspec (~> 2.0.0)
ruby-debug
ruby-debug19
51 changes: 25 additions & 26 deletions Rakefile
Original file line number Diff line number Diff line change
@@ -1,23 +1,22 @@
require "rubygems"
require "rake"
require "rake/rdoctask"
require "rspec"
require "rspec/core/rake_task"
require 'rake/gempackagetask'

require 'rubygems'
require 'rake'
require 'rdoc/task'
require 'rspec'
require 'rspec/core/rake_task'
require 'rubygems/package_task'

gemspec = eval(File.read('custom_fields.gemspec'))
Rake::GemPackageTask.new(gemspec) do |pkg|
Gem::PackageTask.new(gemspec) do |pkg|
pkg.gem_spec = gemspec
end

desc "build the gem and release it to rubygems.org"
desc 'build the gem and release it to rubygems.org'
task :release => :gem do
sh "gem push pkg/custom_fields-#{gemspec.version}.gem"
sh 'gem push pkg/custom_fields-#{gemspec.version}.gem'
end

desc 'Generate documentation for the custom_fields plugin.'
Rake::RDocTask.new(:rdoc) do |rdoc|
RDoc::Task.new do |rdoc|
rdoc.rdoc_dir = 'rdoc'
rdoc.title = 'CustomFields'
rdoc.options << '--line-numbers' << '--inline-source'
Expand All @@ -26,24 +25,24 @@ Rake::RDocTask.new(:rdoc) do |rdoc|
end

Rspec::Core::RakeTask.new('spec:unit') do |spec|
spec.pattern = "spec/unit/**/*_spec.rb"
# spec.pattern = "spec/unit/proxy_class_caching_spec.rb"
# spec.pattern = "spec/unit/proxy_class_enabler_spec.rb"
# spec.pattern = "spec/unit/custom_field_spec.rb"
# spec.pattern = "spec/unit/custom_fields_for_spec.rb"
# spec.pattern = "spec/unit/types/category_spec.rb"
# spec.pattern = "spec/unit/types/date_spec.rb"
# spec.pattern = "spec/unit/types/default_spec.rb"
# spec.pattern = "spec/unit/types/*_spec.rb"
spec.pattern = 'spec/unit/**/*_spec.rb'
# spec.pattern = 'spec/unit/proxy_class_caching_spec.rb'
# spec.pattern = 'spec/unit/proxy_class_enabler_spec.rb'
# spec.pattern = 'spec/unit/custom_field_spec.rb'
# spec.pattern = 'spec/unit/custom_fields_for_spec.rb'
# spec.pattern = 'spec/unit/types/category_spec.rb'
# spec.pattern = 'spec/unit/types/date_spec.rb'
# spec.pattern = 'spec/unit/types/default_spec.rb'
# spec.pattern = 'spec/unit/types/*_spec.rb'
end

Rspec::Core::RakeTask.new('spec:integration') do |spec|
spec.pattern = "spec/integration/**/*_spec.rb"
# spec.pattern = "spec/integration/custom_fields_for_spec.rb"
# spec.pattern = "spec/integration/types/category_spec.rb"
# spec.pattern = "spec/integration/types/has_one_spec.rb"
# spec.pattern = "spec/integration/types/has_many_spec.rb"
# spec.pattern = "spec/integration/types/has_*_spec.rb"
spec.pattern = 'spec/integration/**/*_spec.rb'
# spec.pattern = 'spec/integration/custom_fields_for_spec.rb'
# spec.pattern = 'spec/integration/types/category_spec.rb'
# spec.pattern = 'spec/integration/types/has_one_spec.rb'
# spec.pattern = 'spec/integration/types/has_many_spec.rb'
# spec.pattern = 'spec/integration/types/has_*_spec.rb'
end

task :spec => ['spec:unit', 'spec:integration']
Expand Down
9 changes: 5 additions & 4 deletions lib/custom_fields.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,25 @@
require 'carrierwave/orm/mongoid'

module CustomFields

@@options = {
:reserved_aliases => Mongoid.destructive_fields
}

def self.options=(options)
@@options.merge!(options)
end

def self.options
@@options
end

end

require 'custom_fields/version'
require 'custom_fields/extensions/mongoid/document'
require 'custom_fields/extensions/mongoid/relations/accessors'
require 'custom_fields/extensions/mongoid/relations/builders'
require 'custom_fields/types/default'
require 'custom_fields/types/string'
require 'custom_fields/types/text'
Expand Down
141 changes: 129 additions & 12 deletions lib/custom_fields/custom_fields_for.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,43 @@ module CustomFields

module CustomFieldsFor

def self.included(base)
base.extend(ClassMethods)
# def self.included(base)
# base.extend(ClassMethods)
# end

extend ActiveSupport::Concern

included do

cattr_accessor :custom_fields

self.custom_fields = []

end

module InstanceMethods

def custom_fields?(collection_name)
# puts "\t[custom_fields?] #{collection_name.to_s.inspect} / #{self.class.custom_fields.inspect} / #{self.class.custom_fields?(collection_name.to_s).inspect}"
self.class.custom_fields?(collection_name)
end

def clone_metadata_for_custom_fields(metadata)
singular_name = metadata.name.to_s.singularize.gsub(/^_/, '')

klass = self.send(:"fetch_#{singular_name}_klass")

# puts "\t[clone_metadata_for_custom_fields] #{singular_name} / klass = #{klass.inspect}"

# safer to do that because we are going to modify the metadata klass for next operations
metadata.clone.tap do |metadata|
metadata.instance_variable_set(:@klass, klass)
end
# puts "\t[create_relation / #{metadata.name}] klass = #{klass.inspect} / #{metadata.object_id}"

# metadata.instance_variable_set(:@klass, klass)
end

end

# Enhance an embedded collection OR the instance itself (by passing self) by providing methods to manage custom fields.
Expand Down Expand Up @@ -34,34 +69,53 @@ def self.included(base)
#
module ClassMethods

def custom_fields_for(collection_name)
singular_name = collection_name.to_s.singularize
def custom_fields?(collection_name)
self.custom_fields.include?(collection_name.to_s)
end

# generate the custom field for the couple defined by the class and the collection name
def custom_fields_for(collection_name)
singular_name = collection_name.to_s.singularize
dynamic_custom_field_class_name = "#{self.name}#{singular_name.camelize}Field"

unless Object.const_defined?(dynamic_custom_field_class_name)
(klass = Class.new(::CustomFields::Field)).class_eval <<-EOF
embedded_in :#{self.name.underscore}, :inverse_of => :#{singular_name}_custom_fields
EOF
self.declare_embedded_in_definition_in_custom_field(collection_name)

Object.const_set(dynamic_custom_field_class_name, klass)
end
# unless Object.const_defined?(dynamic_custom_field_class_name)
# (klass = Class.new(::CustomFields::Field)).class_eval <<-EOF
# embedded_in :#{self.name.underscore}, :inverse_of => :#{singular_name}_custom_fields
# EOF
#
# Object.const_set(dynamic_custom_field_class_name, klass)
# end

# enhance the class itself
if (itself = %w(itself self).include?(collection_name.to_s))
collection_name, singular_name = '_metadata', 'metadata'

# self.define_custom_field_metadata_relationship

class_eval <<-EOV
embeds_one :#{collection_name}, :class_name => '::CustomFields::Metadata'
def safe_#{singular_name}
self.#{collection_name} || self.build_#{collection_name}
# puts "[safe_#{singular_name}] begin"
# if self.#{collection_name}.nil?
# puts "safe_#{singular_name} is nil"
# else
# puts "safe_#{singular_name} is NOT nil"
# end
#
# puts "build_#{collection_name}...."
foo = self.#{collection_name} || self.build_#{collection_name}
# puts "...done"
foo
end
EOV
end

# record the collection_name
self.custom_fields << collection_name.to_s

# common part
class_eval <<-EOV
field :#{singular_name}_custom_fields_counter, :type => Integer, :default => 0
Expand Down Expand Up @@ -121,6 +175,8 @@ def invalidate_#{singular_name}_klass?
end
EOV

# self.change_metadata_klass(collection_name)

# mongoid tiny patch: for performance optimization (ie: we do want to invalidate klass with custom fields every time we save a field)
unless instance_methods.include?('write_attributes_with_custom_fields')
class_eval do
Expand All @@ -141,6 +197,67 @@ def write_attributes_with_custom_fields(attrs = nil)

end

protected

def dynamic_custom_field_class_name(collection_name)
"#{self.name}#{collection_name.to_s.singularize.camelize}Field"
end

# An embedded relationship has to be defined on both side in order for it
# to work properly. But because custom_field can be embedded in different
# models that it's not aware of, we have to declare manually the definition
# once we know the target class.
def declare_embedded_in_definition_in_custom_field(target_collection_name)
singular_name = target_collection_name.to_s.singularize
klass_name = self.dynamic_custom_field_class_name(target_collection_name)

unless Object.const_defined?(klass_name)
(klass = Class.new(::CustomFields::Field)).class_eval <<-EOF
embedded_in :#{self.name.underscore}, :inverse_of => :#{singular_name}_custom_fields
EOF

Object.const_set(klass_name, klass)
end
end

# def change_metadata_klass(relation_name)
# metadata = self.relations.delete(relation_name)
#
# metadata = metadata.clone # 2 parent instances should not share the exact same option instance
#
# custom_fields = self.send(:"ordered_#{custom_fields_association_name(association_name)}")
#
# klass = metadata.klass.to_klass_with_custom_fields(custom_fields, self, association_name)
#
# puts "\t[create_relation / #{metadata.name}] klass = #{klass.inspect} / #{metadata.object_id}"
#
# metadata.instance_variable_set(:@klass, klass)
#
# self.relations[relation_name] = metadata
# end

# def define_custom_field_metadata_relationship(target_collection_name)
# collection_name, singular_name = '_metadata', 'metadata'
#
# class_eval <<-EOV
# embeds_one :#{collection_name}, :class_name => '::CustomFields::Metadata'
#
# def safe_#{singular_name}
# if self.#{collection_name}.nil?
# puts "safe_#{singular_name} is nil"
# else
# puts "safe_#{singular_name} is NOT nil"
# end
#
# puts "build_#{collection_name}...."
# foo = self.#{collection_name} || self.build_#{collection_name}
# puts "...done"
# foo
# end
#
# EOV
# end

end

end
Expand Down
Loading

0 comments on commit ea4f635

Please sign in to comment.