very simple api for contents (just create for now) + fix a lot of bugs in the custom fields plugin and some enhancements as well + add an ui for manage categories + fix bugs
This commit is contained in:
parent
fc690d8a0b
commit
7ccc3d4548
@ -2,7 +2,7 @@ module Admin::CustomFieldsHelper
|
||||
|
||||
def options_for_field_kind(selected = nil)
|
||||
# %w{String Text Boolean Email File Date}
|
||||
options = %w{String Text Select}.map do |kind|
|
||||
options = %w{String Text Category}.map do |kind|
|
||||
[t("admin.custom_fields.kind.#{kind.downcase}"), kind]
|
||||
end
|
||||
end
|
||||
|
@ -3,6 +3,9 @@ class ContentInstance
|
||||
include Mongoid::Document
|
||||
include Mongoid::Timestamps
|
||||
|
||||
## extensions ##
|
||||
include CustomFields::ProxyClassEnabler
|
||||
|
||||
## fields (dynamic fields) ##
|
||||
field :_position_in_list, :type => Integer, :default => 0
|
||||
|
||||
@ -17,6 +20,10 @@ class ContentInstance
|
||||
|
||||
## methods ##
|
||||
|
||||
def to_liquid
|
||||
Locomotive::Liquid::Drops::Content.new(self)
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def require_highlighted_field
|
||||
|
@ -9,7 +9,8 @@
|
||||
|
||||
= render 'admin/custom_fields/index', :f => f, :collection_name => 'contents'
|
||||
|
||||
= f.foldable_inputs :name => :options, :class => 'switchable' do
|
||||
= f.input :highlighted_field_name, :as => :select, :collection => options_for_highlighted_field(f.object, 'contents'), :include_blank => false
|
||||
= f.input :order_by, :as => :select, :collection => options_for_order_by(f.object, 'contents'), :include_blank => false
|
||||
- unless f.object.new_record?
|
||||
= f.foldable_inputs :name => :options, :class => 'switchable' do
|
||||
= f.input :highlighted_field_name, :as => :select, :collection => options_for_highlighted_field(f.object, 'contents'), :include_blank => false
|
||||
= f.input :order_by, :as => :select, :collection => options_for_order_by(f.object, 'contents'), :include_blank => false
|
||||
|
||||
|
@ -13,3 +13,5 @@
|
||||
= render 'form', :f => f
|
||||
|
||||
= render 'admin/shared/form_actions', :back_url => admin_pages_url, :button_label => :create
|
||||
|
||||
= render 'admin/custom_fields/edit'
|
@ -1,3 +1,7 @@
|
||||
- content_for :head do
|
||||
= javascript_include_tag 'admin/plugins/json2', 'admin/plugins/fancybox', 'admin/custom_fields/category', 'admin/contents'
|
||||
= stylesheet_link_tag 'admin/plugins/fancybox', 'admin/box'
|
||||
|
||||
- highlighted_field_name = @content.content_type.highlighted_field_name
|
||||
|
||||
= f.inputs :name => :other_fields do
|
||||
@ -5,6 +9,12 @@
|
||||
- required = highlighted_field_name == field._name
|
||||
|
||||
- if field.string?
|
||||
= f.input field._alias.to_sym, :label => field.label, :required => required
|
||||
= f.input field._alias.to_sym, :label => field.label, :hint => field.hint, :required => required
|
||||
- elsif field.text?
|
||||
= f.input field._alias.to_sym, :label => field.label, :as => :text, :required => required
|
||||
= f.input field._alias.to_sym, :label => field.label, :hint => field.hint, :as => :text, :required => required
|
||||
- elsif field.category?
|
||||
= f.custom_input field._alias.to_sym, :label => field.label, :hint => field.hint, :css => 'toggle' do
|
||||
= f.select field._name.to_sym, field.ordered_category_items.collect { |item| [item.name, item.id] }
|
||||
%button.button.light.edit-categories-link{ :type => 'button', :'data-url' => edit_admin_custom_field_path(@content_type.slug, field) }
|
||||
%span= t('.edit_categories')
|
||||
|
||||
|
@ -5,4 +5,5 @@
|
||||
= form_tag '#', :class => 'formtastic' do
|
||||
= fields_for CustomFields::Field.new, :builder => Formtastic::SemanticFormHelper.builder do |g|
|
||||
= g.inputs :name => :information do
|
||||
= g.input :_alias
|
||||
= g.input :_alias
|
||||
= g.input :hint
|
@ -12,6 +12,8 @@
|
||||
= g.hidden_field :position, :class => 'position'
|
||||
|
||||
= g.hidden_field :_alias, :class => 'alias'
|
||||
|
||||
= g.hidden_field :hint, :class => 'hint'
|
||||
|
||||
= g.text_field :label
|
||||
|
||||
@ -27,7 +29,7 @@
|
||||
= link_to image_tag('admin/form/pen.png'), '#edit-custom-field', :class => 'edit first'
|
||||
= link_to image_tag('admin/form/icons/trash.png'), '#', :class => 'remove', :confirm => t('admin.messages.confirm')
|
||||
|
||||
= f.fields_for collection_name.to_sym, custom_fields.build(:label => 'field name'), :child_index => '-1' do |g|
|
||||
= f.fields_for collection_name.to_sym, custom_fields.build(:label => 'field name', :_alias => ''), :child_index => '-1' do |g|
|
||||
%li{ :class => 'item template' }
|
||||
%span.handle
|
||||
= image_tag 'admin/form/icons/drag.png'
|
||||
@ -35,6 +37,8 @@
|
||||
= g.hidden_field :position, :class => 'position'
|
||||
|
||||
= g.hidden_field :_alias, :class => 'alias'
|
||||
|
||||
= g.hidden_field :hint, :class => 'hint'
|
||||
|
||||
= g.text_field :label, :class => 'string label void'
|
||||
|
||||
|
@ -6,7 +6,7 @@
|
||||
|
||||
= f.input :title
|
||||
|
||||
= f.input :layout_id, :as => :select, :collection => current_site.layouts.all, :input_html => { :data_url => admin_layout_page_parts_url('_id_to_replace_') }
|
||||
= f.input :layout_id, :as => :select, :collection => current_site.layouts.all.to_a, :input_html => { :data_url => admin_layout_page_parts_url('_id_to_replace_') }
|
||||
|
||||
- if not @page.index? and not @page.not_found?
|
||||
= f.input :parent_id, :as => :select, :collection => parent_pages_options, :include_blank => false
|
||||
|
@ -1,55 +0,0 @@
|
||||
- collection_name = "#{collection_name.singularize}_custom_fields"
|
||||
- custom_fields = f.object.send(collection_name.to_sym)
|
||||
- ordered_custom_fields = f.object.send(:"ordered_#{collection_name}")
|
||||
|
||||
= f.foldable_inputs :name => :custom_fields, :class => 'editable-list fields' do
|
||||
- ordered_custom_fields.each do |field|
|
||||
= f.fields_for collection_name.to_sym, field, :child_index => field._index do |g|
|
||||
%li{ :class => "item added #{'error' unless field.errors.empty?}"}
|
||||
%span.handle
|
||||
= image_tag 'admin/form/icons/drag.png'
|
||||
|
||||
= g.hidden_field :position, :class => 'position'
|
||||
|
||||
= g.hidden_field :_alias, :class => 'alias'
|
||||
|
||||
= g.text_field :label
|
||||
|
||||
—
|
||||
|
||||
%em= t("admin.custom_fields.kind.#{field.kind.downcase}")
|
||||
|
||||
= g.select :kind, options_for_field_kind
|
||||
|
||||
|
||||
|
||||
%span.actions
|
||||
= link_to image_tag('admin/form/pen.png'), '#', :class => 'edit first'
|
||||
= link_to image_tag('admin/form/icons/trash.png'), '#', :class => 'remove', :confirm => t('admin.messages.confirm')
|
||||
|
||||
= f.fields_for collection_name.to_sym, custom_fields.build(:label => 'field name'), :child_index => '-1' do |g|
|
||||
%li{ :class => 'item template' }
|
||||
%span.handle
|
||||
= image_tag 'admin/form/icons/drag.png'
|
||||
|
||||
= g.hidden_field :position, :class => 'position'
|
||||
|
||||
= g.hidden_field :_alias, :class => 'alias'
|
||||
|
||||
= g.text_field :label, :class => 'string label void'
|
||||
|
||||
—
|
||||
|
||||
%em
|
||||
|
||||
= g.select :kind, options_for_field_kind
|
||||
|
||||
|
||||
|
||||
%span.actions
|
||||
= link_to image_tag('admin/form/pen.png'), '#', :class => 'edit first'
|
||||
= link_to image_tag('admin/form/icons/trash.png'), '#', :class => 'remove', :confirm => t('admin.messages.confirm')
|
||||
%button{ :class => 'button light add', :type => 'button' }
|
||||
%span= t('admin.buttons.new_item')
|
||||
|
||||
|
@ -10,7 +10,7 @@
|
||||
|
||||
= stylesheet_link_tag 'admin/layout', 'admin/plugins/toggle', 'admin/menu', 'admin/buttons', 'admin/formtastic', 'admin/formtastic_changes', 'admin/application', :media => 'screen', :cache => Rails.env.production?
|
||||
|
||||
= javascript_include_tag 'admin/jquery', 'admin/jquery.ui', 'admin/rails', 'admin/plugins/toggle', 'admin/plugins/growl', 'admin/plugins/cookie', 'admin/application', :cache => Rails.env.production?
|
||||
= javascript_include_tag 'admin/jquery', 'admin/jquery.ui', 'admin/rails', 'admin/utils', 'admin/plugins/toggle', 'admin/plugins/growl', 'admin/plugins/cookie', 'admin/application', :cache => Rails.env.production?
|
||||
|
||||
%script{ :type => 'text/javascript' }
|
||||
= find_and_preserve(growl_message)
|
||||
|
@ -38,7 +38,11 @@ en:
|
||||
kind:
|
||||
string: Simple Input
|
||||
text: Text
|
||||
select: Select
|
||||
category: Select
|
||||
edit_category:
|
||||
title: Edit options
|
||||
help: Manage the list of options for your select box.
|
||||
collection_label: List of options
|
||||
|
||||
sessions:
|
||||
new:
|
||||
@ -174,7 +178,9 @@ en:
|
||||
new:
|
||||
title: '{{type}} — new item'
|
||||
edit:
|
||||
title: '{{type}} — editing item'
|
||||
title: '{{type}} — editing item'
|
||||
form:
|
||||
edit_categories: Edit options
|
||||
|
||||
formtastic:
|
||||
titles:
|
||||
|
@ -49,7 +49,10 @@ Rails.application.routes.draw do |map|
|
||||
resources :contents, :path => "content_types/:slug/contents" do
|
||||
put :sort, :on => :collection
|
||||
end
|
||||
|
||||
|
||||
resources :api_contents, :path => "api/:slug/contents", :controller => 'api_contents', :only => [:create]
|
||||
|
||||
resources :custom_fields, :path => "content_types/:slug/fields"
|
||||
end
|
||||
|
||||
# magic urls
|
||||
|
19
doc/TODO
19
doc/TODO
@ -4,13 +4,10 @@ x make an engine:
|
||||
- write doc about setting up the parent app
|
||||
x helpers do not work
|
||||
|
||||
- deploy on Heroku
|
||||
|
||||
x refactoring: CustomFields::CustomField => CustomFields::Field
|
||||
- new custom field type
|
||||
- category
|
||||
x optimization custom_fields: use dynamic class for a collection instead of modifying the metaclass each time we build an item
|
||||
- deploy on Heroku
|
||||
|
||||
- missing translation in english
|
||||
|
||||
BACKLOG:
|
||||
|
||||
- devise messages in French
|
||||
@ -36,6 +33,8 @@ NICE TO HAVE:
|
||||
- asset collections: custom resizing if image
|
||||
- super_finder
|
||||
- better icons for mime type
|
||||
- hint for custom fields (super easy to do !)
|
||||
- tiny mce or similar for custom field text type.
|
||||
|
||||
DONE:
|
||||
x admin layout
|
||||
@ -111,4 +110,10 @@ x theme assets picker (???)
|
||||
x lightbox (http://fancybox.net/api)
|
||||
x select it
|
||||
x flash upload (http://www.plupload.com/example_custom.php)
|
||||
x refactor theme assets / assets uploaders
|
||||
x refactor theme assets / assets uploaders
|
||||
x refactoring: CustomFields::CustomField => CustomFields::Field
|
||||
x optimization custom_fields: use dynamic class for a collection instead of modifying the metaclass each time we build an item
|
||||
x new custom field type: category
|
||||
x model
|
||||
x ui
|
||||
x liquid
|
||||
|
@ -24,31 +24,20 @@ module Locomotive
|
||||
|
||||
def first
|
||||
content = @content_type.ordered_contents(@context['with_scope']).first
|
||||
build_content_drop(content) unless content.nil?
|
||||
end
|
||||
|
||||
def last
|
||||
content = @content_type.ordered_contents(@context['with_scope']).last
|
||||
build_content_drop(content) unless content.nil?
|
||||
end
|
||||
|
||||
def each(&block)
|
||||
@collection ||= @content_type.ordered_contents(@context['with_scope'])
|
||||
to_content_drops.each(&block)
|
||||
end
|
||||
|
||||
def to_content_drops
|
||||
@collection.map { |c| build_content_drop(c) }
|
||||
end
|
||||
|
||||
def build_content_drop(content)
|
||||
Locomotive::Liquid::Drops::Content.new(content)
|
||||
end
|
||||
|
||||
def paginate(options = {})
|
||||
@collection ||= @content_type.ordered_contents(@context['with_scope']).paginate(options)
|
||||
{
|
||||
:collection => to_content_drops,
|
||||
:collection => @collection,
|
||||
:current_page => @collection.current_page,
|
||||
:previous_page => @collection.previous_page,
|
||||
:next_page => @collection.next_page,
|
||||
@ -56,7 +45,16 @@ module Locomotive
|
||||
:total_pages => @collection.total_pages,
|
||||
:per_page => @collection.per_page
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
def api
|
||||
{ 'create' => @context.registers[:controller].send('admin_api_contents_url', @content_type.slug) }
|
||||
end
|
||||
|
||||
def before_method(meth)
|
||||
klass = @content_type.contents.klass # delegate to the proxy class
|
||||
klass.send(meth)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -8,7 +8,7 @@ module Locomotive
|
||||
end
|
||||
|
||||
def before_method(meth)
|
||||
asset = site.theme_assets.where(:content_type => 'javascript', :slug => meth.to_s).first
|
||||
asset = @site.theme_assets.where(:content_type => 'javascript', :slug => meth.to_s).first
|
||||
!asset.nil? ? asset.source.url : nil
|
||||
end
|
||||
|
||||
|
@ -24,7 +24,7 @@ module Locomotive
|
||||
@collection_name = $1
|
||||
@per_page = $2.to_i
|
||||
else
|
||||
raise SyntaxError.new("Syntax Error in 'paginate' - Valid syntax: paginate [collection] by [number]")
|
||||
raise ::Liquid::SyntaxError.new("Syntax Error in 'paginate' - Valid syntax: paginate [collection] by [number]")
|
||||
end
|
||||
|
||||
super
|
||||
|
@ -18,6 +18,7 @@ class MiscFormBuilder < Formtastic::SemanticFormBuilder
|
||||
|
||||
html = options[:with_label] ? self.label(options[:label] || name) : ''
|
||||
html += template.capture(&block) || ''
|
||||
html += inline_hints_for(name, options) || ''
|
||||
html += self.errors_on(name) || ''
|
||||
|
||||
template.content_tag(:li, template.find_and_preserve(html), :class => "#{options[:css]} #{'error' unless @object.errors[name].empty?}")
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 287 B |
Binary file not shown.
Before Width: | Height: | Size: 525 B |
@ -7,7 +7,7 @@ $(document).ready(function() {
|
||||
|
||||
if (!slug.hasClass('filled')) {
|
||||
setTimeout(function() {
|
||||
slug.val(input.val().replace(/\s/g, '_').toLowerCase());
|
||||
slug.val(makeSlug(input.val()));
|
||||
}, 50);
|
||||
}
|
||||
});
|
||||
|
@ -13,5 +13,15 @@ $(document).ready(function() {
|
||||
items: 'li.content',
|
||||
stop: function(event, ui) { updateContentsOrder(); }
|
||||
});
|
||||
|
||||
|
||||
$('button.edit-categories-link').click(function() {
|
||||
var link = $(this);
|
||||
$.fancybox({
|
||||
titleShow: false,
|
||||
href: link.attr('data-url'),
|
||||
padding: 0,
|
||||
onComplete: function() { SetupCustomFieldCategoryEditor(link.prev()); },
|
||||
onCleanup: function() { console.log('closing...'); }
|
||||
})
|
||||
});
|
||||
});
|
@ -124,10 +124,16 @@ $(document).ready(function() {
|
||||
e.stopPropagation();
|
||||
});
|
||||
|
||||
$('#fancybox-wrap #custom_fields_custom_field__alias').val(link.parent().prevAll('.alias').val());
|
||||
var alias = link.parent().prevAll('.alias').val();
|
||||
if (alias == '') alias = makeSlug(link.parent().prevAll('.label').val());
|
||||
$('#fancybox-wrap #custom_fields_field__alias').val(alias);
|
||||
|
||||
var hint = link.parent().prevAll('.hint').val();
|
||||
$('#fancybox-wrap #custom_fields_field_hint').val(hint);
|
||||
},
|
||||
onCleanup: function() {
|
||||
link.parent().prevAll('.alias').val($('#fancybox-wrap #custom_fields_custom_field__alias').val());
|
||||
link.parent().prevAll('.alias').val($('#fancybox-wrap #custom_fields_field__alias').val());
|
||||
link.parent().prevAll('.hint').val($('#fancybox-wrap #custom_fields_field_hint').val());
|
||||
}
|
||||
})
|
||||
});
|
||||
|
@ -38,7 +38,7 @@ $(document).ready(function() {
|
||||
|
||||
if (!slug.hasClass('filled')) {
|
||||
setTimeout(function() {
|
||||
slug.val(input.val().replace(/\s/g, '_').toLowerCase()).addClass('touched');
|
||||
slug.val(makeSlug(input.val())).addClass('touched');
|
||||
}, 50);
|
||||
}
|
||||
});
|
||||
|
@ -7,7 +7,7 @@ $(document).ready(function() {
|
||||
|
||||
if (!slug.hasClass('filled')) {
|
||||
setTimeout(function() {
|
||||
slug.val(input.val().replace(/[\s']/g, '_').toLowerCase());
|
||||
slug.val(makeSlug(input.val()));
|
||||
}, 50);
|
||||
}
|
||||
});
|
||||
|
@ -13,14 +13,31 @@
|
||||
}
|
||||
|
||||
#fancybox-inner form.formtastic legend span {
|
||||
background-image: url("/images/admin/form/header-small.png");
|
||||
width: 450px;
|
||||
background-image: url("/images/admin/form/header-popup.png");
|
||||
width: 453px;
|
||||
}
|
||||
|
||||
#fancybox-inner form.formtastic ol {
|
||||
background-image: url("/images/admin/form/footer-small.png");
|
||||
background-image: url("/images/admin/form/footer-popup.png");
|
||||
}
|
||||
|
||||
#fancybox-inner form.formtastic .editable-list ol {
|
||||
width: 433px;
|
||||
}
|
||||
|
||||
#fancybox-inner form.formtastic .editable-list li {
|
||||
width: 413px;
|
||||
}
|
||||
|
||||
#fancybox-inner form.formtastic .editable-list li {
|
||||
background-image: url("/images/admin/form/item-popup.png");
|
||||
}
|
||||
|
||||
#fancybox-inner form.formtastic .editable-list li.template {
|
||||
background-image: url("/images/admin/form/big_item-popup.png");
|
||||
}
|
||||
|
||||
#fancybox-inner p { color:#8B8D9A; font-size:0.8em; }
|
||||
|
||||
/* ___ asset picker ___ */
|
||||
|
||||
@ -35,5 +52,39 @@ div.asset-picker ul li.new-asset { display: none; }
|
||||
/* ___ custom fields ___ */
|
||||
|
||||
#edit-custom-field {
|
||||
width: 470px;
|
||||
}
|
||||
width: 473px;
|
||||
}
|
||||
|
||||
#edit-custom-field-category {
|
||||
width: 493px;
|
||||
}
|
||||
|
||||
#edit-custom-field-category .inner {
|
||||
padding: 10px 10px 61px 10px;
|
||||
}
|
||||
|
||||
/* ___ form action ___ */
|
||||
|
||||
#fancybox-inner .popup-actions {
|
||||
position: absolute;
|
||||
left: 0px;
|
||||
bottom: 0px;
|
||||
height: 61px;
|
||||
width: 100%;
|
||||
background: #8b8d9a;
|
||||
}
|
||||
|
||||
#fancybox-inner .popup-actions p {
|
||||
padding: 15px;
|
||||
margin: 0px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
/*#fancybox-inner .actions a {
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
font-size: 0.8em;
|
||||
position: relative;
|
||||
top: 4px;
|
||||
}*/
|
||||
|
||||
|
10
vendor/plugins/custom_fields/Rakefile
vendored
10
vendor/plugins/custom_fields/Rakefile
vendored
@ -13,21 +13,17 @@ Rake::RDocTask.new(:rdoc) do |rdoc|
|
||||
rdoc.rdoc_files.include('lib/**/*.rb')
|
||||
end
|
||||
|
||||
# Spec::Rake::SpecTask.new(:rcov) do |spec|
|
||||
# spec.libs << 'lib' << 'spec'
|
||||
# spec.pattern = 'spec/**/*_spec.rb'
|
||||
# spec.rcov = true
|
||||
# end
|
||||
|
||||
Rspec::Core::RakeTask.new('spec:unit') do |spec|
|
||||
spec.pattern = "spec/unit/**/*_spec.rb"
|
||||
# spec.pattern = "spec/unit/custom_fields_for_spec.rb"
|
||||
# spec.pattern = "spec/unit/types/category_spec.rb"
|
||||
end
|
||||
|
||||
Rspec::Core::RakeTask.new('spec:integration') do |spec|
|
||||
spec.pattern = "spec/integration/**/*_spec.rb"
|
||||
# spec.pattern = "spec/integration/types/category_spec.rb"
|
||||
end
|
||||
|
||||
task :spec => [:check_dependencies, 'spec:unit', 'spec:integration']
|
||||
task :spec => ['spec:unit', 'spec:integration']
|
||||
|
||||
task :default => :spec
|
@ -2,10 +2,10 @@ $:.unshift File.expand_path(File.dirname(__FILE__))
|
||||
|
||||
require 'active_support'
|
||||
|
||||
require 'custom_fields/extensions/mongoid/document'
|
||||
require 'custom_fields/extensions/mongoid/associations/proxy'
|
||||
require 'custom_fields/extensions/mongoid/associations/has_many_related'
|
||||
require 'custom_fields/extensions/mongoid/associations/embeds_many'
|
||||
require 'custom_fields/extensions/mongoid/document'
|
||||
require 'custom_fields/types/default'
|
||||
require 'custom_fields/types/category'
|
||||
require 'custom_fields/proxy_class_enabler'
|
||||
|
@ -2,6 +2,7 @@
|
||||
module Mongoid #:nodoc:
|
||||
module Associations #:nodoc:
|
||||
class EmbedsMany < Proxy
|
||||
|
||||
def initialize_with_custom_fields(parent, options, target_array = nil)
|
||||
if custom_fields?(parent, options.name)
|
||||
options = options.clone # 2 parent instances should not share the exact same option instance
|
||||
@ -9,6 +10,8 @@ module Mongoid #:nodoc:
|
||||
custom_fields = parent.send(:"ordered_#{custom_fields_association_name(options.name)}")
|
||||
|
||||
klass = options.klass.to_klass_with_custom_fields(custom_fields)
|
||||
klass._parent = parent
|
||||
klass.association_name = options.name
|
||||
|
||||
options.instance_eval <<-EOF
|
||||
def klass=(klass); @klass = klass; end
|
||||
@ -23,22 +26,6 @@ module Mongoid #:nodoc:
|
||||
|
||||
alias_method_chain :initialize, :custom_fields
|
||||
|
||||
def build_with_custom_field_settings(attrs = {}, type = nil)
|
||||
document = build_without_custom_field_settings(attrs, type)
|
||||
|
||||
if @association_name.ends_with?('_custom_fields')
|
||||
document.class_eval <<-EOV
|
||||
self.associations = {} # prevent associations to be nil
|
||||
embedded_in :#{@parent.class.to_s.underscore}, :inverse_of => :#{@association_name}
|
||||
EOV
|
||||
|
||||
document.send(:set_unique_name!)
|
||||
document.send(:set_alias)
|
||||
end
|
||||
document
|
||||
end
|
||||
|
||||
alias_method_chain :build, :custom_field_settings
|
||||
end
|
||||
end
|
||||
end
|
@ -12,6 +12,8 @@ module Mongoid #:nodoc:
|
||||
custom_fields = parent.send(:"ordered_#{custom_fields_association_name(options.name)}")
|
||||
|
||||
klass = options.klass.to_klass_with_custom_fields(custom_fields)
|
||||
klass._parent = parent
|
||||
klass.association_name = options.name
|
||||
|
||||
options.instance_eval <<-EOF
|
||||
def klass=(klass); @klass = klass; end
|
||||
|
@ -11,6 +11,10 @@ module Mongoid #:nodoc
|
||||
object.respond_to?(custom_fields_association_name(association_name))
|
||||
end
|
||||
|
||||
def klass
|
||||
@klass
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
@ -2,34 +2,27 @@
|
||||
module Mongoid #:nodoc:
|
||||
module Document
|
||||
module InstanceMethods
|
||||
# def parentize_with_custom_fields(object, association_name)
|
||||
# parentize_without_custom_fields(object, association_name)
|
||||
#
|
||||
# if self.custom_fields?(object, association_name)
|
||||
# # puts "[parentize_with_custom_fields] association_name = #{association_name} / #{self.custom_fields_association_name(association_name)}"
|
||||
# object.send(self.custom_fields_association_name(association_name)).each do |field|
|
||||
# field.apply(self)
|
||||
# end
|
||||
#
|
||||
# self.instance_eval <<-EOV
|
||||
# def custom_fields
|
||||
# fields = self._parent.send(:#{self.custom_fields_association_name(association_name)})
|
||||
# fields.sort { |a, b| (a.position || 0) <=> (b.position || 0) }
|
||||
# end
|
||||
# EOV
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# alias_method_chain :parentize, :custom_fields
|
||||
#
|
||||
# def custom_fields_association_name(association_name)
|
||||
# "#{association_name.to_s.singularize}_custom_fields".to_sym
|
||||
# end
|
||||
#
|
||||
# def custom_fields?(object, association_name)
|
||||
# object.respond_to?(custom_fields_association_name(association_name)) &&
|
||||
# object.associations[association_name]
|
||||
# end
|
||||
end
|
||||
|
||||
def parentize_with_custom_fields(object, association_name)
|
||||
if association_name.to_s.ends_with?('_custom_fields')
|
||||
self.singleton_class.associations = {}
|
||||
self.singleton_class.embedded_in object.class.to_s.underscore.to_sym, :inverse_of => association_name
|
||||
end
|
||||
|
||||
parentize_without_custom_fields(object, association_name)
|
||||
|
||||
if self.embedded? && self.instance_variable_get(:"@association_name").nil?
|
||||
self.instance_variable_set(:"@association_name", association_name) # weird bug with proxy class
|
||||
end
|
||||
|
||||
if association_name.to_s.ends_with?('_custom_fields')
|
||||
self.send(:set_unique_name!)
|
||||
self.send(:set_alias)
|
||||
end
|
||||
end
|
||||
|
||||
alias_method_chain :parentize, :custom_fields
|
||||
|
||||
end
|
||||
end
|
||||
end
|
@ -9,10 +9,11 @@ module CustomFields
|
||||
include Types::Category
|
||||
|
||||
## 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 :label
|
||||
field :_alias # need it for instance in: > asset.description (description being a custom field)
|
||||
field :_name
|
||||
field :kind
|
||||
field :hint
|
||||
field :position, :type => Integer, :default => 0
|
||||
|
||||
## validations ##
|
||||
@ -47,23 +48,6 @@ module CustomFields
|
||||
apply_default_type(klass)
|
||||
end
|
||||
end
|
||||
|
||||
# def apply_to_object(object)
|
||||
# return unless self.valid?
|
||||
#
|
||||
# # trick mongoid: fields are now on a the singleton class level also called metaclass
|
||||
# self.singleton_class.fields = self.fields.clone
|
||||
# self.singleton_class.send(:define_method, :fields) { self.singleton_class.fields }
|
||||
#
|
||||
# object.singleton_class.field self._name, :type => self.field_type
|
||||
#
|
||||
# case self.kind
|
||||
# when 'Category'
|
||||
# apply_category_type(object)
|
||||
# else
|
||||
# apply_default_type(object)
|
||||
# end
|
||||
# end
|
||||
|
||||
def safe_alias
|
||||
self.set_alias
|
||||
@ -85,7 +69,7 @@ module CustomFields
|
||||
|
||||
def set_alias
|
||||
return if self.label.blank? && self._alias.blank?
|
||||
self._alias = (self._alias || self.label).parameterize('_').downcase
|
||||
self._alias = (self._alias.blank? ? self.label : self._alias).parameterize('_').downcase
|
||||
end
|
||||
|
||||
def increment_counter!
|
||||
|
@ -12,7 +12,7 @@ module CustomFields
|
||||
|
||||
klass = Class.new(self)
|
||||
klass.class_eval <<-EOF
|
||||
cattr_accessor :custom_fields
|
||||
cattr_accessor :custom_fields, :_parent, :association_name
|
||||
|
||||
def self.model_name
|
||||
@_model_name ||= ActiveModel::Name.new(self.superclass)
|
||||
|
@ -6,6 +6,10 @@ module CustomFields
|
||||
|
||||
included do
|
||||
embeds_many :category_items, :class_name => 'CustomFields::Types::Category::Item'
|
||||
|
||||
validates_associated :category_items
|
||||
|
||||
accepts_nested_attributes_for :category_items, :allow_destroy => true
|
||||
end
|
||||
|
||||
module InstanceMethods
|
||||
@ -17,21 +21,36 @@ module CustomFields
|
||||
def apply_category_type(klass)
|
||||
klass.cattr_accessor :"#{self.safe_alias}_items"
|
||||
|
||||
klass.send("#{self.safe_alias}_items=", self.category_items)
|
||||
klass.send("#{self.safe_alias}_items=", self.ordered_category_items)
|
||||
|
||||
klass.class_eval <<-EOF
|
||||
def self.#{self.safe_alias}_names
|
||||
#{self.safe_alias}_items.collect(&:name)
|
||||
self.#{self.safe_alias}_items.collect(&:name)
|
||||
end
|
||||
|
||||
def self.group_by_#{self.safe_alias}
|
||||
groups = (if self.embedded?
|
||||
self._parent.send(self.association_name).all
|
||||
else
|
||||
self.all
|
||||
end).to_a.group_by(&:#{self._name})
|
||||
|
||||
self.#{self.safe_alias}_items.collect do |category|
|
||||
{
|
||||
:name => category.name,
|
||||
:items => groups[category._id] || []
|
||||
}.with_indifferent_access
|
||||
end
|
||||
end
|
||||
|
||||
def #{self.safe_alias}=(name)
|
||||
category_id = self.class.#{self.safe_alias}_items.where(:name => name).first._id rescue nil
|
||||
category_id = self.class.#{self.safe_alias}_items.find { |item| item.name == name }._id rescue name
|
||||
write_attribute(:#{self._name}, category_id)
|
||||
end
|
||||
|
||||
def #{self.safe_alias}
|
||||
category_id = read_attribute(:#{self._name})
|
||||
self.class.#{self.safe_alias}_items.find(category_id).name rescue nil
|
||||
self.class.#{self.safe_alias}_items.find { |item| item._id == category_id }.name rescue category_id
|
||||
end
|
||||
EOF
|
||||
end
|
||||
@ -46,6 +65,8 @@ module CustomFields
|
||||
field :position, :type => Integer, :default => 0
|
||||
|
||||
embedded_in :custom_field, :inverse_of => :category_items
|
||||
|
||||
validates_presence_of :name
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -20,6 +20,7 @@ Mongoid.configure do |config|
|
||||
name = "custom_fields_test"
|
||||
host = "localhost"
|
||||
config.master = Mongo::Connection.new.db(name)
|
||||
# config.master = Mongo::Connection.new('localhost', '27017', :logger => Logger.new($stdout)).db(name)
|
||||
end
|
||||
|
||||
Rspec.configure do |config|
|
||||
|
@ -2,6 +2,27 @@ require 'spec_helper'
|
||||
|
||||
describe CustomFields::CustomFieldsFor do
|
||||
|
||||
context '#proxy class' do
|
||||
|
||||
before(:each) do
|
||||
@project = Project.new
|
||||
@klass = @project.tasks.klass
|
||||
end
|
||||
|
||||
it 'returns the proxy class in the association' do
|
||||
@klass.should == @project.tasks.build.class
|
||||
end
|
||||
|
||||
it 'has a link to the parent' do
|
||||
@klass._parent.should == @project
|
||||
end
|
||||
|
||||
it 'has the association name which references to' do
|
||||
@klass.association_name.should == 'tasks'
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
context 'with embedded collection' do
|
||||
|
||||
context '#association' do
|
||||
@ -13,7 +34,7 @@ describe CustomFields::CustomFieldsFor do
|
||||
it 'has custom fields for embedded collection' do
|
||||
@project.respond_to?(:task_custom_fields).should be_true
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
context '#building' do
|
||||
|
@ -30,13 +30,43 @@ describe CustomFields::Types::Category do
|
||||
end
|
||||
|
||||
it 'has the values of this category' do
|
||||
@project.class.global_category_names.should == %w{Development Design Maintenance}
|
||||
@project.class.global_category_names.should == %w{Maintenance Design Development}
|
||||
end
|
||||
|
||||
it 'sets the category from a name' do
|
||||
@project.global_category = 'Design'
|
||||
@project.global_category.should == 'Design'
|
||||
@project.field_1.should_not be_nil
|
||||
@project.field_1.should == '42'
|
||||
end
|
||||
|
||||
it 'sets the category even it does not exit' do
|
||||
@project.global_category = 'Accounting'
|
||||
@project.global_category.should == 'Accounting'
|
||||
@project.field_1.should == 'Accounting'
|
||||
end
|
||||
|
||||
context 'group by category' do
|
||||
|
||||
before(:each) do
|
||||
seed_projects
|
||||
@groups = @project.class.group_by_global_category
|
||||
end
|
||||
|
||||
it 'is an non empty array' do
|
||||
@groups.class.should == Array
|
||||
@groups.size.should == 3
|
||||
end
|
||||
|
||||
it 'is an array of hash composed of a name' do
|
||||
@groups.collect { |g| g[:name] }.should == %w{Maintenance Design Development}
|
||||
end
|
||||
|
||||
it 'is an array of hash composed of a list of objects' do
|
||||
@groups[0][:items].size.should == 0
|
||||
@groups[1][:items].size.should == 1
|
||||
@groups[2][:items].size.should == 2
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
@ -49,10 +79,19 @@ describe CustomFields::Types::Category do
|
||||
def build_category
|
||||
field = CustomFields::Field.new(:label => 'global_category', :_name => 'field_1', :kind => 'Category')
|
||||
field.stubs(:valid?).returns(true)
|
||||
field.category_items.build :name => 'Development'
|
||||
field.category_items.build :name => 'Design'
|
||||
field.category_items.build :name => 'Maintenance'
|
||||
field.category_items.build :name => 'Development', :_id => '41', :position => 2
|
||||
field.category_items.build :name => 'Design', :_id => '42', :position => 1
|
||||
field.category_items.build :name => 'Maintenance', :_id => '43', :position => 0
|
||||
field
|
||||
end
|
||||
|
||||
def seed_projects
|
||||
list = [
|
||||
@project.class.new(:name => 'Locomotive CMS', :global_category => '41'),
|
||||
@project.class.new(:name => 'Ruby on Rails', :global_category => '41'),
|
||||
@project.class.new(:name => 'Dribble', :global_category => '42')
|
||||
]
|
||||
@project.class.stubs(:all).returns(list)
|
||||
end
|
||||
|
||||
end
|
Loading…
Reference in New Issue
Block a user