custom_fields 1.0.0.beta.21 → 1.0.0.beta.22
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/lib/custom_fields/proxy_class_enabler.rb +8 -0
- data/lib/custom_fields/types/has_many/proxy_collection.rb +92 -0
- data/lib/custom_fields/types/has_many/reverse_lookup_proxy_collection.rb +93 -0
- data/lib/custom_fields/types/has_many.rb +54 -73
- data/lib/custom_fields/types/has_one.rb +3 -0
- data/lib/custom_fields/version.rb +1 -1
- data/lib/custom_fields.rb +2 -0
- metadata +5 -3
|
@@ -83,6 +83,14 @@ module CustomFields
|
|
|
83
83
|
self.custom_fields.detect { |f| f._name == name }
|
|
84
84
|
end
|
|
85
85
|
|
|
86
|
+
def self.custom_field_alias_to_name(value)
|
|
87
|
+
self.custom_fields.detect { |f| f._alias == value }._name
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def self.custom_field_name_to_alias(value)
|
|
91
|
+
self.custom_fields.detect { |f| f._name == value }._alias
|
|
92
|
+
end
|
|
93
|
+
|
|
86
94
|
def self.hereditary?
|
|
87
95
|
false
|
|
88
96
|
end
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
module CustomFields
|
|
2
|
+
module Types
|
|
3
|
+
module HasMany
|
|
4
|
+
|
|
5
|
+
class ProxyCollection
|
|
6
|
+
|
|
7
|
+
attr_accessor :parent, :target_klass, :field_name, :ids, :values
|
|
8
|
+
|
|
9
|
+
def initialize(parent, target_klass, field_name, options = {})
|
|
10
|
+
self.parent = parent
|
|
11
|
+
|
|
12
|
+
self.target_klass = target_klass
|
|
13
|
+
|
|
14
|
+
self.field_name = field_name
|
|
15
|
+
|
|
16
|
+
self.ids, self.values = [], []
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def find(id)
|
|
20
|
+
id = BSON::ObjectId(id) unless id.is_a?(BSON::ObjectId)
|
|
21
|
+
self.values.detect { |obj_id| obj_id == id }
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def update(values)
|
|
25
|
+
values = [] if values.blank? || self.target_klass.nil?
|
|
26
|
+
|
|
27
|
+
self.ids = values.collect { |obj| self.id_for_sure(obj) }.compact
|
|
28
|
+
self.values = values.collect { |obj| self.object_for_sure(obj) }.compact
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# just before the parent gets saved, reflect the changes to the parent object
|
|
32
|
+
def store_values
|
|
33
|
+
self.parent.write_attribute(self.field_name, self.ids)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# once the parent object gets saved, call this method, kind of hook or callback
|
|
37
|
+
def persist
|
|
38
|
+
true
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def <<(*args)
|
|
42
|
+
args.flatten.compact.each do |obj|
|
|
43
|
+
self.ids << self.id_for_sure(obj)
|
|
44
|
+
self.values << self.object_for_sure(obj)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
alias :push :<<
|
|
49
|
+
|
|
50
|
+
def size
|
|
51
|
+
self.values.size
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
alias :length :size
|
|
55
|
+
|
|
56
|
+
def method_missing(name, *args, &block)
|
|
57
|
+
self.values.send(name, *args, &block)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
protected
|
|
61
|
+
|
|
62
|
+
def id_for_sure(id_or_object)
|
|
63
|
+
id_or_object.respond_to?(:_id) ? id_or_object._id : id_or_object
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def object_for_sure(id_or_object)
|
|
67
|
+
if id_or_object.respond_to?(:_id)
|
|
68
|
+
id_or_object
|
|
69
|
+
else
|
|
70
|
+
self.collection.find(id_or_object)
|
|
71
|
+
end
|
|
72
|
+
rescue # target_klass does not exist anymore or the target element has been removed since
|
|
73
|
+
nil
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def collection(reload_embedded = false)
|
|
77
|
+
if self.target_klass.embedded?
|
|
78
|
+
parent_target_klass = self.target_klass._parent
|
|
79
|
+
|
|
80
|
+
parent_target_klass = parent_target_klass.reload if reload_embedded
|
|
81
|
+
|
|
82
|
+
parent_target_klass.send(self.target_klass.association_name)
|
|
83
|
+
else
|
|
84
|
+
self.target_klass
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
module CustomFields
|
|
2
|
+
module Types
|
|
3
|
+
module HasMany
|
|
4
|
+
|
|
5
|
+
class ReverseLookupProxyCollection < ProxyCollection
|
|
6
|
+
|
|
7
|
+
attr_accessor :reverse_lookup_field, :previous_state
|
|
8
|
+
|
|
9
|
+
def initialize(parent, target_klass, field_name, options = {})
|
|
10
|
+
super
|
|
11
|
+
|
|
12
|
+
self.reverse_lookup_field = options[:reverse_lookup_field].to_sym
|
|
13
|
+
|
|
14
|
+
if self.parent.new_record?
|
|
15
|
+
self.previous_state = { :ids => [], :values => [] }
|
|
16
|
+
else
|
|
17
|
+
self.reload
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def store_values
|
|
22
|
+
true # do nothing
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def persist
|
|
26
|
+
(self.previous_state[:ids] - self.ids).each do |id|
|
|
27
|
+
self.set_foreign_key_and_position(id, nil)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
self.ids.each_with_index do |id, position|
|
|
31
|
+
self.set_foreign_key_and_position(id, self.parent._id, position)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
if self.target_klass.embedded?
|
|
35
|
+
self.target_klass._parent.save!(:validate => false)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
self.reset_previous_state
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def <<(id_or_object)
|
|
42
|
+
object = self.object_for_sure(id_or_object)
|
|
43
|
+
|
|
44
|
+
foreign_key = object.send(self.reverse_lookup_field)
|
|
45
|
+
|
|
46
|
+
if foreign_key && foreign_key != self.parent._id
|
|
47
|
+
raise ArgumentError, "Object #{object} cannot be added: already has a different foreign key"
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
self.ids << self.id_for_sure(object._id)
|
|
51
|
+
self.values << self.object_for_sure(object)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def reload
|
|
55
|
+
self.update(self.reverse_collection)
|
|
56
|
+
|
|
57
|
+
self.reset_previous_state
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
protected
|
|
61
|
+
|
|
62
|
+
def reverse_collection
|
|
63
|
+
self.collection.where(self.reverse_lookup_field => self.parent._id).order_by([[:"#{self.reverse_lookup_field}_position", :asc]])
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def set_foreign_key_and_position(object_id, value, position = nil)
|
|
67
|
+
if self.target_klass.embedded?
|
|
68
|
+
object = self.collection.find(object_id)
|
|
69
|
+
else
|
|
70
|
+
object = self.previous_state[:values].detect { |o| o._id == object_id }
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
object.send("#{self.reverse_lookup_field}=".to_sym, value)
|
|
74
|
+
|
|
75
|
+
object.send("#{self.reverse_lookup_field}_position=".to_sym, position)
|
|
76
|
+
|
|
77
|
+
if self.target_klass.embedded?
|
|
78
|
+
object.save(:validate => false)
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def reset_previous_state
|
|
83
|
+
self.previous_state = {
|
|
84
|
+
:ids => self.ids.clone,
|
|
85
|
+
:values => self.values.clone
|
|
86
|
+
}
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
@@ -6,6 +6,7 @@ module CustomFields
|
|
|
6
6
|
|
|
7
7
|
included do
|
|
8
8
|
field :target
|
|
9
|
+
field :reverse_lookup
|
|
9
10
|
|
|
10
11
|
validates_presence_of :target, :if => :has_many?
|
|
11
12
|
|
|
@@ -14,21 +15,43 @@ module CustomFields
|
|
|
14
15
|
|
|
15
16
|
module InstanceMethods
|
|
16
17
|
|
|
18
|
+
def target_klass
|
|
19
|
+
self.target.constantize rescue nil
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def reverse_has_many?
|
|
23
|
+
self.reverse_lookup && !self.reverse_lookup.strip.blank?
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def safe_reverse_lookup
|
|
27
|
+
if self.reverse_lookup =~ /^custom_field_[0-9]+$/
|
|
28
|
+
self.reverse_lookup
|
|
29
|
+
else
|
|
30
|
+
self.target_klass.custom_field_alias_to_name(self.reverse_lookup)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def reverse_lookup_alias
|
|
35
|
+
self.target_klass.custom_field_name_to_alias(self.reverse_lookup)
|
|
36
|
+
end
|
|
37
|
+
|
|
17
38
|
def apply_has_many_type(klass)
|
|
18
39
|
klass.class_eval <<-EOF
|
|
19
40
|
|
|
20
41
|
before_validation :store_#{self.safe_alias.singularize}_ids
|
|
21
42
|
|
|
43
|
+
after_save :persist_#{self.safe_alias}
|
|
44
|
+
|
|
22
45
|
def #{self.safe_alias}=(ids_or_objects)
|
|
23
|
-
|
|
24
|
-
@_#{self._name} = ProxyCollection.new('#{self.target.to_s}', ids_or_objects)
|
|
25
|
-
else
|
|
26
|
-
@_#{self._name}.update(ids_or_objects)
|
|
27
|
-
end
|
|
46
|
+
self.#{self.safe_alias}.update(ids_or_objects)
|
|
28
47
|
end
|
|
29
48
|
|
|
30
49
|
def #{self.safe_alias}
|
|
31
|
-
@_#{self._name} ||=
|
|
50
|
+
@_#{self._name} ||= build_#{self.safe_alias.singularize}_proxy_collection
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def #{self.safe_alias}_klass
|
|
54
|
+
'#{self.target.to_s}'.constantize rescue nil
|
|
32
55
|
end
|
|
33
56
|
|
|
34
57
|
def #{self.safe_alias.singularize}_ids
|
|
@@ -36,84 +59,42 @@ module CustomFields
|
|
|
36
59
|
end
|
|
37
60
|
|
|
38
61
|
def store_#{self.safe_alias.singularize}_ids
|
|
39
|
-
|
|
62
|
+
self.#{self.safe_alias}.store_values
|
|
40
63
|
end
|
|
41
|
-
EOF
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
def add_has_many_validation(klass)
|
|
45
|
-
if self.required?
|
|
46
|
-
klass.validates_length_of self.safe_alias.to_sym, :minimum => 1, :too_short => :blank
|
|
47
|
-
end
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
class ProxyCollection
|
|
53
|
-
|
|
54
|
-
attr_accessor :target_klass, :ids, :values
|
|
55
64
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
array = [] if self.target_klass.nil?
|
|
60
|
-
|
|
61
|
-
self.update(array || [])
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
def find(id)
|
|
65
|
-
id = BSON::ObjectId(id) unless id.is_a?(BSON::ObjectId)
|
|
66
|
-
self.values.detect { |obj_id| obj_id == id }
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
def update(values)
|
|
70
|
-
values = [] if values.blank?
|
|
65
|
+
def persist_#{self.safe_alias}
|
|
66
|
+
self.#{self.safe_alias}.persist
|
|
67
|
+
end
|
|
71
68
|
|
|
72
|
-
|
|
73
|
-
self.values = values.collect { |obj| self.object_for_sure(obj) }.compact
|
|
74
|
-
end
|
|
69
|
+
EOF
|
|
75
70
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
71
|
+
if reverse_has_many?
|
|
72
|
+
klass.class_eval <<-EOF
|
|
73
|
+
def build_#{self.safe_alias.singularize}_proxy_collection
|
|
74
|
+
::CustomFields::Types::HasMany::ReverseLookupProxyCollection.new(self, self.#{self.safe_alias}_klass, '#{self._name}', {
|
|
75
|
+
:reverse_lookup_field => '#{self.safe_reverse_lookup}'
|
|
76
|
+
})
|
|
77
|
+
end
|
|
78
|
+
EOF
|
|
79
|
+
else
|
|
80
|
+
klass.class_eval <<-EOF
|
|
81
|
+
def build_#{self.safe_alias.singularize}_proxy_collection
|
|
82
|
+
::CustomFields::Types::HasMany::ProxyCollection.new(self, self.#{self.safe_alias}_klass, '#{self._name}').tap do |collection|
|
|
83
|
+
collection.update(self.#{self._name})
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
EOF
|
|
80
87
|
end
|
|
81
88
|
end
|
|
82
89
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
self.values.size
|
|
87
|
-
end
|
|
88
|
-
|
|
89
|
-
alias :length :size
|
|
90
|
-
|
|
91
|
-
def method_missing(name, *args, &block)
|
|
92
|
-
self.values.send(name, *args, &block)
|
|
93
|
-
end
|
|
94
|
-
|
|
95
|
-
protected
|
|
96
|
-
|
|
97
|
-
def id_for_sure(id_or_object)
|
|
98
|
-
id_or_object.respond_to?(:_id) ? id_or_object._id : id_or_object
|
|
99
|
-
end
|
|
100
|
-
|
|
101
|
-
def object_for_sure(id_or_object)
|
|
102
|
-
if id_or_object.respond_to?(:_id)
|
|
103
|
-
id_or_object
|
|
104
|
-
else
|
|
105
|
-
if self.target_klass.embedded?
|
|
106
|
-
self.target_klass._parent.reload.send(self.target_klass.association_name).find(id_or_object)
|
|
107
|
-
else
|
|
108
|
-
self.target_klass.find(id_or_object)
|
|
109
|
-
end
|
|
90
|
+
def add_has_many_validation(klass)
|
|
91
|
+
if self.required?
|
|
92
|
+
klass.validates_length_of self.safe_alias.to_sym, :minimum => 1, :too_short => :blank
|
|
110
93
|
end
|
|
111
|
-
rescue # target_klass does not exist anymore or the target element has been removed since
|
|
112
|
-
nil
|
|
113
94
|
end
|
|
114
95
|
|
|
115
96
|
end
|
|
116
97
|
|
|
117
98
|
end
|
|
118
99
|
end
|
|
119
|
-
end
|
|
100
|
+
end
|
|
@@ -15,6 +15,9 @@ module CustomFields
|
|
|
15
15
|
module InstanceMethods
|
|
16
16
|
|
|
17
17
|
def apply_has_one_type(klass)
|
|
18
|
+
|
|
19
|
+
klass.field :"#{self._name}_position", :type => Integer, :default => 0 # needed by the has_many reverse
|
|
20
|
+
|
|
18
21
|
klass.class_eval <<-EOF
|
|
19
22
|
|
|
20
23
|
def #{self.safe_alias}=(id_or_object)
|
data/lib/custom_fields.rb
CHANGED
|
@@ -31,6 +31,8 @@ require 'custom_fields/types/boolean'
|
|
|
31
31
|
require 'custom_fields/types/date'
|
|
32
32
|
require 'custom_fields/types/file'
|
|
33
33
|
require 'custom_fields/types/has_one'
|
|
34
|
+
require 'custom_fields/types/has_many/proxy_collection'
|
|
35
|
+
require 'custom_fields/types/has_many/reverse_lookup_proxy_collection'
|
|
34
36
|
require 'custom_fields/types/has_many'
|
|
35
37
|
require 'custom_fields/proxy_class_enabler'
|
|
36
38
|
require 'custom_fields/field'
|
metadata
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name: custom_fields
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
4
|
prerelease: 6
|
|
5
|
-
version: 1.0.0.beta.
|
|
5
|
+
version: 1.0.0.beta.22
|
|
6
6
|
platform: ruby
|
|
7
7
|
authors:
|
|
8
8
|
- Didier Lafforgue
|
|
@@ -10,7 +10,7 @@ autorequire:
|
|
|
10
10
|
bindir: bin
|
|
11
11
|
cert_chain: []
|
|
12
12
|
|
|
13
|
-
date: 2011-
|
|
13
|
+
date: 2011-08-11 00:00:00 -07:00
|
|
14
14
|
default_executable:
|
|
15
15
|
dependencies:
|
|
16
16
|
- !ruby/object:Gem::Dependency
|
|
@@ -61,6 +61,8 @@ files:
|
|
|
61
61
|
- lib/custom_fields/types/date.rb
|
|
62
62
|
- lib/custom_fields/types/default.rb
|
|
63
63
|
- lib/custom_fields/types/file.rb
|
|
64
|
+
- lib/custom_fields/types/has_many/proxy_collection.rb
|
|
65
|
+
- lib/custom_fields/types/has_many/reverse_lookup_proxy_collection.rb
|
|
64
66
|
- lib/custom_fields/types/has_many.rb
|
|
65
67
|
- lib/custom_fields/types/has_one.rb
|
|
66
68
|
- lib/custom_fields/types/string.rb
|
|
@@ -85,7 +87,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
85
87
|
requirements:
|
|
86
88
|
- - ">="
|
|
87
89
|
- !ruby/object:Gem::Version
|
|
88
|
-
hash: -
|
|
90
|
+
hash: -2478047520408348975
|
|
89
91
|
segments:
|
|
90
92
|
- 0
|
|
91
93
|
version: "0"
|