neo4j 4.0.0 → 4.1.0
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.
- checksums.yaml +4 -4
- data/CHANGELOG +10 -0
- data/Gemfile +6 -16
- data/README.md +5 -2
- data/bin/neo4j-jars +6 -6
- data/lib/neo4j.rb +13 -9
- data/lib/neo4j/active_node.rb +9 -9
- data/lib/neo4j/active_node/dependent.rb +11 -0
- data/lib/neo4j/active_node/dependent/association_methods.rb +28 -0
- data/lib/neo4j/active_node/dependent/query_proxy_methods.rb +48 -0
- data/lib/neo4j/active_node/has_n.rb +124 -112
- data/lib/neo4j/active_node/has_n/association.rb +45 -30
- data/lib/neo4j/active_node/id_property.rb +22 -19
- data/lib/neo4j/active_node/initialize.rb +2 -4
- data/lib/neo4j/active_node/labels.rb +23 -22
- data/lib/neo4j/active_node/node_wrapper.rb +5 -8
- data/lib/neo4j/active_node/orm_adapter.rb +2 -4
- data/lib/neo4j/active_node/persistence.rb +5 -10
- data/lib/neo4j/active_node/property.rb +3 -4
- data/lib/neo4j/active_node/query.rb +27 -6
- data/lib/neo4j/active_node/query/query_proxy.rb +65 -110
- data/lib/neo4j/active_node/query/query_proxy_enumerable.rb +67 -0
- data/lib/neo4j/active_node/query/query_proxy_find_in_batches.rb +0 -1
- data/lib/neo4j/active_node/query/query_proxy_methods.rb +29 -28
- data/lib/neo4j/active_node/query_methods.rb +6 -6
- data/lib/neo4j/active_node/reflection.rb +3 -2
- data/lib/neo4j/active_node/rels.rb +1 -1
- data/lib/neo4j/active_node/scope.rb +13 -8
- data/lib/neo4j/active_node/validations.rb +5 -6
- data/lib/neo4j/active_rel.rb +1 -2
- data/lib/neo4j/active_rel/callbacks.rb +3 -3
- data/lib/neo4j/active_rel/persistence.rb +9 -7
- data/lib/neo4j/active_rel/property.rb +12 -4
- data/lib/neo4j/active_rel/query.rb +6 -8
- data/lib/neo4j/active_rel/rel_wrapper.rb +0 -2
- data/lib/neo4j/active_rel/related_node.rb +4 -5
- data/lib/neo4j/active_rel/types.rb +4 -6
- data/lib/neo4j/active_rel/validations.rb +0 -1
- data/lib/neo4j/config.rb +11 -23
- data/lib/neo4j/core/query.rb +1 -1
- data/lib/neo4j/migration.rb +17 -18
- data/lib/neo4j/paginated.rb +4 -4
- data/lib/neo4j/railtie.rb +19 -19
- data/lib/neo4j/shared.rb +7 -3
- data/lib/neo4j/shared/callbacks.rb +15 -4
- data/lib/neo4j/shared/identity.rb +2 -2
- data/lib/neo4j/shared/persistence.rb +10 -21
- data/lib/neo4j/shared/property.rb +17 -30
- data/lib/neo4j/shared/rel_type_converters.rb +1 -3
- data/lib/neo4j/shared/type_converters.rb +13 -25
- data/lib/neo4j/shared/validations.rb +3 -3
- data/lib/neo4j/tasks/migration.rake +7 -7
- data/lib/neo4j/type_converters.rb +1 -1
- data/lib/neo4j/version.rb +1 -1
- data/lib/rails/generators/neo4j/model/model_generator.rb +16 -12
- data/lib/rails/generators/neo4j_generator.rb +18 -18
- data/neo4j.gemspec +22 -18
- metadata +103 -1
@@ -5,16 +5,16 @@ module Neo4j
|
|
5
5
|
module HasN
|
6
6
|
class Association
|
7
7
|
include Neo4j::Shared::RelTypeConverters
|
8
|
-
|
8
|
+
include Neo4j::ActiveNode::Dependent::AssociationMethods
|
9
|
+
attr_reader :type, :name, :relationship, :direction, :dependent
|
9
10
|
|
10
11
|
def initialize(type, direction, name, options = {})
|
11
|
-
|
12
|
+
validate_init_arguments(type, direction, options)
|
12
13
|
@type = type.to_sym
|
13
14
|
@name = name
|
14
15
|
@direction = direction.to_sym
|
15
16
|
@target_class_name_from_name = name.to_s.classify
|
16
|
-
|
17
|
-
set_vars_from_options(options)
|
17
|
+
apply_vars_from_options(options)
|
18
18
|
end
|
19
19
|
|
20
20
|
def target_class_option(options)
|
@@ -42,7 +42,11 @@ module Neo4j
|
|
42
42
|
end
|
43
43
|
|
44
44
|
def target_class_name
|
45
|
-
@target_class_option.to_s if @target_class_option
|
45
|
+
@target_class_name ||= @target_class_option.to_s if @target_class_option
|
46
|
+
end
|
47
|
+
|
48
|
+
def target_class_name_or_nil
|
49
|
+
@target_class_name_or_nil ||= target_class_name || 'nil'
|
46
50
|
end
|
47
51
|
|
48
52
|
def target_class
|
@@ -75,9 +79,7 @@ module Neo4j
|
|
75
79
|
end
|
76
80
|
end
|
77
81
|
|
78
|
-
|
79
|
-
@relationship_class
|
80
|
-
end
|
82
|
+
attr_reader :relationship_class
|
81
83
|
|
82
84
|
def relationship_class_type
|
83
85
|
@relationship_class = @relationship_class.constantize if @relationship_class.class == String || @relationship_class == Symbol
|
@@ -104,6 +106,10 @@ module Neo4j
|
|
104
106
|
properties
|
105
107
|
end
|
106
108
|
|
109
|
+
def unique?
|
110
|
+
@origin ? origin_association.unique? : !!@unique
|
111
|
+
end
|
112
|
+
|
107
113
|
private
|
108
114
|
|
109
115
|
def get_direction(relationship_cypher, create)
|
@@ -129,19 +135,24 @@ module Neo4j
|
|
129
135
|
p.size == 0 ? '' : " {#{p}}"
|
130
136
|
end
|
131
137
|
|
138
|
+
def origin_association
|
139
|
+
target_class.associations[@origin]
|
140
|
+
end
|
141
|
+
|
132
142
|
def origin_type
|
133
|
-
|
143
|
+
origin_association.relationship_type
|
134
144
|
end
|
135
145
|
|
136
146
|
private
|
137
147
|
|
138
|
-
def
|
139
|
-
validate_option_combinations(options)
|
148
|
+
def apply_vars_from_options(options)
|
140
149
|
@target_class_option = target_class_option(options)
|
141
150
|
@callbacks = {before: options[:before], after: options[:after]}
|
142
151
|
@origin = options[:origin] && options[:origin].to_sym
|
143
152
|
@relationship_class = options[:rel_class]
|
144
153
|
@relationship_type = options[:type] && options[:type].to_sym
|
154
|
+
@dependent = options[:dependent]
|
155
|
+
@unique = options[:unique]
|
145
156
|
end
|
146
157
|
|
147
158
|
# Return basic details about association as declared in the model
|
@@ -151,15 +162,25 @@ module Neo4j
|
|
151
162
|
"#{type} #{direction.inspect}, #{name.inspect}"
|
152
163
|
end
|
153
164
|
|
165
|
+
def validate_init_arguments(type, direction, options)
|
166
|
+
validate_option_combinations(options)
|
167
|
+
check_valid_type_and_dir(type, direction)
|
168
|
+
end
|
169
|
+
|
154
170
|
def check_valid_type_and_dir(type, direction)
|
155
|
-
|
156
|
-
|
171
|
+
fail ArgumentError, "Invalid association type: #{type.inspect} (valid value: :has_many and :has_one)" if not [:has_many, :has_one].include?(type.to_sym)
|
172
|
+
fail ArgumentError, "Invalid direction: #{direction.inspect} (valid value: :out, :in, and :both)" if not [:out, :in, :both].include?(direction.to_sym)
|
157
173
|
end
|
158
174
|
|
159
175
|
def validate_option_combinations(options)
|
160
|
-
|
161
|
-
|
162
|
-
|
176
|
+
fail ArgumentError, "Cannot specify both :type and :origin (#{base_declaration})" if options[:type] && options[:origin]
|
177
|
+
fail ArgumentError, "Cannot specify both :type and :rel_class (#{base_declaration})" if options[:type] && options[:rel_class]
|
178
|
+
fail ArgumentError, "Cannot specify both :origin and :rel_class (#{base_declaration}" if options[:origin] && options[:rel_class]
|
179
|
+
end
|
180
|
+
|
181
|
+
VALID_DEPENDENT_TYPES = [:delete, :delete_orphans, :destroy_orphans, :destroy, nil]
|
182
|
+
def validate_dependent(value)
|
183
|
+
fail ArgumentError, "Invalid dependent value: #{value.inspect}" if not VALID_DEPENDENT_TYPES.include?(value)
|
163
184
|
end
|
164
185
|
|
165
186
|
# Determine if model class as derived from the association name would be different than the one specified via the model_class key
|
@@ -174,22 +195,16 @@ module Neo4j
|
|
174
195
|
end
|
175
196
|
|
176
197
|
def validate_origin!
|
177
|
-
if @origin
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
end
|
186
|
-
else
|
187
|
-
raise ArgumentError, "Cannot use :origin without a model_class (implied or explicit)"
|
188
|
-
end
|
189
|
-
end
|
198
|
+
return if not @origin
|
199
|
+
|
200
|
+
fail ArgumentError, 'Cannot use :origin without a model_class (implied or explicit)' if not target_class
|
201
|
+
|
202
|
+
association = origin_association
|
203
|
+
fail ArgumentError, "Origin `#{@origin.inspect}` association not found for #{target_class} (specified in #{base_declaration})" if not association
|
204
|
+
|
205
|
+
fail ArgumentError, "Origin `#{@origin.inspect}` (specified in #{base_declaration}) has same direction `#{@direction}`)" if @direction == association.direction
|
190
206
|
end
|
191
207
|
end
|
192
208
|
end
|
193
209
|
end
|
194
210
|
end
|
195
|
-
|
@@ -1,5 +1,4 @@
|
|
1
1
|
module Neo4j::ActiveNode
|
2
|
-
|
3
2
|
# This module makes it possible to use other IDs than the build it neo4j id (neo_id)
|
4
3
|
#
|
5
4
|
# @example using generated UUIDs
|
@@ -27,16 +26,16 @@ module Neo4j::ActiveNode
|
|
27
26
|
|
28
27
|
module TypeMethods
|
29
28
|
def define_id_methods(clazz, name, conf)
|
30
|
-
|
29
|
+
fail "Expected a Hash, got #{conf.class} (#{conf}) for id_property" unless conf.is_a?(Hash)
|
31
30
|
if conf[:on]
|
32
31
|
define_custom_method(clazz, name, conf[:on])
|
33
32
|
elsif conf[:auto]
|
34
|
-
|
33
|
+
fail "only :uuid auto id_property allowed, got #{conf[:auto]}" unless conf[:auto] == :uuid
|
35
34
|
define_uuid_method(clazz, name)
|
36
35
|
elsif conf.empty?
|
37
36
|
define_property_method(clazz, name)
|
38
37
|
else
|
39
|
-
|
38
|
+
fail "Illegal value #{conf.inspect} for id_property, expected :on or :auto"
|
40
39
|
end
|
41
40
|
end
|
42
41
|
|
@@ -45,23 +44,22 @@ module Neo4j::ActiveNode
|
|
45
44
|
def define_property_method(clazz, name)
|
46
45
|
clear_methods(clazz, name)
|
47
46
|
|
48
|
-
clazz.module_eval(%
|
47
|
+
clazz.module_eval(%(
|
49
48
|
def id
|
50
|
-
|
49
|
+
_persisted_obj ? #{name.to_sym == :id ? 'attribute(\'id\')' : name} : nil
|
51
50
|
end
|
52
51
|
|
53
52
|
validates_uniqueness_of :#{name}
|
54
53
|
|
55
54
|
property :#{name}
|
56
|
-
|
57
|
-
|
55
|
+
), __FILE__, __LINE__)
|
58
56
|
end
|
59
57
|
|
60
58
|
|
61
59
|
def define_uuid_method(clazz, name)
|
62
60
|
clear_methods(clazz, name)
|
63
61
|
|
64
|
-
clazz.module_eval(%
|
62
|
+
clazz.module_eval(%(
|
65
63
|
default_property :#{name} do
|
66
64
|
::SecureRandom.uuid
|
67
65
|
end
|
@@ -71,13 +69,13 @@ module Neo4j::ActiveNode
|
|
71
69
|
end
|
72
70
|
|
73
71
|
alias_method :id, :#{name}
|
74
|
-
|
72
|
+
), __FILE__, __LINE__)
|
75
73
|
end
|
76
74
|
|
77
75
|
def define_custom_method(clazz, name, on)
|
78
76
|
clear_methods(clazz, name)
|
79
77
|
|
80
|
-
clazz.module_eval(%
|
78
|
+
clazz.module_eval(%{
|
81
79
|
default_property :#{name} do |instance|
|
82
80
|
raise "Specifying custom id_property #{name} on none existing method #{on}" unless instance.respond_to?(:#{on})
|
83
81
|
instance.#{on}
|
@@ -93,15 +91,15 @@ module Neo4j::ActiveNode
|
|
93
91
|
|
94
92
|
def clear_methods(clazz, name)
|
95
93
|
if clazz.method_defined?(name)
|
96
|
-
clazz.module_eval(%
|
94
|
+
clazz.module_eval(%(
|
97
95
|
undef_method :#{name}
|
98
|
-
|
96
|
+
), __FILE__, __LINE__)
|
99
97
|
end
|
100
98
|
|
101
99
|
if clazz.attribute_names.include?(name.to_s)
|
102
|
-
clazz.module_eval(%
|
100
|
+
clazz.module_eval(%(
|
103
101
|
undef_property :#{name}
|
104
|
-
|
102
|
+
), __FILE__, __LINE__)
|
105
103
|
end
|
106
104
|
end
|
107
105
|
|
@@ -110,7 +108,6 @@ module Neo4j::ActiveNode
|
|
110
108
|
|
111
109
|
|
112
110
|
module ClassMethods
|
113
|
-
|
114
111
|
def find_by_neo_id(id)
|
115
112
|
Neo4j::Node.load(id)
|
116
113
|
end
|
@@ -125,7 +122,7 @@ module Neo4j::ActiveNode
|
|
125
122
|
|
126
123
|
def id_property(name, conf = {})
|
127
124
|
begin
|
128
|
-
if
|
125
|
+
if id_property?
|
129
126
|
unless mapped_label.uniqueness_constraints[:property_keys].include?([name])
|
130
127
|
drop_constraint(id_property_name, type: :unique)
|
131
128
|
end
|
@@ -142,7 +139,15 @@ module Neo4j::ActiveNode
|
|
142
139
|
end
|
143
140
|
end
|
144
141
|
|
142
|
+
# rubocop:disable Style/PredicateName
|
145
143
|
def has_id_property?
|
144
|
+
ActiveSupport::Deprecation.warn 'has_id_property? is deprecated and may be removed from future releases, use id_property? instead.', caller
|
145
|
+
|
146
|
+
id_property?
|
147
|
+
end
|
148
|
+
# rubocop:enable Style/PredicateName
|
149
|
+
|
150
|
+
def id_property?
|
146
151
|
id_property_info && !id_property_info.empty?
|
147
152
|
end
|
148
153
|
|
@@ -155,8 +160,6 @@ module Neo4j::ActiveNode
|
|
155
160
|
end
|
156
161
|
|
157
162
|
alias_method :primary_key, :id_property_name
|
158
|
-
|
159
163
|
end
|
160
164
|
end
|
161
|
-
|
162
165
|
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
module Neo4j::ActiveNode::Initialize
|
2
2
|
extend ActiveSupport::Concern
|
3
3
|
include Neo4j::Shared::TypeConverters
|
4
|
+
attr_reader :called_by
|
4
5
|
|
5
6
|
# called when loading the node from the database
|
6
7
|
# @param [Neo4j::Node] persisted_node the node this class wraps
|
@@ -11,7 +12,7 @@ module Neo4j::ActiveNode::Initialize
|
|
11
12
|
@association_cache = {}
|
12
13
|
changed_attributes && changed_attributes.clear
|
13
14
|
@attributes = attributes.merge(properties.stringify_keys)
|
14
|
-
self.default_properties=properties
|
15
|
+
self.default_properties = properties
|
15
16
|
@attributes = convert_properties_to :ruby, @attributes
|
16
17
|
end
|
17
18
|
|
@@ -21,7 +22,4 @@ module Neo4j::ActiveNode::Initialize
|
|
21
22
|
def wrapper
|
22
23
|
self
|
23
24
|
end
|
24
|
-
|
25
25
|
end
|
26
|
-
|
27
|
-
|
@@ -1,7 +1,5 @@
|
|
1
1
|
module Neo4j
|
2
2
|
module ActiveNode
|
3
|
-
|
4
|
-
|
5
3
|
# Provides a mapping between neo4j labels and Ruby classes
|
6
4
|
module Labels
|
7
5
|
extend ActiveSupport::Concern
|
@@ -52,7 +50,7 @@ module Neo4j
|
|
52
50
|
# Only for testing purpose
|
53
51
|
# @private
|
54
52
|
def self._wrapped_labels=(wl)
|
55
|
-
@_wrapped_labels=(wl)
|
53
|
+
@_wrapped_labels = (wl)
|
56
54
|
end
|
57
55
|
|
58
56
|
def self._wrapped_labels
|
@@ -74,25 +72,24 @@ module Neo4j
|
|
74
72
|
# Returns the object with the specified neo4j id.
|
75
73
|
# @param [String,Fixnum] id of node to find
|
76
74
|
def find(id)
|
77
|
-
map_id =
|
75
|
+
map_id = proc { |object| object.respond_to?(:id) ? object.send(:id) : object }
|
78
76
|
|
79
77
|
if id.is_a?(Array)
|
80
|
-
find_by_ids(id.map {|o| map_id.call(o) })
|
78
|
+
find_by_ids(id.map { |o| map_id.call(o) })
|
81
79
|
else
|
82
80
|
find_by_id(map_id.call(id))
|
83
81
|
end
|
84
82
|
end
|
85
83
|
|
86
84
|
# Finds the first record matching the specified conditions. There is no implied ordering so if order matters, you should specify it yourself.
|
87
|
-
# @param
|
88
|
-
def find_by(
|
89
|
-
self.query_as(:n).where(n:
|
85
|
+
# @param Hash args of arguments to find
|
86
|
+
def find_by(values)
|
87
|
+
self.query_as(:n).where(n: values).limit(1).pluck(:n).first
|
90
88
|
end
|
91
89
|
|
92
90
|
# Like find_by, except that if no record is found, raises a RecordNotFound error.
|
93
|
-
def find_by!(
|
94
|
-
|
95
|
-
find_by(args) or raise RecordNotFound, "#{self.query_as(:n).where(n: a).limit(1).to_cypher} returned no results"
|
91
|
+
def find_by!(values)
|
92
|
+
find_by(values) || fail(RecordNotFound, "#{self.query_as(:n).where(n: values).limit(1).to_cypher} returned no results")
|
96
93
|
end
|
97
94
|
|
98
95
|
# Deletes all nodes and connected relationships from Cypher.
|
@@ -104,7 +101,7 @@ module Neo4j
|
|
104
101
|
# Returns each node to Ruby and calls `destroy`. Be careful, as this can be a very slow operation if you have many nodes. It will generate at least
|
105
102
|
# one database query per node in the database, more if callbacks require them.
|
106
103
|
def destroy_all
|
107
|
-
self.all.each
|
104
|
+
self.all.each(&:destroy)
|
108
105
|
end
|
109
106
|
|
110
107
|
# Creates a Neo4j index on given property
|
@@ -141,14 +138,14 @@ module Neo4j
|
|
141
138
|
# @example
|
142
139
|
# Person.constraint :name, type: :unique
|
143
140
|
#
|
144
|
-
def constraint(property, constraints
|
141
|
+
def constraint(property, constraints)
|
145
142
|
Neo4j::Session.on_session_available do |session|
|
146
143
|
label = Neo4j::Label.create(mapped_label_name)
|
147
144
|
label.create_constraint(property, constraints, session)
|
148
145
|
end
|
149
146
|
end
|
150
147
|
|
151
|
-
def drop_constraint(property, constraint
|
148
|
+
def drop_constraint(property, constraint)
|
152
149
|
Neo4j::Session.on_session_available do |session|
|
153
150
|
label = Neo4j::Label.create(mapped_label_name)
|
154
151
|
label.drop_constraint(property, constraint, session)
|
@@ -166,7 +163,7 @@ module Neo4j
|
|
166
163
|
|
167
164
|
# @return [Symbol] the label that this class has which corresponds to a Ruby class
|
168
165
|
def mapped_label_name
|
169
|
-
@
|
166
|
+
@mapped_label_name || (self.name.nil? ? object_id.to_s.to_sym : self.name.to_sym)
|
170
167
|
end
|
171
168
|
|
172
169
|
# @return [Neo4j::Label] the label for this class
|
@@ -180,7 +177,7 @@ module Neo4j
|
|
180
177
|
|
181
178
|
def base_class
|
182
179
|
unless self < Neo4j::ActiveNode
|
183
|
-
|
180
|
+
fail "#{name} doesn't belong in a hierarchy descending from ActiveNode"
|
184
181
|
end
|
185
182
|
|
186
183
|
if superclass == Object
|
@@ -204,21 +201,25 @@ module Neo4j
|
|
204
201
|
else
|
205
202
|
label.create_index(property) unless existing.flatten.include?(property)
|
206
203
|
end
|
207
|
-
|
208
204
|
end
|
209
205
|
end
|
210
206
|
|
211
207
|
def mapped_labels
|
212
|
-
mapped_label_names.map{|label_name| Neo4j::Label.create(label_name)}
|
208
|
+
mapped_label_names.map { |label_name| Neo4j::Label.create(label_name) }
|
213
209
|
end
|
214
210
|
|
215
|
-
def
|
216
|
-
@
|
211
|
+
def mapped_label_name=(name)
|
212
|
+
@mapped_label_name = name.to_sym
|
217
213
|
end
|
218
214
|
|
219
|
-
|
215
|
+
# rubocop:disable Style/AccessorMethodName
|
216
|
+
def set_mapped_label_name(name)
|
217
|
+
ActiveSupport::Deprecation.warn 'set_mapped_label_name is deprecated and may be removed from future releases, use self.mapped_label_name= instead.', caller
|
220
218
|
|
219
|
+
self.mapped_label_name = name
|
220
|
+
end
|
221
|
+
# rubocop:enable Style/AccessorMethodName
|
222
|
+
end
|
221
223
|
end
|
222
|
-
|
223
224
|
end
|
224
225
|
end
|
@@ -1,7 +1,6 @@
|
|
1
1
|
class Neo4j::Node
|
2
2
|
# The wrapping process is what transforms a raw CypherNode or EmbeddedNode from Neo4j::Core into a healthy ActiveNode (or ActiveRel) object.
|
3
3
|
module Wrapper
|
4
|
-
|
5
4
|
# this is a plugin in the neo4j-core so that the Ruby wrapper will be wrapped around the Neo4j::Node objects
|
6
5
|
def wrapper
|
7
6
|
self.props.symbolize_keys!
|
@@ -26,22 +25,20 @@ class Neo4j::Node
|
|
26
25
|
|
27
26
|
# Makes the determination of whether to use <tt>_classname</tt> (or whatever is defined by config) or the node's labels.
|
28
27
|
def sorted_wrapper_classes
|
29
|
-
if self.props.is_a?(Hash) && self.props.
|
28
|
+
if self.props.is_a?(Hash) && self.props.key?(Neo4j::Config.class_name_property)
|
30
29
|
self.props[Neo4j::Config.class_name_property].constantize
|
31
30
|
else
|
32
31
|
wrappers = _class_wrappers
|
33
32
|
return self if wrappers.nil?
|
34
|
-
wrapper_classes = wrappers.map{|w| Neo4j::ActiveNode::Labels._wrapped_labels[w]}
|
33
|
+
wrapper_classes = wrappers.map { |w| Neo4j::ActiveNode::Labels._wrapped_labels[w] }
|
35
34
|
wrapper_classes.sort.first
|
36
35
|
end
|
37
36
|
end
|
38
37
|
|
39
38
|
def load_class_from_label(label_name)
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
nil
|
44
|
-
end
|
39
|
+
label_name.to_s.split('::').inject(Kernel) { |container, name| container.const_get(name.to_s) }
|
40
|
+
rescue NameError
|
41
|
+
nil
|
45
42
|
end
|
46
43
|
|
47
44
|
def _class_wrappers
|