custom_fields 1.1.0.rc1 → 2.0.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- data/MIT-LICENSE +1 -1
- data/README.textile +14 -6
- data/config/locales/fr.yml +5 -1
- data/lib/custom_fields.rb +11 -37
- data/lib/custom_fields/extensions/carrierwave.rb +25 -0
- data/lib/custom_fields/extensions/mongoid/document.rb +12 -50
- data/lib/custom_fields/extensions/mongoid/factory.rb +20 -0
- data/lib/custom_fields/extensions/mongoid/fields.rb +29 -0
- data/lib/custom_fields/extensions/mongoid/fields/i18n.rb +53 -0
- data/lib/custom_fields/extensions/mongoid/fields/internal/localized.rb +84 -0
- data/lib/custom_fields/extensions/mongoid/relations/referenced/many.rb +29 -0
- data/lib/custom_fields/field.rb +44 -175
- data/lib/custom_fields/source.rb +333 -0
- data/lib/custom_fields/target.rb +90 -0
- data/lib/custom_fields/types/boolean.rb +26 -3
- data/lib/custom_fields/types/date.rb +36 -24
- data/lib/custom_fields/types/default.rb +44 -24
- data/lib/custom_fields/types/file.rb +35 -17
- data/lib/custom_fields/types/select.rb +184 -0
- data/lib/custom_fields/types/string.rb +25 -6
- data/lib/custom_fields/types/text.rb +35 -8
- data/lib/custom_fields/types_old/boolean.rb +13 -0
- data/lib/custom_fields/{types → types_old}/category.rb +0 -0
- data/lib/custom_fields/types_old/date.rb +49 -0
- data/lib/custom_fields/types_old/default.rb +44 -0
- data/lib/custom_fields/types_old/file.rb +27 -0
- data/lib/custom_fields/{types → types_old}/has_many.rb +0 -0
- data/lib/custom_fields/{types → types_old}/has_many/proxy_collection.rb +0 -0
- data/lib/custom_fields/{types → types_old}/has_many/reverse_lookup_proxy_collection.rb +0 -0
- data/lib/custom_fields/{types → types_old}/has_one.rb +0 -0
- data/lib/custom_fields/types_old/string.rb +13 -0
- data/lib/custom_fields/types_old/text.rb +15 -0
- data/lib/custom_fields/version.rb +1 -1
- metadata +115 -91
- data/lib/custom_fields/custom_fields_for.rb +0 -350
- data/lib/custom_fields/extensions/mongoid/relations/accessors.rb +0 -31
- data/lib/custom_fields/extensions/mongoid/relations/builders.rb +0 -30
- data/lib/custom_fields/proxy_class/base.rb +0 -112
- data/lib/custom_fields/proxy_class/builder.rb +0 -60
- data/lib/custom_fields/proxy_class/helper.rb +0 -57
- data/lib/custom_fields/self_metadata.rb +0 -30
data/MIT-LICENSE
CHANGED
data/README.textile
CHANGED
@@ -1,7 +1,9 @@
|
|
1
|
+
"!https://secure.travis-ci.org/locomotivecms/custom_fields.png!":http://travis-ci.org/locomotivecms/custom_fields
|
2
|
+
|
1
3
|
h1. CustomFields
|
2
4
|
|
3
5
|
Manage custom fields to a mongoid document or a collection. This module is one of the core features we implemented in our custom cms named Locomotive.
|
4
|
-
Basically, its aim is to provide to editors a way to manage extra fields to a Mongoid document through for instance a web UI.
|
6
|
+
Basically, its aim is to provide to editors a way to manage extra fields to a Mongoid document through, for instance, a web UI.
|
5
7
|
|
6
8
|
The main goals:
|
7
9
|
|
@@ -10,26 +12,30 @@ The main goals:
|
|
10
12
|
|
11
13
|
h2. Requirements
|
12
14
|
|
13
|
-
ActiveSupport 3.1.
|
15
|
+
ActiveSupport 3.1.3, MongoDB 2.0 and Mongoid 2.3.4
|
14
16
|
|
15
17
|
h2. Examples
|
16
18
|
|
17
19
|
h3. On a has_many relationship
|
18
20
|
|
19
21
|
bc.. class Company
|
20
|
-
|
22
|
+
include CustomFields::Source
|
23
|
+
|
24
|
+
has_many :employees
|
21
25
|
|
22
26
|
custom_fields_for :employees
|
23
27
|
end
|
24
28
|
|
25
29
|
class Employee
|
30
|
+
include CustomFields::Target
|
31
|
+
|
26
32
|
field :name, String
|
27
33
|
|
28
|
-
|
34
|
+
belongs_to :company, :inverse_of => :employees
|
29
35
|
end
|
30
36
|
|
31
37
|
company = Company.new
|
32
|
-
company.employees_custom_fields.build :label => 'His/her position', :
|
38
|
+
company.employees_custom_fields.build :label => 'His/her position', :name => 'position', :type => 'string', :required => true
|
33
39
|
|
34
40
|
company.save
|
35
41
|
|
@@ -41,12 +47,14 @@ employee.position # returns a "not defined method" error
|
|
41
47
|
|
42
48
|
h3. On the class itself
|
43
49
|
|
50
|
+
[IN PROGRESS]
|
51
|
+
|
44
52
|
bc.. class Company
|
45
53
|
custom_fields_for_itself
|
46
54
|
end
|
47
55
|
|
48
56
|
company = Company.new
|
49
|
-
company.self_metadata_custom_fields.build :label => 'Shipping Address', :
|
57
|
+
company.self_metadata_custom_fields.build :label => 'Shipping Address', :name => 'address', :type => 'text'
|
50
58
|
|
51
59
|
company.save
|
52
60
|
|
data/config/locales/fr.yml
CHANGED
data/lib/custom_fields.rb
CHANGED
@@ -6,7 +6,7 @@ require 'carrierwave/mongoid'
|
|
6
6
|
module CustomFields
|
7
7
|
|
8
8
|
@@options = {
|
9
|
-
:
|
9
|
+
:reserved_names => Mongoid.destructive_fields + %w(id _id send class)
|
10
10
|
}
|
11
11
|
|
12
12
|
def self.options=(options)
|
@@ -20,49 +20,23 @@ module CustomFields
|
|
20
20
|
end
|
21
21
|
|
22
22
|
require 'custom_fields/version'
|
23
|
-
require 'custom_fields/extensions/
|
23
|
+
require 'custom_fields/extensions/carrierwave'
|
24
24
|
require 'custom_fields/extensions/mongoid/document'
|
25
|
-
require 'custom_fields/extensions/mongoid/
|
26
|
-
require 'custom_fields/extensions/mongoid/relations/
|
25
|
+
require 'custom_fields/extensions/mongoid/factory'
|
26
|
+
require 'custom_fields/extensions/mongoid/relations/referenced/many'
|
27
|
+
require 'custom_fields/extensions/mongoid/fields.rb'
|
28
|
+
require 'custom_fields/extensions/mongoid/fields/i18n.rb'
|
29
|
+
require 'custom_fields/extensions/mongoid/fields/internal/localized.rb'
|
27
30
|
require 'custom_fields/types/default'
|
28
31
|
require 'custom_fields/types/string'
|
29
32
|
require 'custom_fields/types/text'
|
30
|
-
require 'custom_fields/types/category'
|
31
|
-
require 'custom_fields/types/boolean'
|
32
33
|
require 'custom_fields/types/date'
|
34
|
+
require 'custom_fields/types/boolean'
|
33
35
|
require 'custom_fields/types/file'
|
34
|
-
require 'custom_fields/types/
|
35
|
-
require 'custom_fields/types/has_many/proxy_collection'
|
36
|
-
require 'custom_fields/types/has_many/reverse_lookup_proxy_collection'
|
37
|
-
require 'custom_fields/types/has_many'
|
38
|
-
require 'custom_fields/proxy_class/base'
|
39
|
-
require 'custom_fields/proxy_class/builder'
|
40
|
-
require 'custom_fields/proxy_class/helper'
|
36
|
+
require 'custom_fields/types/select'
|
41
37
|
require 'custom_fields/field'
|
42
|
-
require 'custom_fields/
|
43
|
-
require 'custom_fields/
|
44
|
-
|
45
|
-
module Mongoid
|
46
|
-
module CustomFields
|
47
|
-
extend ActiveSupport::Concern
|
48
|
-
included do
|
49
|
-
extend ::CustomFields::ProxyClass::Helper
|
50
|
-
include ::CustomFields::CustomFieldsFor
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
module TargetCustomFields
|
55
|
-
extend ActiveSupport::Concern
|
56
|
-
included do
|
57
|
-
extend ::CustomFields::ProxyClass::Helper
|
58
|
-
extend ::CustomFields::ProxyClass::Builder
|
59
|
-
end
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
|
-
ActiveSupport::Inflector.inflections do |inflect|
|
64
|
-
inflect.irregular 'metadata', 'metadata'
|
65
|
-
end
|
38
|
+
require 'custom_fields/source'
|
39
|
+
require 'custom_fields/target'
|
66
40
|
|
67
41
|
# Load all the translation files
|
68
42
|
I18n.load_path += Dir[File.join(File.dirname(__FILE__), '..', 'config', 'locales', '*.yml')]
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'carrierwave/mongoid'
|
2
|
+
|
3
|
+
module CarrierWave
|
4
|
+
|
5
|
+
module Mongoid
|
6
|
+
|
7
|
+
def mount_uploader_with_localization(column, uploader=nil, options={}, &block)
|
8
|
+
mount_uploader_without_localization(column, uploader, options, &block)
|
9
|
+
|
10
|
+
define_method(:read_uploader) do |name|
|
11
|
+
# puts "read_uploader #{name} / #{read_attribute(name.to_sym).inspect}" # DEBUG
|
12
|
+
|
13
|
+
value = read_attribute(name.to_sym)
|
14
|
+
unless value.nil?
|
15
|
+
self.class.fields[name.to_s].deserialize(value)
|
16
|
+
else
|
17
|
+
nil
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
alias_method_chain :mount_uploader, :localization
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
@@ -1,59 +1,21 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
module Mongoid #:nodoc:
|
3
3
|
|
4
|
-
|
5
|
-
# # the database as documents.
|
6
|
-
# module Document
|
7
|
-
#
|
8
|
-
# # Reloads the +Document+ attributes from the database. If the document has
|
9
|
-
# # not been saved then an error will get raised if the configuration option
|
10
|
-
# # was set.
|
11
|
-
# #
|
12
|
-
# # @example Reload the document.
|
13
|
-
# # person.reload
|
14
|
-
# #
|
15
|
-
# # @raise [ Errors::DocumentNotFound ] If the document was deleted.
|
16
|
-
# #
|
17
|
-
# # @return [ Document ] The document, reloaded.
|
18
|
-
# def reload_with_custom_fields
|
19
|
-
# reload_without_custom_fields.tap do
|
20
|
-
# instance_variable_names.each do |name|
|
21
|
-
# if name =~ /_proxy_class$/
|
22
|
-
# remove_instance_variable("#{name}")
|
23
|
-
# end
|
24
|
-
# end
|
25
|
-
# end
|
26
|
-
# end
|
27
|
-
#
|
28
|
-
# alias_method_chain :reload, :custom_fields
|
29
|
-
#
|
30
|
-
# end
|
4
|
+
module Document #:nodoc:
|
31
5
|
|
32
|
-
|
33
|
-
# the database as documents.
|
34
|
-
module Reloading
|
6
|
+
module ClassMethods #:nodoc:
|
35
7
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
# @raise [ Errors::DocumentNotFound ] If the document was deleted.
|
44
|
-
#
|
45
|
-
# @return [ Document ] The document, reloaded.
|
46
|
-
def reload_with_custom_fields
|
47
|
-
reload_without_custom_fields.tap do
|
48
|
-
instance_variable_names.each do |name|
|
49
|
-
if name =~ /_proxy_class$/
|
50
|
-
remove_instance_variable("#{name}")
|
51
|
-
end
|
52
|
-
end
|
8
|
+
# The mongoid default document returns always false.
|
9
|
+
# The documents with custom fields return true.
|
10
|
+
#
|
11
|
+
# @return [ Boolean ] False
|
12
|
+
#
|
13
|
+
def with_custom_fields?
|
14
|
+
false
|
53
15
|
end
|
54
|
-
end
|
55
16
|
|
56
|
-
|
17
|
+
end
|
57
18
|
|
58
19
|
end
|
59
|
-
|
20
|
+
|
21
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module Mongoid #:nodoc:
|
3
|
+
|
4
|
+
# Instantiates documents that came from the database.
|
5
|
+
module Factory
|
6
|
+
|
7
|
+
def from_db_with_custom_fields(klass, attributes = {})
|
8
|
+
if klass.with_custom_fields?
|
9
|
+
klass.klass_with_custom_fields(attributes['custom_fields_recipe'])
|
10
|
+
end
|
11
|
+
from_db_without_custom_fields(klass, attributes)
|
12
|
+
end
|
13
|
+
|
14
|
+
# equivalent for "alias_method_chain :from_db, :custom_fields"
|
15
|
+
alias_method :from_db_without_custom_fields, :from_db unless method_defined?(:from_db_without_custom_fields)
|
16
|
+
alias_method :from_db, :from_db_with_custom_fields
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Mongoid #:nodoc
|
2
|
+
|
3
|
+
# This module defines behaviour for fields.
|
4
|
+
module Fields
|
5
|
+
|
6
|
+
module ClassMethods
|
7
|
+
|
8
|
+
# Replace a field with a new type.
|
9
|
+
#
|
10
|
+
# @example Replace the field.
|
11
|
+
# Model.replace_field("_id", String)
|
12
|
+
#
|
13
|
+
# @param [ String ] name The name of the field.
|
14
|
+
# @param [ Class ] type The new type of field.
|
15
|
+
# @param [ Boolean ] localize The option to localize or not the field.
|
16
|
+
#
|
17
|
+
# @return [ Serializable ] The new field.
|
18
|
+
#
|
19
|
+
# @since 2.1.0
|
20
|
+
def replace_field(name, type, localize = false)
|
21
|
+
defaults.delete_one(name)
|
22
|
+
add_field(name, fields[name].options.merge(:type => type, :localize => localize))
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Mongoid #:nodoc
|
2
|
+
|
3
|
+
# This module defines behaviour for fields.
|
4
|
+
module Fields
|
5
|
+
|
6
|
+
class I18n
|
7
|
+
|
8
|
+
include Singleton
|
9
|
+
|
10
|
+
attr_accessor :locale, :fallbacks
|
11
|
+
|
12
|
+
def self.locale
|
13
|
+
self.instance.locale || ::I18n.locale
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.locale=(value)
|
17
|
+
self.instance.locale = value.to_sym rescue nil
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.fallbacks
|
21
|
+
if ::I18n.respond_to?(:fallbacks)
|
22
|
+
::I18n.fallbacks
|
23
|
+
elsif !self.instance.fallbacks.blank?
|
24
|
+
self.instance.fallbacks
|
25
|
+
else
|
26
|
+
nil
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.fallbacks_for(locale, fallbacks)
|
31
|
+
self.instance.fallbacks ||= {}
|
32
|
+
self.instance.fallbacks[locale.to_sym] = fallbacks
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.fallbacks?
|
36
|
+
::I18n.respond_to?(:fallbacks) || !self.instance.fallbacks.blank?
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.with_locale(new_locale = nil)
|
40
|
+
if tmp_locale
|
41
|
+
current_locale = self.locale
|
42
|
+
self.locale = new_locale
|
43
|
+
end
|
44
|
+
yield
|
45
|
+
ensure
|
46
|
+
self.locale = current_locale if new_locale
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module Mongoid #:nodoc:
|
3
|
+
module Fields #:nodoc:
|
4
|
+
module Internal #:nodoc:
|
5
|
+
|
6
|
+
# Defines the behaviour for localized string fields.
|
7
|
+
class Localized
|
8
|
+
|
9
|
+
attr_accessor :original_field_type
|
10
|
+
|
11
|
+
class << self
|
12
|
+
|
13
|
+
# Instantiate 2 field types:
|
14
|
+
# - a wrapper in charge of dealing with the translations
|
15
|
+
# - the original field type to help to serialize / deserialize types
|
16
|
+
#
|
17
|
+
# @param [ Hash ] options The field options.
|
18
|
+
#
|
19
|
+
# @option options [ Class ] :type The class of the field.
|
20
|
+
# @option options [ Object ] :default The default value for the field.
|
21
|
+
# @option options [ String ] :label The field's label.
|
22
|
+
#
|
23
|
+
def instantiate_with_localize(name, options = {})
|
24
|
+
instantiate_without_localize(name, options).tap do |field|
|
25
|
+
field.original_field_type = Mappings.for(options[:type], options[:identity]).instantiate(name, options)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
alias_method_chain :instantiate, :localize
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
# Deserialize the object based on the current locale. Will look in the
|
34
|
+
# hash for the current locale.
|
35
|
+
#
|
36
|
+
# @example Get the deserialized value.
|
37
|
+
# field.deserialize({ "en" => "testing" })
|
38
|
+
#
|
39
|
+
# @param [ Hash ] object The hash of translations.
|
40
|
+
#
|
41
|
+
# @return [ String ] The value for the current locale.
|
42
|
+
#
|
43
|
+
# @since 2.3.0
|
44
|
+
def deserialize(object)
|
45
|
+
return nil if object.nil?
|
46
|
+
|
47
|
+
# puts "deserializing...#{locale.inspect} / #{object.inspect}" # DEBUG
|
48
|
+
|
49
|
+
value = if I18n.fallbacks?
|
50
|
+
object[I18n.fallbacks[locale.to_sym].map(&:to_s).find { |loc| !object[loc].nil? }]
|
51
|
+
else
|
52
|
+
object[locale.to_s]
|
53
|
+
end
|
54
|
+
|
55
|
+
self.original_field_type.deserialize(value)
|
56
|
+
end
|
57
|
+
|
58
|
+
# Convert the provided string into a hash for the locale.
|
59
|
+
#
|
60
|
+
# @example Serialize the value.
|
61
|
+
# field.serialize("testing")
|
62
|
+
#
|
63
|
+
# @param [ String ] object The string to convert.
|
64
|
+
#
|
65
|
+
# @return [ Hash ] The locale with string translation.
|
66
|
+
#
|
67
|
+
# @since 2.3.0
|
68
|
+
def serialize(object)
|
69
|
+
# puts "serializing...#{locale} / #{object.inspect} / #{options.inspect}" # DEBUG
|
70
|
+
|
71
|
+
value = self.original_field_type.serialize(object)
|
72
|
+
{ locale.to_s => value }
|
73
|
+
end
|
74
|
+
|
75
|
+
protected
|
76
|
+
|
77
|
+
def locale
|
78
|
+
I18n.locale
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module Mongoid #:nodoc:
|
3
|
+
module Relations #:nodoc:
|
4
|
+
module Referenced #:nodoc:
|
5
|
+
|
6
|
+
# This class defines the behaviour for all relations that are a
|
7
|
+
# one-to-many between documents in different collections.
|
8
|
+
class Many < Relations::Many
|
9
|
+
|
10
|
+
def build_with_custom_fields(attributes = {}, options = {}, type = nil)
|
11
|
+
if base.respond_to?(:custom_fields_for?) && base.custom_fields_for?(metadata.name)
|
12
|
+
# all the information about how to build the custom class are stored here
|
13
|
+
recipe = base.custom_fields_recipe_for(metadata.name)
|
14
|
+
|
15
|
+
attributes.merge!(:custom_fields_recipe => recipe)
|
16
|
+
|
17
|
+
# build the class with custom_fields for the first time
|
18
|
+
type = metadata.klass.klass_with_custom_fields(recipe)
|
19
|
+
end
|
20
|
+
|
21
|
+
build_without_custom_fields(attributes, options, type)
|
22
|
+
end
|
23
|
+
|
24
|
+
alias_method_chain :build, :custom_fields
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|