custom_fields 1.1.0.rc1 → 2.0.0.rc1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|