custom field validation is more robust + editing new fields for assets works now

This commit is contained in:
dinedine 2010-05-22 16:46:32 +02:00
parent 10ffc2e8bb
commit 1ed613ede8
9 changed files with 104 additions and 28 deletions

View File

@ -36,10 +36,6 @@ class AssetCollection
@assets_order = order @assets_order = order
end end
def ordered_asset_custom_fields # FIXME (custom fields)
self.asset_custom_fields.sort { |a, b| (a.position || 0) <=> (b.position || 0) }
end
protected protected
def normalize_slug def normalize_slug

View File

@ -1,4 +1,4 @@
= f.foldable_inputs :name => :custom_fields, :class => 'editable-list fields off' do = f.foldable_inputs :name => :custom_fields, :class => 'editable-list fields' do
- f.object.ordered_asset_custom_fields.each do |field| - f.object.ordered_asset_custom_fields.each do |field|
= f.fields_for :asset_custom_fields, field, :child_index => field._index do |g| = f.fields_for :asset_custom_fields, field, :child_index => field._index do |g|
%li{ :class => "item added #{'error' unless field.errors.empty?}"} %li{ :class => "item added #{'error' unless field.errors.empty?}"}

View File

@ -2,6 +2,14 @@
= f.input :name = f.input :name
= f.input :source = f.input :source
- unless @asset.custom_fields.empty?
= f.inputs :name => :other_fields do
- @asset.custom_fields.each do |field|
- if field.string?
= f.input field._alias.to_sym, :label => field.label
- elsif field.text?
= f.input field._alias.to_sym, :label => field.label, :as => :text
- if @asset.image? - if @asset.image?
= f.foldable_inputs :name => "#{t('formtastic.titles.preview')} #{image_dimensions_and_size(@asset)}", :class => 'preview' do = f.foldable_inputs :name => "#{t('formtastic.titles.preview')} #{image_dimensions_and_size(@asset)}", :class => 'preview' do
%li %li

View File

@ -155,6 +155,7 @@ en:
preview: Preview preview: Preview
options: Advanced options options: Advanced options
custom_fields: Custom fields custom_fields: Custom fields
other_fields: Other information
labels: labels:
theme_asset: theme_asset:
new: new:

View File

@ -55,16 +55,16 @@ x domain scoping when authenticating
x ui x ui
x field position x field position
x rename asset_field x rename asset_field
- apply in asset_field x nested attributes
- nested attributes x keep tracks of all custom fields (adding / editing assets) + order them
- keep tracks of all custom fields (adding / editing assets) x duplicate fields
- custom fields -> metadata keys - apply in asset_field (in order to handle Date, File, ...etc)
- duplicate fields
BACKLOG: BACKLOG:
- liquid rendering engine - liquid rendering engine
- theme assets - theme assets
- assets collection - assets collection
- custom models
- devise messages in French - devise messages in French
- localize devise emails - localize devise emails

View File

@ -9,10 +9,52 @@ describe AssetCollection do
describe 'custom fields (beta)' do describe 'custom fields (beta)' do
before(:each) do before(:each) do
@collection = Factory.build(:asset_collection, :site => nil) site = Factory.build(:site)
Site.stubs(:find).returns(site)
@collection = Factory.build(:asset_collection, :site => site)
@collection.asset_custom_fields.build :label => 'My Description', :_alias => 'description', :kind => 'Text' @collection.asset_custom_fields.build :label => 'My Description', :_alias => 'description', :kind => 'Text'
@collection.asset_custom_fields.build :label => 'Active', :kind => 'Boolean' @collection.asset_custom_fields.build :label => 'Active', :kind => 'Boolean'
puts "first field index = #{@collection.asset_custom_fields.first._index}" end
context 'unit' do
before(:each) do
@field = CustomFields::CustomField.new(:kind => 'String')
end
it 'should tell if it is a String' do
@field.string?.should be_true
end
it 'should tell if it is a Text' do
@field.kind = 'Text'
@field.text?.should be_true
end
end
context 'validation' do
%w{label kind}.each do |key|
it "should validate presence of #{key}" do
field = @collection.asset_custom_fields.build({ :label => 'Shortcut', :kind => 'String' }.merge(key.to_sym => nil))
field.should_not be_valid
field.errors[key.to_sym].should == ["can't be blank"]
end
end
it 'should not have unique label' do
field = @collection.asset_custom_fields.build :label => 'Active', :kind => 'Boolean'
field.should_not be_valid
field.errors[:label].should == ["is already taken"]
end
it 'should invalidate parent if custom field is not valid' do
field = @collection.asset_custom_fields.build
@collection.should_not be_valid
@collection.asset_custom_fields.last.errors[:label].should == ["can't be blank"]
end
end end
context 'define core attributes' do context 'define core attributes' do
@ -36,6 +78,7 @@ describe AssetCollection do
lambda { lambda {
asset.description asset.description
asset.active asset.active
asset.custom_fields.size.should == 2
}.should_not raise_error }.should_not raise_error
end end
@ -97,11 +140,11 @@ describe AssetCollection do
context 'managing from hash' do context 'managing from hash' do
before(:each) do # before(:each) do
site = Factory.build(:site) # site = Factory.build(:site)
Site.stubs(:find).returns(site) # Site.stubs(:find).returns(site)
@collection.site = site # @collection.site = site
end # end
it 'should add new field' do it 'should add new field' do
@collection.asset_custom_fields.clear @collection.asset_custom_fields.clear

