rename CustomFields::CustomField into CustomFields::Field + create a dynamic class when we have an object with custom fields (optimization) + create a new type of custom field: categories + fix minor bugs + add ui to rename the alias of a custom field
This commit is contained in:
parent
3e7f18e8e3
commit
fc690d8a0b
1
.gitignore
vendored
1
.gitignore
vendored
@ -14,3 +14,4 @@ pkg
|
|||||||
*.gemspec
|
*.gemspec
|
||||||
rails_3_gems
|
rails_3_gems
|
||||||
doc/performance.txt
|
doc/performance.txt
|
||||||
|
doc/production.sh
|
||||||
|
10
Gemfile
10
Gemfile
@ -11,20 +11,14 @@ gem "mongoid", ">= 2.0.0.beta6"
|
|||||||
gem "mongoid_acts_as_tree", ">= 0.1.2"
|
gem "mongoid_acts_as_tree", ">= 0.1.2"
|
||||||
gem "warden"
|
gem "warden"
|
||||||
gem "devise", ">= 1.1.rc0"
|
gem "devise", ">= 1.1.rc0"
|
||||||
gem "haml", '>= 3.0.1'
|
gem "haml", ">= 3.0.1"
|
||||||
# gem "rmagick"
|
gem "rmagick", "2.12.2"
|
||||||
gem "aws"
|
gem "aws"
|
||||||
gem "jeweler"
|
gem "jeweler"
|
||||||
gem "mimetype-fu", :require => "mimetype_fu"
|
gem "mimetype-fu", :require => "mimetype_fu"
|
||||||
gem "formtastic-rails3", :require => "formtastic"
|
gem "formtastic-rails3", :require => "formtastic"
|
||||||
gem "carrierwave-rails3", :require => "carrierwave"
|
gem "carrierwave-rails3", :require => "carrierwave"
|
||||||
|
|
||||||
# gem 'formtastic-rails3', :require => 'formtastic', :path => 'rails_3_gems/formtastic'
|
|
||||||
# gem "formtastic", :git => 'git://github.com/justinfrench/formtastic.git', :branch => 'rails3'
|
|
||||||
# gem "carrierwave", :git => "http://github.com/jnicklas/carrierwave.git"
|
|
||||||
# gem "carrierwave", :path => 'rails_3_gems/carrierwave'
|
|
||||||
|
|
||||||
|
|
||||||
# Development environment
|
# Development environment
|
||||||
group :development do
|
group :development do
|
||||||
# Using mongrel instead of webrick (default server)
|
# Using mongrel instead of webrick (default server)
|
||||||
|
@ -45,7 +45,7 @@ module Admin
|
|||||||
flash[:error] = e.to_s
|
flash[:error] = e.to_s
|
||||||
end
|
end
|
||||||
|
|
||||||
redirect_to admin_content_types_url
|
redirect_to admin_pages_url
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
@ -2,7 +2,7 @@ module Admin::CustomFieldsHelper
|
|||||||
|
|
||||||
def options_for_field_kind(selected = nil)
|
def options_for_field_kind(selected = nil)
|
||||||
# %w{String Text Boolean Email File Date}
|
# %w{String Text Boolean Email File Date}
|
||||||
options = %w{String Text}.map do |kind|
|
options = %w{String Text Select}.map do |kind|
|
||||||
[t("admin.custom_fields.kind.#{kind.downcase}"), kind]
|
[t("admin.custom_fields.kind.#{kind.downcase}"), kind]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -5,6 +5,7 @@ class Asset
|
|||||||
|
|
||||||
## Extensions ##
|
## Extensions ##
|
||||||
include Models::Extensions::Asset::Vignette
|
include Models::Extensions::Asset::Vignette
|
||||||
|
include CustomFields::ProxyClassEnabler
|
||||||
|
|
||||||
## fields ##
|
## fields ##
|
||||||
field :name, :type => String
|
field :name, :type => String
|
||||||
|
@ -53,6 +53,8 @@ class ThemeAsset
|
|||||||
end
|
end
|
||||||
|
|
||||||
def performing_plain_text?
|
def performing_plain_text?
|
||||||
|
return true if !self.new_record? && !self.image? && self.errors.empty?
|
||||||
|
|
||||||
!(self.performing_plain_text.blank? || self.performing_plain_text == 'false' || self.performing_plain_text == false)
|
!(self.performing_plain_text.blank? || self.performing_plain_text == 'false' || self.performing_plain_text == false)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -35,8 +35,8 @@ class AssetUploader < CarrierWave::Uploader::Base
|
|||||||
self.class.content_types.each_pair do |type, rules|
|
self.class.content_types.each_pair do |type, rules|
|
||||||
rules.each do |rule|
|
rules.each do |rule|
|
||||||
case rule
|
case rule
|
||||||
when String then value = type if file.content_type == rule
|
when String then value = type if content_type == rule
|
||||||
when Regexp then value = type if (file.content_type =~ rule) == 0
|
when Regexp then value = type if (content_type =~ rule) == 0
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -1,13 +1,15 @@
|
|||||||
- content_for :head do
|
- content_for :head do
|
||||||
= javascript_include_tag 'admin/custom_fields'
|
= javascript_include_tag 'admin/plugins/fancybox', 'admin/custom_fields'
|
||||||
|
= stylesheet_link_tag 'admin/plugins/fancybox', 'admin/box'
|
||||||
|
|
||||||
= f.inputs :name => :information do
|
= f.inputs :name => :information do
|
||||||
= f.input :name
|
= f.input :name
|
||||||
= f.input :slug
|
= f.input :slug
|
||||||
= f.input :description
|
= f.input :description
|
||||||
|
|
||||||
= render 'admin/shared/custom_fields', :f => f, :collection_name => 'contents'
|
= render 'admin/custom_fields/index', :f => f, :collection_name => 'contents'
|
||||||
|
|
||||||
= f.foldable_inputs :name => :options, :class => 'switchable' do
|
= 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 :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
|
= f.input :order_by, :as => :select, :collection => options_for_order_by(f.object, 'contents'), :include_blank => false
|
||||||
|
|
||||||
|
@ -14,3 +14,5 @@
|
|||||||
= render 'form', :f => form
|
= render 'form', :f => form
|
||||||
|
|
||||||
= render 'admin/shared/form_actions', :back_url => admin_contents_url(@content_type.slug), :button_label => :update
|
= render 'admin/shared/form_actions', :back_url => admin_contents_url(@content_type.slug), :button_label => :update
|
||||||
|
|
||||||
|
= render 'admin/custom_fields/edit'
|
8
app/views/admin/custom_fields/_edit.html.haml
Normal file
8
app/views/admin/custom_fields/_edit.html.haml
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
.box-wrapper
|
||||||
|
#edit-custom-field
|
||||||
|
%h2= t('.title')
|
||||||
|
|
||||||
|
= form_tag '#', :class => 'formtastic' do
|
||||||
|
= fields_for CustomFields::Field.new, :builder => Formtastic::SemanticFormHelper.builder do |g|
|
||||||
|
= g.inputs :name => :information do
|
||||||
|
= g.input :_alias
|
53
app/views/admin/custom_fields/_index.html.haml
Normal file
53
app/views/admin/custom_fields/_index.html.haml
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
- 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 off' 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'), '#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|
|
||||||
|
%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'), '#edit-custom-field', :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')
|
@ -11,6 +11,8 @@
|
|||||||
|
|
||||||
= g.hidden_field :position, :class => 'position'
|
= g.hidden_field :position, :class => 'position'
|
||||||
|
|
||||||
|
= g.hidden_field :_alias, :class => 'alias'
|
||||||
|
|
||||||
= g.text_field :label
|
= g.text_field :label
|
||||||
|
|
||||||
—
|
—
|
||||||
@ -22,7 +24,8 @@
|
|||||||
|
|
||||||
|
|
||||||
%span.actions
|
%span.actions
|
||||||
= link_to image_tag('admin/form/icons/trash.png'), '#', :class => 'remove first', :confirm => t('admin.messages.confirm')
|
= 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|
|
= f.fields_for collection_name.to_sym, custom_fields.build(:label => 'field name'), :child_index => '-1' do |g|
|
||||||
%li{ :class => 'item template' }
|
%li{ :class => 'item template' }
|
||||||
@ -31,6 +34,8 @@
|
|||||||
|
|
||||||
= g.hidden_field :position, :class => 'position'
|
= g.hidden_field :position, :class => 'position'
|
||||||
|
|
||||||
|
= g.hidden_field :_alias, :class => 'alias'
|
||||||
|
|
||||||
= g.text_field :label, :class => 'string label void'
|
= g.text_field :label, :class => 'string label void'
|
||||||
|
|
||||||
—
|
—
|
||||||
@ -42,6 +47,9 @@
|
|||||||
|
|
||||||
|
|
||||||
%span.actions
|
%span.actions
|
||||||
= link_to image_tag('admin/form/icons/trash.png'), '#', :class => 'remove first', :confirm => t('admin.messages.confirm')
|
= 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' }
|
%button{ :class => 'button light add', :type => 'button' }
|
||||||
%span= t('admin.buttons.new_item')
|
%span= t('admin.buttons.new_item')
|
||||||
|
|
||||||
|
|
@ -24,7 +24,7 @@
|
|||||||
%span.actions
|
%span.actions
|
||||||
= link_to image_tag('admin/form/icons/trash.png'), '#', :class => 'remove first', :confirm => t('admin.messages.confirm')
|
= link_to image_tag('admin/form/icons/trash.png'), '#', :class => 'remove first', :confirm => t('admin.messages.confirm')
|
||||||
|
|
||||||
%li.item.new
|
%li.item.template
|
||||||
%em
|
%em
|
||||||
http://
|
http://
|
||||||
= text_field_tag 'label', t('formtastic.hints.site.domain_name'), :class => 'string label void'
|
= text_field_tag 'label', t('formtastic.hints.site.domain_name'), :class => 'string label void'
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
- if @image_assets.empty?
|
- if @image_assets.empty?
|
||||||
%p.no-items= t('.no_items')
|
%p.no-items= t('.no_items')
|
||||||
- else
|
|
||||||
%ul.assets
|
%ul.assets
|
||||||
= render 'asset', :asset => current_site.theme_assets.build, :edit => false
|
= render 'asset', :asset => current_site.theme_assets.build, :edit => false
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ require File.expand_path('../boot', __FILE__)
|
|||||||
require "action_controller/railtie"
|
require "action_controller/railtie"
|
||||||
require "action_mailer/railtie"
|
require "action_mailer/railtie"
|
||||||
require "active_resource/railtie"
|
require "active_resource/railtie"
|
||||||
|
require "mongoid/railtie"
|
||||||
|
|
||||||
# Auto-require default libraries and those for the current Rails environment.
|
# Auto-require default libraries and those for the current Rails environment.
|
||||||
Bundler.require :default, Rails.env
|
Bundler.require :default, Rails.env
|
||||||
|
@ -1,20 +0,0 @@
|
|||||||
defaults: &defaults
|
|
||||||
host: localhost
|
|
||||||
|
|
||||||
development:
|
|
||||||
<<: *defaults
|
|
||||||
database: locomotive_dev
|
|
||||||
|
|
||||||
test: &test
|
|
||||||
<<: *defaults
|
|
||||||
database: locomotive_test
|
|
||||||
|
|
||||||
production:
|
|
||||||
<<: *defaults
|
|
||||||
host: db.mongohq.com
|
|
||||||
username: user
|
|
||||||
password: pass
|
|
||||||
database: fanboy
|
|
||||||
|
|
||||||
cucumber:
|
|
||||||
<<: *test
|
|
@ -31,15 +31,3 @@ Locomotive::Application.configure do
|
|||||||
# Enable threaded mode
|
# Enable threaded mode
|
||||||
# config.threadsafe!
|
# config.threadsafe!
|
||||||
end
|
end
|
||||||
|
|
||||||
config.action_mailer.delivery_method = :smtp
|
|
||||||
|
|
||||||
config.action_mailer.smtp_settings = {
|
|
||||||
:enable_starttls_auto => true,
|
|
||||||
:address => "smtp.gmail.com",
|
|
||||||
:port => 587,
|
|
||||||
:domain => "nocoffee.fr",
|
|
||||||
:authentication => :plain,
|
|
||||||
:user_name => "didier@nocoffee.fr",
|
|
||||||
:password => "pepscou"
|
|
||||||
}
|
|
@ -1,11 +1,5 @@
|
|||||||
require 'mongoid'
|
require 'mongoid'
|
||||||
|
|
||||||
File.open(File.join(Rails.root, 'config/database.yml'), 'r') do |f|
|
|
||||||
@settings = YAML.load(f)[Rails.env]
|
|
||||||
end
|
|
||||||
|
|
||||||
Mongoid::Config.instance.from_hash(@settings)
|
|
||||||
|
|
||||||
## various patches
|
## various patches
|
||||||
module Mongoid #:nodoc:
|
module Mongoid #:nodoc:
|
||||||
|
|
||||||
|
@ -33,9 +33,12 @@ en:
|
|||||||
update: Update
|
update: Update
|
||||||
|
|
||||||
custom_fields:
|
custom_fields:
|
||||||
|
edit:
|
||||||
|
title: Editing custom field
|
||||||
kind:
|
kind:
|
||||||
string: Simple Input
|
string: Simple Input
|
||||||
text: Text
|
text: Text
|
||||||
|
select: Select
|
||||||
|
|
||||||
sessions:
|
sessions:
|
||||||
new:
|
new:
|
||||||
@ -173,7 +176,6 @@ en:
|
|||||||
edit:
|
edit:
|
||||||
title: '{{type}} — editing item'
|
title: '{{type}} — editing item'
|
||||||
|
|
||||||
|
|
||||||
formtastic:
|
formtastic:
|
||||||
titles:
|
titles:
|
||||||
information: General information
|
information: General information
|
||||||
@ -196,6 +198,10 @@ en:
|
|||||||
source: File
|
source: File
|
||||||
edit:
|
edit:
|
||||||
source: Replace file
|
source: Replace file
|
||||||
|
custom_fields:
|
||||||
|
custom_field:
|
||||||
|
_alias: Alias
|
||||||
|
|
||||||
hints:
|
hints:
|
||||||
page:
|
page:
|
||||||
keywords: "Meta keywords used within the head tag of the page. They are separeted by an empty space. Required for SEO."
|
keywords: "Meta keywords used within the head tag of the page. They are separeted by an empty space. Required for SEO."
|
||||||
|
24
config/mongoid.yml
Normal file
24
config/mongoid.yml
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
defaults: &defaults
|
||||||
|
host: localhost
|
||||||
|
# slaves:
|
||||||
|
# - host: slave1.local
|
||||||
|
# port: 27018
|
||||||
|
# - host: slave2.local
|
||||||
|
# port: 27019
|
||||||
|
|
||||||
|
development:
|
||||||
|
<<: *defaults
|
||||||
|
database: locomotive_dev
|
||||||
|
|
||||||
|
test:
|
||||||
|
<<: *defaults
|
||||||
|
database: locomotive_test
|
||||||
|
|
||||||
|
# set these environment variables on your prod server
|
||||||
|
production:
|
||||||
|
<<: *defaults
|
||||||
|
host: <%= ENV['MONGOID_HOST'] %>
|
||||||
|
port: <%= ENV['MONGOID_PORT'] %>
|
||||||
|
username: <%= ENV['MONGOID_USERNAME'] %>
|
||||||
|
password: <%= ENV['MONGOID_PASSWORD'] %>
|
||||||
|
database: <%= ENV['MONGOID_DATABASE'] %>
|
@ -49,6 +49,7 @@ Rails.application.routes.draw do |map|
|
|||||||
resources :contents, :path => "content_types/:slug/contents" do
|
resources :contents, :path => "content_types/:slug/contents" do
|
||||||
put :sort, :on => :collection
|
put :sort, :on => :collection
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# magic urls
|
# magic urls
|
||||||
|
20
doc/TODO
20
doc/TODO
@ -6,13 +6,10 @@ x make an engine:
|
|||||||
|
|
||||||
- deploy on Heroku
|
- deploy on Heroku
|
||||||
|
|
||||||
- refactoring: CustomFields::CustomField => CustomFields::Field
|
x refactoring: CustomFields::CustomField => CustomFields::Field
|
||||||
- new types for custom field
|
- new custom field type
|
||||||
- file
|
- category
|
||||||
- boolean
|
x optimization custom_fields: use dynamic class for a collection instead of modifying the metaclass each time we build an item
|
||||||
- date
|
|
||||||
- optimization custom_fields: use dynamic class for a collection instead of modifying the metaclass each time we build an item
|
|
||||||
|
|
||||||
|
|
||||||
BACKLOG:
|
BACKLOG:
|
||||||
|
|
||||||
@ -24,9 +21,16 @@ BACKLOG:
|
|||||||
|
|
||||||
- theme assets: disable version if not image
|
- theme assets: disable version if not image
|
||||||
|
|
||||||
|
- new custom field types
|
||||||
|
- file
|
||||||
|
- boolean
|
||||||
|
- date
|
||||||
|
|
||||||
|
- refactor slugify method (use parameterize + create a module)
|
||||||
|
|
||||||
BUGS:
|
BUGS:
|
||||||
- when assigning new layout, disabled parts show up :-( (js problem)
|
- when assigning new layout, disabled parts show up :-( (js problem)
|
||||||
- password resets
|
- password resets (url is not handled correctly)
|
||||||
|
|
||||||
NICE TO HAVE:
|
NICE TO HAVE:
|
||||||
- asset collections: custom resizing if image
|
- asset collections: custom resizing if image
|
||||||
|
@ -32,4 +32,13 @@ class MiscFormBuilder < Formtastic::SemanticFormBuilder
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def normalize_model_name(name)
|
||||||
|
if name =~ /(.+)\/(.+)/
|
||||||
|
[$1, $2]
|
||||||
|
else
|
||||||
|
super
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
BIN
public/images/admin/form/footer-small.png
Normal file
BIN
public/images/admin/form/footer-small.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 287 B |
BIN
public/images/admin/form/header-small.png
Normal file
BIN
public/images/admin/form/header-small.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 525 B |
BIN
public/images/admin/form/icons/edit.png
Normal file
BIN
public/images/admin/form/icons/edit.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 221 B |
BIN
public/images/admin/list/none-small.png
Normal file
BIN
public/images/admin/list/none-small.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.4 KiB |
@ -19,15 +19,16 @@ $.growl.settings.dockCss = {
|
|||||||
/* ___ codemirror ___ */
|
/* ___ codemirror ___ */
|
||||||
|
|
||||||
var addCodeMirrorEditor = function(type, el, parser) {
|
var addCodeMirrorEditor = function(type, el, parser) {
|
||||||
|
if (type == 'liquid') type = 'xml';
|
||||||
var parserfile = "parse" + type + ".js";
|
var parserfile = "parse" + type + ".js";
|
||||||
if (parser != undefined) parserfile = parser;
|
if (parser != undefined) parserfile = parser;
|
||||||
// if (type == 'liquid') type = 'xml';
|
|
||||||
|
|
||||||
var editor = CodeMirror.fromTextArea(el.attr('id'), {
|
var editor = CodeMirror.fromTextArea(el.attr('id'), {
|
||||||
height: "400px",
|
height: "400px",
|
||||||
parserfile: parserfile,
|
parserfile: parserfile,
|
||||||
stylesheet: [
|
stylesheet: [
|
||||||
"/stylesheets/admin/plugins/codemirror/csscolors.css",
|
"/stylesheets/admin/plugins/codemirror/csscolors.css",
|
||||||
|
"/stylesheets/admin/plugins/codemirror/xmlcolors.css",
|
||||||
"/stylesheets/admin/plugins/codemirror/javascriptcolors.css",
|
"/stylesheets/admin/plugins/codemirror/javascriptcolors.css",
|
||||||
"/stylesheets/admin/plugins/codemirror/liquidcolors.css"],
|
"/stylesheets/admin/plugins/codemirror/liquidcolors.css"],
|
||||||
path: "/javascripts/admin/plugins/codemirror/",
|
path: "/javascripts/admin/plugins/codemirror/",
|
||||||
|
@ -110,4 +110,25 @@ $(document).ready(function() {
|
|||||||
axis: 'y',
|
axis: 'y',
|
||||||
update: refreshPosition
|
update: refreshPosition
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// edit in depth custom field
|
||||||
|
$('fieldset.fields li.item span.actions a.edit').click(function() {
|
||||||
|
var link = $(this);
|
||||||
|
$.fancybox({
|
||||||
|
titleShow: false,
|
||||||
|
content: $(link.attr('href')).parent().html(),
|
||||||
|
onComplete: function() {
|
||||||
|
$('#fancybox-wrap form').submit(function(e) {
|
||||||
|
$.fancybox.close();
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#fancybox-wrap #custom_fields_custom_field__alias').val(link.parent().prevAll('.alias').val());
|
||||||
|
},
|
||||||
|
onCleanup: function() {
|
||||||
|
link.parent().prevAll('.alias').val($('#fancybox-wrap #custom_fields_custom_field__alias').val());
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
});
|
});
|
@ -7,7 +7,7 @@ $(document).ready(function() {
|
|||||||
|
|
||||||
if (!slug.hasClass('filled')) {
|
if (!slug.hasClass('filled')) {
|
||||||
setTimeout(function() {
|
setTimeout(function() {
|
||||||
slug.val(input.val().replace(/\s/g, '_').toLowerCase());
|
slug.val(input.val().replace(/[\s']/g, '_').toLowerCase());
|
||||||
}, 50);
|
}, 50);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -71,6 +71,8 @@ var setupUploader = function() {
|
|||||||
|
|
||||||
asset.removeClass('new-asset');
|
asset.removeClass('new-asset');
|
||||||
|
|
||||||
|
$('.asset-picker p.no-items').hide();
|
||||||
|
|
||||||
$('.asset-picker ul').scrollTo($('li.asset:last'), 400);
|
$('.asset-picker ul').scrollTo($('li.asset:last'), 400);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
/* custom styles for fancybox */
|
/* custom styles for fancybox */
|
||||||
|
|
||||||
div.asset-picker { width: 470px; position: relative; }
|
/* ___ common ___ */
|
||||||
div.asset-picker .actions { position: absolute; right: 4px; top: 0px; }
|
|
||||||
div.asset-picker h2 {
|
.box-wrapper { display: none; }
|
||||||
|
|
||||||
|
#fancybox-inner h2 {
|
||||||
border-bottom:1px dotted #BBBBBD;
|
border-bottom:1px dotted #BBBBBD;
|
||||||
color:#1E1F26;
|
color:#1E1F26;
|
||||||
font-size:1.1em;
|
font-size:1.1em;
|
||||||
@ -10,5 +12,28 @@ div.asset-picker h2 {
|
|||||||
padding-bottom:10px;
|
padding-bottom:10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#fancybox-inner form.formtastic legend span {
|
||||||
|
background-image: url("/images/admin/form/header-small.png");
|
||||||
|
width: 450px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#fancybox-inner form.formtastic ol {
|
||||||
|
background-image: url("/images/admin/form/footer-small.png");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ___ asset picker ___ */
|
||||||
|
|
||||||
|
div.asset-picker { width: 470px; position: relative; }
|
||||||
|
div.asset-picker .actions { position: absolute; right: 4px; top: 0px; }
|
||||||
|
|
||||||
|
div.asset-picker p.no-items { background-image: url("/images/admin/list/none-small.png"); }
|
||||||
|
|
||||||
div.asset-picker ul { overflow: auto; height: 471px; }
|
div.asset-picker ul { overflow: auto; height: 471px; }
|
||||||
div.asset-picker ul li.new-asset { display: none; }
|
div.asset-picker ul li.new-asset { display: none; }
|
||||||
|
|
||||||
|
/* ___ custom fields ___ */
|
||||||
|
|
||||||
|
#edit-custom-field {
|
||||||
|
width: 470px;
|
||||||
|
}
|
@ -226,8 +226,9 @@ form.formtastic fieldset ol li.item span.actions {
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
top: 7px;
|
top: 7px;
|
||||||
right: 10px;
|
right: 10px;
|
||||||
width: 16px;
|
width: 50px;
|
||||||
height: 16px;
|
height: 16px;
|
||||||
|
text-align:right;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ___ editable-list (content type fields and validations) ___ */
|
/* ___ editable-list (content type fields and validations) ___ */
|
||||||
@ -338,6 +339,10 @@ form.formtastic fieldset.editable-list ol li.template span.actions {
|
|||||||
top: 10px;
|
top: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
form.formtastic fieldset.editable-list ol li.template span.actions a.edit {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
form.formtastic fieldset.editable-list ol li.template span.actions a.remove {
|
form.formtastic fieldset.editable-list ol li.template span.actions a.remove {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@ describe AssetCollection do
|
|||||||
context 'unit' do
|
context 'unit' do
|
||||||
|
|
||||||
before(:each) do
|
before(:each) do
|
||||||
@field = CustomFields::CustomField.new(:kind => 'String')
|
@field = CustomFields::Field.new(:kind => 'String')
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'should tell if it is a String' do
|
it 'should tell if it is a String' do
|
||||||
@ -75,6 +75,7 @@ describe AssetCollection do
|
|||||||
context 'build and save' do
|
context 'build and save' do
|
||||||
|
|
||||||
it 'should build asset' do
|
it 'should build asset' do
|
||||||
|
puts "___ TEST #1 ___"
|
||||||
asset = @collection.assets.build
|
asset = @collection.assets.build
|
||||||
lambda {
|
lambda {
|
||||||
asset.description
|
asset.description
|
||||||
@ -84,12 +85,14 @@ describe AssetCollection do
|
|||||||
end
|
end
|
||||||
|
|
||||||
it 'should assign values to newly built asset' do
|
it 'should assign values to newly built asset' do
|
||||||
|
puts "___ TEST #2 ___"
|
||||||
asset = build_asset(@collection)
|
asset = build_asset(@collection)
|
||||||
asset.description.should == 'Lorem ipsum'
|
asset.description.should == 'Lorem ipsum'
|
||||||
asset.active.should == true
|
asset.active.should == true
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'should save asset' do
|
it 'should save asset' do
|
||||||
|
puts "___ TEST #3 ___"
|
||||||
asset = build_asset(@collection)
|
asset = build_asset(@collection)
|
||||||
asset.save and @collection.reload
|
asset.save and @collection.reload
|
||||||
asset = @collection.assets.first
|
asset = @collection.assets.first
|
||||||
@ -98,6 +101,7 @@ describe AssetCollection do
|
|||||||
end
|
end
|
||||||
|
|
||||||
it 'should not modify assets from another collection' do
|
it 'should not modify assets from another collection' do
|
||||||
|
puts "___ TEST #4 ___"
|
||||||
asset = build_asset(@collection)
|
asset = build_asset(@collection)
|
||||||
asset.save and @collection.reload
|
asset.save and @collection.reload
|
||||||
new_collection = AssetCollection.new
|
new_collection = AssetCollection.new
|
||||||
|
3
vendor/plugins/custom_fields/.rspec
vendored
Normal file
3
vendor/plugins/custom_fields/.rspec
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
--colour
|
||||||
|
--format nested
|
||||||
|
--backtrace
|
11
vendor/plugins/custom_fields/Gemfile
vendored
Normal file
11
vendor/plugins/custom_fields/Gemfile
vendored
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
source "http://gemcutter.org"
|
||||||
|
|
||||||
|
gem "bson_ext", ">= 1.0.1"
|
||||||
|
gem "mongo_ext"
|
||||||
|
gem "mongoid", ">= 2.0.0.beta6"
|
||||||
|
gem "activesupport", ">= 3.0.0.beta3"
|
||||||
|
|
||||||
|
group :test do
|
||||||
|
gem 'rspec', '>= 2.0.0.beta.10'
|
||||||
|
gem 'mocha', :git => 'git://github.com/floehopper/mocha.git'
|
||||||
|
end
|
4
vendor/plugins/custom_fields/README
vendored
4
vendor/plugins/custom_fields/README
vendored
@ -1,4 +1,4 @@
|
|||||||
CustomField
|
CustomFields
|
||||||
===========
|
===========
|
||||||
|
|
||||||
Introduction goes here.
|
Introduction goes here.
|
||||||
@ -10,4 +10,4 @@ Example
|
|||||||
Example goes here.
|
Example goes here.
|
||||||
|
|
||||||
|
|
||||||
Copyright (c) 2010 [name of plugin creator], released under the MIT license
|
Copyright (c) 2010 [Didier Lafforgue], released under the MIT license
|
||||||
|
42
vendor/plugins/custom_fields/Rakefile
vendored
42
vendor/plugins/custom_fields/Rakefile
vendored
@ -1,23 +1,33 @@
|
|||||||
require 'rake'
|
require "rubygems"
|
||||||
require 'rake/testtask'
|
require "rake"
|
||||||
require 'rake/rdoctask'
|
require "rake/rdoctask"
|
||||||
|
require "rspec"
|
||||||
|
require "rspec/core/rake_task"
|
||||||
|
|
||||||
desc 'Default: run unit tests.'
|
desc 'Generate documentation for the custom_fields plugin.'
|
||||||
task :default => :test
|
|
||||||
|
|
||||||
desc 'Test the custom_field plugin.'
|
|
||||||
Rake::TestTask.new(:test) do |t|
|
|
||||||
t.libs << 'lib'
|
|
||||||
t.libs << 'test'
|
|
||||||
t.pattern = 'test/**/*_test.rb'
|
|
||||||
t.verbose = true
|
|
||||||
end
|
|
||||||
|
|
||||||
desc 'Generate documentation for the custom_field plugin.'
|
|
||||||
Rake::RDocTask.new(:rdoc) do |rdoc|
|
Rake::RDocTask.new(:rdoc) do |rdoc|
|
||||||
rdoc.rdoc_dir = 'rdoc'
|
rdoc.rdoc_dir = 'rdoc'
|
||||||
rdoc.title = 'CustomField'
|
rdoc.title = 'CustomFields'
|
||||||
rdoc.options << '--line-numbers' << '--inline-source'
|
rdoc.options << '--line-numbers' << '--inline-source'
|
||||||
rdoc.rdoc_files.include('README')
|
rdoc.rdoc_files.include('README')
|
||||||
rdoc.rdoc_files.include('lib/**/*.rb')
|
rdoc.rdoc_files.include('lib/**/*.rb')
|
||||||
end
|
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"
|
||||||
|
end
|
||||||
|
|
||||||
|
Rspec::Core::RakeTask.new('spec:integration') do |spec|
|
||||||
|
spec.pattern = "spec/integration/**/*_spec.rb"
|
||||||
|
end
|
||||||
|
|
||||||
|
task :spec => [:check_dependencies, 'spec:unit', 'spec:integration']
|
||||||
|
|
||||||
|
task :default => :spec
|
1
vendor/plugins/custom_fields/install.rb
vendored
1
vendor/plugins/custom_fields/install.rb
vendored
@ -1 +0,0 @@
|
|||||||
# Install hook code here
|
|
@ -1,7 +1,15 @@
|
|||||||
$:.unshift File.expand_path(File.dirname(__FILE__))
|
$:.unshift File.expand_path(File.dirname(__FILE__))
|
||||||
|
|
||||||
|
require 'active_support'
|
||||||
|
|
||||||
|
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/associations/embeds_many'
|
||||||
require 'custom_fields/extensions/mongoid/document'
|
require 'custom_fields/extensions/mongoid/document'
|
||||||
|
require 'custom_fields/types/default'
|
||||||
|
require 'custom_fields/types/category'
|
||||||
|
require 'custom_fields/proxy_class_enabler'
|
||||||
|
require 'custom_fields/field'
|
||||||
require 'custom_fields/custom_fields_for'
|
require 'custom_fields/custom_fields_for'
|
||||||
|
|
||||||
module Mongoid
|
module Mongoid
|
||||||
|
@ -30,7 +30,7 @@ module CustomFields
|
|||||||
class_eval <<-EOV
|
class_eval <<-EOV
|
||||||
field :#{singular_name}_custom_fields_counter, :type => Integer, :default => 0
|
field :#{singular_name}_custom_fields_counter, :type => Integer, :default => 0
|
||||||
|
|
||||||
embeds_many :#{singular_name}_custom_fields, :class_name => "::CustomFields::CustomField"
|
embeds_many :#{singular_name}_custom_fields, :class_name => "::CustomFields::Field"
|
||||||
|
|
||||||
validates_associated :#{singular_name}_custom_fields
|
validates_associated :#{singular_name}_custom_fields
|
||||||
|
|
||||||
@ -39,6 +39,7 @@ module CustomFields
|
|||||||
def ordered_#{singular_name}_custom_fields
|
def ordered_#{singular_name}_custom_fields
|
||||||
self.#{singular_name}_custom_fields.sort { |a, b| (a.position || 0) <=> (b.position || 0) }
|
self.#{singular_name}_custom_fields.sort { |a, b| (a.position || 0) <=> (b.position || 0) }
|
||||||
end
|
end
|
||||||
|
|
||||||
EOV
|
EOV
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -2,6 +2,27 @@
|
|||||||
module Mongoid #:nodoc:
|
module Mongoid #:nodoc:
|
||||||
module Associations #:nodoc:
|
module Associations #:nodoc:
|
||||||
class EmbedsMany < Proxy
|
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
|
||||||
|
|
||||||
|
custom_fields = parent.send(:"ordered_#{custom_fields_association_name(options.name)}")
|
||||||
|
|
||||||
|
klass = options.klass.to_klass_with_custom_fields(custom_fields)
|
||||||
|
|
||||||
|
options.instance_eval <<-EOF
|
||||||
|
def klass=(klass); @klass = klass; end
|
||||||
|
def klass; @klass || class_name.constantize; end
|
||||||
|
EOF
|
||||||
|
|
||||||
|
options.klass = klass
|
||||||
|
end
|
||||||
|
|
||||||
|
initialize_without_custom_fields(parent, options, target_array)
|
||||||
|
end
|
||||||
|
|
||||||
|
alias_method_chain :initialize, :custom_fields
|
||||||
|
|
||||||
def build_with_custom_field_settings(attrs = {}, type = nil)
|
def build_with_custom_field_settings(attrs = {}, type = nil)
|
||||||
document = build_without_custom_field_settings(attrs, type)
|
document = build_without_custom_field_settings(attrs, type)
|
||||||
|
|
||||||
@ -19,6 +40,5 @@ module Mongoid #:nodoc:
|
|||||||
|
|
||||||
alias_method_chain :build, :custom_field_settings
|
alias_method_chain :build, :custom_field_settings
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
@ -0,0 +1,31 @@
|
|||||||
|
# encoding: utf-8
|
||||||
|
module Mongoid #:nodoc:
|
||||||
|
module Associations #:nodoc:
|
||||||
|
# Represents an relational one-to-many association with an object in a
|
||||||
|
# separate collection or database.
|
||||||
|
class HasManyRelated < 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
|
||||||
|
|
||||||
|
custom_fields = parent.send(:"ordered_#{custom_fields_association_name(options.name)}")
|
||||||
|
|
||||||
|
klass = options.klass.to_klass_with_custom_fields(custom_fields)
|
||||||
|
|
||||||
|
options.instance_eval <<-EOF
|
||||||
|
def klass=(klass); @klass = klass; end
|
||||||
|
def klass; @klass || class_name.constantize; end
|
||||||
|
EOF
|
||||||
|
|
||||||
|
options.klass = klass
|
||||||
|
end
|
||||||
|
|
||||||
|
initialize_without_custom_fields(parent, options, target_array)
|
||||||
|
end
|
||||||
|
|
||||||
|
alias_method_chain :initialize, :custom_fields
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
16
vendor/plugins/custom_fields/lib/custom_fields/extensions/mongoid/associations/proxy.rb
vendored
Normal file
16
vendor/plugins/custom_fields/lib/custom_fields/extensions/mongoid/associations/proxy.rb
vendored
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
# encoding: utf-8
|
||||||
|
module Mongoid #:nodoc
|
||||||
|
module Associations #:nodoc
|
||||||
|
class Proxy #:nodoc
|
||||||
|
|
||||||
|
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))
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
@ -2,34 +2,34 @@
|
|||||||
module Mongoid #:nodoc:
|
module Mongoid #:nodoc:
|
||||||
module Document
|
module Document
|
||||||
module InstanceMethods
|
module InstanceMethods
|
||||||
def parentize_with_custom_fields(object, association_name)
|
# def parentize_with_custom_fields(object, association_name)
|
||||||
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)
|
||||||
# puts "[parentize_with_custom_fields] association_name = #{association_name} / #{self.custom_fields_association_name(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|
|
# object.send(self.custom_fields_association_name(association_name)).each do |field|
|
||||||
field.apply(self, association_name)
|
# field.apply(self)
|
||||||
end
|
# end
|
||||||
|
#
|
||||||
self.instance_eval <<-EOV
|
# self.instance_eval <<-EOV
|
||||||
def custom_fields
|
# def custom_fields
|
||||||
fields = self._parent.send(:#{self.custom_fields_association_name(association_name)})
|
# fields = self._parent.send(:#{self.custom_fields_association_name(association_name)})
|
||||||
fields.sort { |a, b| (a.position || 0) <=> (b.position || 0) }
|
# fields.sort { |a, b| (a.position || 0) <=> (b.position || 0) }
|
||||||
end
|
# end
|
||||||
EOV
|
# EOV
|
||||||
end
|
# end
|
||||||
end
|
# end
|
||||||
|
#
|
||||||
alias_method_chain :parentize, :custom_fields
|
# alias_method_chain :parentize, :custom_fields
|
||||||
|
#
|
||||||
def custom_fields_association_name(association_name)
|
# def custom_fields_association_name(association_name)
|
||||||
"#{association_name.to_s.singularize}_custom_fields".to_sym
|
# "#{association_name.to_s.singularize}_custom_fields".to_sym
|
||||||
end
|
# end
|
||||||
|
#
|
||||||
def custom_fields?(object, association_name)
|
# def custom_fields?(object, association_name)
|
||||||
object.respond_to?(custom_fields_association_name(association_name)) &&
|
# object.respond_to?(custom_fields_association_name(association_name)) &&
|
||||||
object.associations[association_name]
|
# object.associations[association_name]
|
||||||
end
|
# end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
@ -1,8 +1,12 @@
|
|||||||
module CustomFields
|
module CustomFields
|
||||||
|
|
||||||
class CustomField
|
class Field
|
||||||
include Mongoid::Document
|
include ::Mongoid::Document
|
||||||
include Mongoid::Timestamps
|
include ::Mongoid::Timestamps
|
||||||
|
|
||||||
|
# types ##
|
||||||
|
include Types::Default
|
||||||
|
include Types::Category
|
||||||
|
|
||||||
## fields ##
|
## fields ##
|
||||||
field :label, :type => String
|
field :label, :type => String
|
||||||
@ -17,7 +21,7 @@ module CustomFields
|
|||||||
|
|
||||||
## methods ##
|
## methods ##
|
||||||
|
|
||||||
%w{String Text Email Boolean Date File}.each do |kind|
|
%w{String Text Category}.each do |kind|
|
||||||
define_method "#{kind.downcase}?" do
|
define_method "#{kind.downcase}?" do
|
||||||
self.kind == kind
|
self.kind == kind
|
||||||
end
|
end
|
||||||
@ -25,26 +29,41 @@ module CustomFields
|
|||||||
|
|
||||||
def field_type
|
def field_type
|
||||||
case self.kind
|
case self.kind
|
||||||
when 'String', 'Text', 'Email' then String
|
when 'String', 'Text', 'Category' then String
|
||||||
else
|
else
|
||||||
self.kind.constantize
|
self.kind.constantize
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def apply(object, association_name)
|
def apply(klass)
|
||||||
return unless self.valid?
|
return unless self.valid?
|
||||||
|
|
||||||
# trick mongoid
|
klass.field self._name, :type => self.field_type
|
||||||
object.class_eval { def meta; (class << self; self; end); end }
|
|
||||||
object.meta.fields = object.fields.clone
|
|
||||||
object.meta.send(:define_method, :fields) { self.meta.fields }
|
|
||||||
|
|
||||||
object.meta.field self._name, :type => self.field_type
|
case self.kind
|
||||||
object.class_eval <<-EOF
|
when 'Category'
|
||||||
alias :#{self.safe_alias} :#{self._name}
|
apply_category_type(klass)
|
||||||
alias :#{self.safe_alias}= :#{self._name}=
|
else
|
||||||
EOF
|
apply_default_type(klass)
|
||||||
end
|
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
|
def safe_alias
|
||||||
self.set_alias
|
self.set_alias
|
||||||
@ -66,8 +85,7 @@ module CustomFields
|
|||||||
|
|
||||||
def set_alias
|
def set_alias
|
||||||
return if self.label.blank? && self._alias.blank?
|
return if self.label.blank? && self._alias.blank?
|
||||||
self._alias ||= self.label.clone
|
self._alias = (self._alias || self.label).parameterize('_').downcase
|
||||||
self._alias.slugify!(:downcase => true, :underscore => true)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def increment_counter!
|
def increment_counter!
|
37
vendor/plugins/custom_fields/lib/custom_fields/proxy_class_enabler.rb
vendored
Normal file
37
vendor/plugins/custom_fields/lib/custom_fields/proxy_class_enabler.rb
vendored
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
module CustomFields
|
||||||
|
module ProxyClassEnabler
|
||||||
|
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
included do
|
||||||
|
|
||||||
|
cattr_accessor :klass_with_custom_fields
|
||||||
|
|
||||||
|
def self.to_klass_with_custom_fields(fields)
|
||||||
|
return klass_with_custom_fields unless klass_with_custom_fields.nil?
|
||||||
|
|
||||||
|
klass = Class.new(self)
|
||||||
|
klass.class_eval <<-EOF
|
||||||
|
cattr_accessor :custom_fields
|
||||||
|
|
||||||
|
def self.model_name
|
||||||
|
@_model_name ||= ActiveModel::Name.new(self.superclass)
|
||||||
|
end
|
||||||
|
|
||||||
|
def custom_fields
|
||||||
|
self.class.custom_fields
|
||||||
|
end
|
||||||
|
EOF
|
||||||
|
|
||||||
|
klass.hereditary = false
|
||||||
|
klass.custom_fields = fields
|
||||||
|
|
||||||
|
[*fields].each { |field| field.apply(klass) }
|
||||||
|
|
||||||
|
klass_with_custom_fields = klass
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
52
vendor/plugins/custom_fields/lib/custom_fields/types/category.rb
vendored
Normal file
52
vendor/plugins/custom_fields/lib/custom_fields/types/category.rb
vendored
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
module CustomFields
|
||||||
|
module Types
|
||||||
|
module Category
|
||||||
|
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
included do
|
||||||
|
embeds_many :category_items, :class_name => 'CustomFields::Types::Category::Item'
|
||||||
|
end
|
||||||
|
|
||||||
|
module InstanceMethods
|
||||||
|
|
||||||
|
def ordered_category_items
|
||||||
|
self.category_items.sort { |a, b| (a.position || 0) <=> (b.position || 0) }
|
||||||
|
end
|
||||||
|
|
||||||
|
def apply_category_type(klass)
|
||||||
|
klass.cattr_accessor :"#{self.safe_alias}_items"
|
||||||
|
|
||||||
|
klass.send("#{self.safe_alias}_items=", self.category_items)
|
||||||
|
|
||||||
|
klass.class_eval <<-EOF
|
||||||
|
def self.#{self.safe_alias}_names
|
||||||
|
#{self.safe_alias}_items.collect(&:name)
|
||||||
|
end
|
||||||
|
|
||||||
|
def #{self.safe_alias}=(name)
|
||||||
|
category_id = self.class.#{self.safe_alias}_items.where(:name => name).first._id rescue nil
|
||||||
|
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
|
||||||
|
end
|
||||||
|
EOF
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
class Item
|
||||||
|
|
||||||
|
include Mongoid::Document
|
||||||
|
|
||||||
|
field :name
|
||||||
|
field :position, :type => Integer, :default => 0
|
||||||
|
|
||||||
|
embedded_in :custom_field, :inverse_of => :category_items
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
18
vendor/plugins/custom_fields/lib/custom_fields/types/default.rb
vendored
Normal file
18
vendor/plugins/custom_fields/lib/custom_fields/types/default.rb
vendored
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
module CustomFields
|
||||||
|
module Types
|
||||||
|
module Default
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
module InstanceMethods
|
||||||
|
|
||||||
|
def apply_default_type(klass)
|
||||||
|
klass.class_eval <<-EOF
|
||||||
|
alias :#{self.safe_alias} :#{self._name}
|
||||||
|
alias :#{self.safe_alias}= :#{self._name}=
|
||||||
|
EOF
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
10
vendor/plugins/custom_fields/spec/models/person.rb
vendored
Normal file
10
vendor/plugins/custom_fields/spec/models/person.rb
vendored
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
class Person
|
||||||
|
|
||||||
|
include Mongoid::Document
|
||||||
|
include CustomFields::ProxyClassEnabler
|
||||||
|
|
||||||
|
field :full_name
|
||||||
|
|
||||||
|
embedded_in :project, :inverse_of => :people
|
||||||
|
|
||||||
|
end
|
16
vendor/plugins/custom_fields/spec/models/project.rb
vendored
Normal file
16
vendor/plugins/custom_fields/spec/models/project.rb
vendored
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
class Project
|
||||||
|
|
||||||
|
include Mongoid::Document
|
||||||
|
include CustomFields::ProxyClassEnabler
|
||||||
|
include CustomFields::CustomFieldsFor
|
||||||
|
|
||||||
|
field :name
|
||||||
|
field :description
|
||||||
|
|
||||||
|
has_many_related :people
|
||||||
|
embeds_many :tasks
|
||||||
|
|
||||||
|
custom_fields_for :people
|
||||||
|
custom_fields_for :tasks
|
||||||
|
|
||||||
|
end
|
10
vendor/plugins/custom_fields/spec/models/task.rb
vendored
Normal file
10
vendor/plugins/custom_fields/spec/models/task.rb
vendored
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
class Task
|
||||||
|
|
||||||
|
include Mongoid::Document
|
||||||
|
include CustomFields::ProxyClassEnabler
|
||||||
|
|
||||||
|
field :title
|
||||||
|
|
||||||
|
embedded_in :project, :inverse_of => :tasks
|
||||||
|
|
||||||
|
end
|
30
vendor/plugins/custom_fields/spec/spec_helper.rb
vendored
Normal file
30
vendor/plugins/custom_fields/spec/spec_helper.rb
vendored
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
||||||
|
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), "..", "lib"))
|
||||||
|
|
||||||
|
MODELS = File.join(File.dirname(__FILE__), "models")
|
||||||
|
$LOAD_PATH.unshift(MODELS)
|
||||||
|
|
||||||
|
require 'rubygems'
|
||||||
|
require 'bundler'
|
||||||
|
Bundler.setup
|
||||||
|
Bundler.require
|
||||||
|
|
||||||
|
require 'mongoid'
|
||||||
|
require 'mocha'
|
||||||
|
require 'rspec'
|
||||||
|
require 'custom_fields'
|
||||||
|
|
||||||
|
Dir[ File.join(MODELS, "*.rb") ].sort.each { |file| require File.basename(file) }
|
||||||
|
|
||||||
|
Mongoid.configure do |config|
|
||||||
|
name = "custom_fields_test"
|
||||||
|
host = "localhost"
|
||||||
|
config.master = Mongo::Connection.new.db(name)
|
||||||
|
end
|
||||||
|
|
||||||
|
Rspec.configure do |config|
|
||||||
|
config.mock_with :mocha
|
||||||
|
config.after :suite do
|
||||||
|
Mongoid.master.collections.each(&:drop)
|
||||||
|
end
|
||||||
|
end
|
42
vendor/plugins/custom_fields/spec/unit/custom_field_spec.rb
vendored
Normal file
42
vendor/plugins/custom_fields/spec/unit/custom_field_spec.rb
vendored
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
describe CustomFields::Field do
|
||||||
|
|
||||||
|
it 'is initialized' do
|
||||||
|
lambda { CustomFields::Field.new }.should_not raise_error
|
||||||
|
end
|
||||||
|
|
||||||
|
context '#mongoid' do
|
||||||
|
|
||||||
|
before(:each) do
|
||||||
|
@field = CustomFields::Field.new(:label => 'manager', :_name => 'field_1', :kind => 'String', :_alias => 'manager')
|
||||||
|
@field.stubs(:valid?).returns(true)
|
||||||
|
@project = Project.to_klass_with_custom_fields(@field).new
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'is added to the list of mongoid fields' do
|
||||||
|
@project.fields['field_1'].should_not be_nil
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'on target class' do
|
||||||
|
|
||||||
|
before(:each) do
|
||||||
|
@field = CustomFields::Field.new(:label => 'manager', :_name => 'field_1', :kind => 'String', :_alias => 'manager')
|
||||||
|
@field.stubs(:valid?).returns(true)
|
||||||
|
@project = Project.to_klass_with_custom_fields(@field).new
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'has a new field' do
|
||||||
|
@project.respond_to?(:manager).should be_true
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'sets / retrieves a value' do
|
||||||
|
@project.manager = 'Mickael Scott'
|
||||||
|
@project.manager.should == 'Mickael Scott'
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
85
vendor/plugins/custom_fields/spec/unit/custom_fields_for_spec.rb
vendored
Normal file
85
vendor/plugins/custom_fields/spec/unit/custom_fields_for_spec.rb
vendored
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
describe CustomFields::CustomFieldsFor do
|
||||||
|
|
||||||
|
context 'with embedded collection' do
|
||||||
|
|
||||||
|
context '#association' do
|
||||||
|
|
||||||
|
before(:each) do
|
||||||
|
@project = Project.new
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'has custom fields for embedded collection' do
|
||||||
|
@project.respond_to?(:task_custom_fields).should be_true
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
context '#building' do
|
||||||
|
|
||||||
|
before(:each) do
|
||||||
|
@project = Project.new
|
||||||
|
@project.task_custom_fields.build :label => 'Short summary', :_alias => 'summary', :kind => 'String'
|
||||||
|
@task = @project.tasks.build
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns a new document whose Class is different from the original one' do
|
||||||
|
@task.class.should_not == Task
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns a new document with custom field' do
|
||||||
|
@project.tasks.build
|
||||||
|
@project.tasks.build
|
||||||
|
@task.respond_to?(:summary).should be_true
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'sets/gets custom attributes' do
|
||||||
|
@task.summary = 'Lorem ipsum...'
|
||||||
|
@task.summary.should == 'Lorem ipsum...'
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with related collection' do
|
||||||
|
|
||||||
|
context '#association' do
|
||||||
|
|
||||||
|
before(:each) do
|
||||||
|
@project = Project.new
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'has custom fields for related collections' do
|
||||||
|
@project.respond_to?(:person_custom_fields).should be_true
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
context '#building' do
|
||||||
|
|
||||||
|
before(:each) do
|
||||||
|
@project = Project.new
|
||||||
|
@project.person_custom_fields.build :label => 'Position in the project', :_alias => 'position', :kind => 'String'
|
||||||
|
@person = @project.people.build
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns a new document whose Class is different from the original one' do
|
||||||
|
@person.class.should_not == Task
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns a new document with custom field' do
|
||||||
|
@person.respond_to?(:position).should be_true
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'sets/gets custom attributes' do
|
||||||
|
@person.position = 'Designer'
|
||||||
|
@person.position.should == 'Designer'
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
25
vendor/plugins/custom_fields/spec/unit/proxy_class_enabler_spec.rb
vendored
Normal file
25
vendor/plugins/custom_fields/spec/unit/proxy_class_enabler_spec.rb
vendored
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
describe CustomFields::ProxyClassEnabler do
|
||||||
|
|
||||||
|
context '#proxy klass' do
|
||||||
|
|
||||||
|
before(:each) do
|
||||||
|
@klass = Task.to_klass_with_custom_fields([])
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does not be flagged as a inherited document' do
|
||||||
|
@klass.new.hereditary?.should be_false
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'has a list of custom fields' do
|
||||||
|
@klass.custom_fields.should == []
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'has the exact same model name than its parent' do
|
||||||
|
@klass.model_name.should == 'Task'
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
58
vendor/plugins/custom_fields/spec/unit/types/category_spec.rb
vendored
Normal file
58
vendor/plugins/custom_fields/spec/unit/types/category_spec.rb
vendored
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
describe CustomFields::Types::Category do
|
||||||
|
|
||||||
|
context 'on field class' do
|
||||||
|
|
||||||
|
before(:each) do
|
||||||
|
@field = CustomFields::Field.new
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'has the category items field' do
|
||||||
|
@field.respond_to?(:category_items).should be_true
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'has the apply method used for the target object' do
|
||||||
|
@field.respond_to?(:apply_category_type).should be_true
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'on target class' do
|
||||||
|
|
||||||
|
before(:each) do
|
||||||
|
@project = build_project_with_category
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'has getter/setter' do
|
||||||
|
@project.respond_to?(:global_category).should be_true
|
||||||
|
@project.respond_to?(:global_category=).should be_true
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'has the values of this category' do
|
||||||
|
@project.class.global_category_names.should == %w{Development Design Maintenance}
|
||||||
|
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
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
def build_project_with_category
|
||||||
|
field = build_category
|
||||||
|
Project.to_klass_with_custom_fields(field).new
|
||||||
|
end
|
||||||
|
|
||||||
|
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
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
Loading…
Reference in New Issue
Block a user