custom field validation is more robust + editing new fields for assets works now
This commit is contained in:
parent
10ffc2e8bb
commit
1ed613ede8
@ -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
|
||||||
|
@ -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?}"}
|
||||||
|
@ -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
|
||||||
|
@ -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:
|
||||||
|
10
doc/TODO
10
doc/TODO
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user