View File

@ -13,9 +13,16 @@ module CustomFields
## validations ## ## validations ##
validates_presence_of :label, :kind validates_presence_of :label, :kind
validate :uniqueness_of_label
## methods ## ## methods ##
%w{String Text Email Boolean Date File}.each do |kind|
define_method "#{kind.downcase}?" do
self.kind == kind
end
end
def field_type def field_type
case self.kind case self.kind
when 'String', 'Text', 'Email' then String when 'String', 'Text', 'Email' then String
@ -25,7 +32,10 @@ module CustomFields
end end
def apply(object, association_name) def apply(object, association_name)
return unless self.valid?
object.class.send(:set_field, self._name, { :type => self.field_type }) object.class.send(:set_field, self._name, { :type => self.field_type })
object.class_eval <<-EOF object.class_eval <<-EOF
alias :#{self.safe_alias} :#{self._name} alias :#{self.safe_alias} :#{self._name}
alias :#{self.safe_alias}= :#{self._name}= alias :#{self.safe_alias}= :#{self._name}=
@ -39,6 +49,13 @@ module CustomFields
protected protected
def uniqueness_of_label
duplicate = self.siblings.detect { |f| f.label == self.label && f._id != self._id }
if not duplicate.nil?
self.errors.add(:label, :taken)
end
end
def set_unique_name! def set_unique_name!
self._name ||= "custom_field_#{self.increment_counter!}" self._name ||= "custom_field_#{self.increment_counter!}"
end end
@ -56,7 +73,7 @@ module CustomFields
end end
def siblings def siblings
self._parent.associations[self.association_name] self._parent.send(self.association_name)
end end
end end

View File

@ -8,19 +8,19 @@ module CustomFields
# Enhance an embedded collection by providing methods to manage custom fields # Enhance an embedded collection by providing methods to manage custom fields
# #
# class Person # class Company
# embeds_many :addresses # embeds_many :employees
# custom_fields_for :addresses # custom_fields_for :employees
# end # end
# #
# class Address # class Employee
# embedded_in :person, :inverse_of => :addresses # embedded_in :company, :inverse_of => :employees
# field :street, String # field :name, String
# end # end
# #
# person.address_fields.build :label => 'Floor', :kind => 'String' # company.employee_custom_fields.build :label => 'His/her position', :_alias => 'position', :kind => 'String'
# #
# person.addresses.build :street => 'Laflin Street', :floor => '42' # company.employees.build :name => 'Mickael Scott', :position => 'Regional manager'
# #
module ClassMethods module ClassMethods
@ -34,7 +34,13 @@ module CustomFields
embeds_many :#{singular_name}_custom_fields, :class_name => "::CustomFields::CustomField" embeds_many :#{singular_name}_custom_fields, :class_name => "::CustomFields::CustomField"
validates_associated :#{singular_name}_custom_fields
accepts_nested_attributes_for :#{singular_name}_custom_fields, :allow_destroy => true accepts_nested_attributes_for :#{singular_name}_custom_fields, :allow_destroy => true
def ordered_#{singular_name}_custom_fields
self.#{singular_name}_custom_fields.sort { |a, b| (a.position || 0) <=> (b.position || 0) }
end
EOV EOV
end end

View File

@ -6,6 +6,11 @@ module Mongoid #:nodoc:
parentize_without_custom_fields(object, association_name) parentize_without_custom_fields(object, association_name)
if self.custom_fields?(object, association_name) if self.custom_fields?(object, association_name)
self.class.send(:define_method, :custom_fields) do
fields = object.send(self.custom_fields_association_name(association_name))
fields.sort { |a, b| (a.position || 0) <=> (b.position || 0) }
end
object.send(self.custom_fields_association_name(association_name)).each do |field| object.send(self.custom_fields_association_name(association_name)).each do |field|
field.apply(self, association_name) field.apply(self, association_name)
end end