diff --git a/app/models/asset.rb b/app/models/asset.rb index 69322400..fe4c3041 100644 --- a/app/models/asset.rb +++ b/app/models/asset.rb @@ -24,5 +24,5 @@ class Asset self.content_type == type end end - + end \ No newline at end of file diff --git a/app/models/asset_collection.rb b/app/models/asset_collection.rb index 9150f32b..80b6dae5 100644 --- a/app/models/asset_collection.rb +++ b/app/models/asset_collection.rb @@ -5,11 +5,14 @@ class AssetCollection ## fields ## field :name, :type => String field :slug, :type => String + field :asset_fields_counter, :type => Integer, :default => 0 # FIXME (custom fields) + ## associations ## belongs_to_related :site - embeds_many :assets - # has_many_related :assets + embeds_many :assets, :custom_fields => true + + embeds_many :asset_fields # FIXME (custom fields) ## callbacks ## before_validate :normalize_slug diff --git a/app/models/asset_field.rb b/app/models/asset_field.rb new file mode 100644 index 00000000..b04c3f9c --- /dev/null +++ b/app/models/asset_field.rb @@ -0,0 +1,67 @@ +class AssetField + include Mongoid::Document + include Mongoid::Timestamps + + ## fields ## + field :label, :type => String + field :_alias, :type => String # need it for instance in: > asset.description (description being a custom field) + field :_name, :type => String + field :kind, :type => String + field :position, :type => Integer, :default => 0 + + ## associations ## + embedded_in :collection, :class_name => 'AssetCollection', :inverse_of => :asset_fields + + ## callbacks ## + before_save :set_alias + # before_create :add_to_list_bottom => FIXME _index does the trick actually + # before_save :set_unique_name! + + ## validations ## + validates_presence_of :label, :kind + + ## methods ## + + def field_type + case self.kind + when 'String', 'Text', 'Email' then String + else + self.kind.constantize + end + end + + def apply(object, association_name) + puts "applying...#{self._name} / #{self._alias}" + object.class.send(:set_field, self._name, { :type => self.field_type }) + object.class_eval <<-EOF + alias :#{self._alias} :#{self._name} + EOF + end + + protected + + # def add_to_list_bottom + # self.position = (self.siblings.map(&:position).max || 0) + 1 + # end + + def set_unique_name! + self._name = "custom_field_#{self.increment_counter!}" + end + + def set_alias + return if self.label.blank? && self._alias.blank? + puts "set_alias !!!" + self._alias ||= self.label.clone + self._alias.slugify!(:downcase => true, :underscore => true) + end + + def increment_counter! + next_value = self._parent.send(:"#{self.association_name}_counter") + 1 + self._parent.send(:"#{self.association_name}_counter=", next_value) + next_value + end + + def siblings + self._parent.associations[self.association_name] + end +end \ No newline at end of file diff --git a/config/initializers/custom_fields.rb b/config/initializers/custom_fields.rb new file mode 100644 index 00000000..4afded8c --- /dev/null +++ b/config/initializers/custom_fields.rb @@ -0,0 +1,73 @@ +# encoding: utf-8 +module Mongoid #:nodoc: + module Associations #:nodoc: + class Options #:nodoc: + def custom_fields + @attributes[:custom_fields] == true + end + end + end +end + +# encoding: utf-8 +module Mongoid #:nodoc: + module Associations #:nodoc: + class EmbedsMany < Proxy + def build_with_custom_field_settings(attrs = {}, type = nil) + document = build_without_custom_field_settings(attrs, type) + if self.target_custom_field_association? + document.send(:set_unique_name!) + document.send(:set_alias) + end + document + end + + alias_method_chain :build, :custom_field_settings + + def target_custom_field_association? + target_name = @association_name.gsub(/_fields$/, '').pluralize + # puts "target_name = #{target_name} / #{@parent.associations.key?(target_name).inspect} / #{@parent.inspect} / #{@parent.associations.inspect}" + if @parent.associations.key?(target_name) + @parent.associations[target_name].options.custom_fields + end + end + + end + + end +end + +# encoding: utf-8 +module Mongoid #:nodoc: + module Document + module InstanceMethods + def parentize_with_custom_fields(object, association_name) + # puts "...parentize_with_custom_fields...#{self.inspect} - #{object.inspect} - #{association_name}" + parentize_without_custom_fields(object, association_name) + + if self.custom_fields?(object, association_name) + # puts "custom fields = #{object.asset_fields.inspect}" + puts "((((((((" + object.send(self.custom_fields_association_name(association_name)).each do |field| + # puts "field = #{field.inspect}" + # self.class.send(:set_field, field.name, { :type => field.field_type }) + field.apply(self, association_name) + end + puts "))))))))" + end + end + + alias_method_chain :parentize, :custom_fields + + def custom_fields_association_name(association_name) + "#{association_name.singularize}_fields".to_sym + end + + def custom_fields?(object, association_name) + object.respond_to?(custom_fields_association_name(association_name)) && + object.associations[association_name] && + object.associations[association_name].options.custom_fields + end + end + end +end \ No newline at end of file diff --git a/doc/TODO b/doc/TODO index 750b9105..597e0987 100644 --- a/doc/TODO +++ b/doc/TODO @@ -49,6 +49,13 @@ x domain scoping when authenticating - custom resizing - assets uploader: - remove old files if new one +- custom fields: + - renaming fields + - ui + - rename asset_field + - apply in asset_field + - extract a plugin from custom fields + - field position BACKLOG: - liquid rendering engine diff --git a/spec/factories.rb b/spec/factories.rb index 0f820b5e..56d0b937 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -61,4 +61,10 @@ end ## Theme assets ## Factory.define :theme_asset do |a| a.association :site -end \ No newline at end of file +end + +## Asset collection ## +Factory.define :asset_collection do |s| + s.association :site, :factory => :site + s.name 'Trip to Chicago' +end diff --git a/spec/models/asset_collections_spec.rb b/spec/models/asset_collections_spec.rb new file mode 100644 index 00000000..e89400d1 --- /dev/null +++ b/spec/models/asset_collections_spec.rb @@ -0,0 +1,106 @@ +require 'spec_helper' + +describe AssetCollection do + + it 'should have a valid factory' do + Factory.build(:asset_collection).should be_valid + end + + describe 'custom fields (beta)' do + + before(:each) do + @collection = Factory.build(:asset_collection) + @collection.asset_fields.build :label => 'My Description', :_alias => 'description', :kind => 'Text' + @collection.asset_fields.build :label => 'Active', :kind => 'Boolean' + end + + context 'define attributes' do + + # it 'should have an unique name' do + # @collection.asset_fields.first._name.should == "custom_field_1" + # @collection.asset_fields.last._name.should == "custom_field_2" + # end + + it 'should have an unique alias' do + @collection.save + @collection.asset_fields.first._alias.should == "description" + @collection.asset_fields.last._alias.should == "active" + end + + # + # it 'should define a position according to siblings' + + end + + # context 'build and save' do + # + # it 'should build asset' do + # asset = @collection.assets.build + # lambda { + # asset.description + # asset.active + # }.should_not raise_error + # end + # + # it 'should assign values to newly built asset' do + # asset = build_asset(@collection) + # asset.description.should == 'Lorem ipsum' + # asset.active.should == true + # end + # + # it 'should save asset' do + # asset = build_asset(@collection) + # asset.save and @collection.reload + # asset = @collection.assets.first + # asset.description.should == 'Lorem ipsum' + # asset.active.should == true + # end + # + # it 'should not modify assets from another collection' do + # asset = build_asset(@collection) + # asset.save and @collection.reload + # new_collection = AssetCollection.new + # lambda { new_collection.assets.build.description }.should raise_error + # end + # + # end + # + context 'modifying fields' do + + # before(:each) do + # @asset = build_asset(@collection).save + # end + # + # it 'should add new field' do + # @collection.asset_fields.build :label => 'Active at', :name => 'active_at', :kind => 'Date' + # @collection.save + # + # # puts "association = #{@collection.send(:associations)['assets'].options.inspect}" + # + # # @collection = AssetCollection.first + # # @collection.flush_cache(:asset_fields) + # @collection.reload + # # puts "# fields #{@collection.asset_fields.size.inspect}" + # # puts "============================" + # # puts "@collection asset fields ==> #{@collection.asset_fields.inspect}" + # asset = @collection.assets.first + # lambda { asset.active_at }.should_not raise_error + # end + + # it 'should remove field' do + # @collection.asset_fields.delete_all :name => 'active' + # @collection.save + # lambda { asset.active }.should raise_error + # end + # + # it 'should be able to change field name' + + end + + end + + def build_asset(collection) + collection.assets.build(:name => 'Asset on steroids', :description => 'Lorem ipsum', :active => true) + end + +end \ No newline at end of file