diff --git a/lib/neo4j/active_node/has_n/association.rb b/lib/neo4j/active_node/has_n/association.rb index 16af7537c..e9e846bcf 100644 --- a/lib/neo4j/active_node/has_n/association.rb +++ b/lib/neo4j/active_node/has_n/association.rb @@ -21,7 +21,7 @@ def target_class_option(model_class) case model_class when nil if @target_class_name_from_name - "::#{@target_class_name_from_name}" + "#{association_model_namespace}::#{@target_class_name_from_name}" else @target_class_name_from_name end @@ -49,11 +49,13 @@ def target_class_names end def target_classes_or_nil - @target_classes_or_nil ||= if target_class_names - target_class_names.map(&:constantize).select do |constant| - constant.ancestors.include?(::Neo4j::ActiveNode) - end - end + @target_classes_or_nil ||= discovered_model if target_class_names + end + + def discovered_model + target_class_names.map(&:constantize).select do |constant| + constant.ancestors.include?(::Neo4j::ActiveNode) + end end def target_class @@ -123,6 +125,12 @@ def create_method private + def association_model_namespace + namespace = Neo4j::Config[:association_model_namespace] + return nil if namespace.nil? + "::#{namespace}" + end + def direction_cypher(relationship_cypher, create, reverse = false) case get_direction(create, reverse) when :out diff --git a/lib/neo4j/active_node/labels.rb b/lib/neo4j/active_node/labels.rb index 9a75f1a25..d7a6e525e 100644 --- a/lib/neo4j/active_node/labels.rb +++ b/lib/neo4j/active_node/labels.rb @@ -5,7 +5,6 @@ module Labels extend ActiveSupport::Concern WRAPPED_CLASSES = [] - WRAPPED_MODELS = [] MODELS_FOR_LABELS_CACHE = {} MODELS_FOR_LABELS_CACHE.clear @@ -16,7 +15,7 @@ def self.inherited(model) super end - Neo4j::ActiveNode::Labels.add_wrapped_class(model) + Neo4j::ActiveNode::Labels.add_wrapped_class(model) unless Neo4j::ActiveNode::Labels._wrapped_classes.include?(model) end class InvalidQueryError < StandardError; end @@ -48,7 +47,6 @@ def remove_label(*label) def self.add_wrapped_class(model) _wrapped_classes << model - WRAPPED_MODELS << model end def self._wrapped_classes @@ -60,7 +58,7 @@ def self.model_for_labels(labels) end def self.model_cache(labels) - models = WRAPPED_MODELS.select do |model| + models = WRAPPED_CLASSES.select do |model| (model.mapped_label_names - labels).size == 0 end @@ -74,7 +72,7 @@ def self.clear_model_for_label_cache end def self.clear_wrapped_models - WRAPPED_MODELS.clear + WRAPPED_CLASSES.clear end protected @@ -178,7 +176,7 @@ def mapped_label_names # @return [Symbol] the label that this class has which corresponds to a Ruby class def mapped_label_name - @mapped_label_name || (self.name.nil? ? object_id.to_s.to_sym : self.name.to_sym) + @mapped_label_name || label_for_model end # @return [Neo4j::Label] the label for this class @@ -234,6 +232,25 @@ def set_mapped_label_name(name) self.mapped_label_name = name end # rubocop:enable Style/AccessorMethodName + + private + + def label_for_model + (self.name.nil? ? object_id.to_s.to_sym : decorated_label_name) + end + + def decorated_label_name + name = case Neo4j::Config[:module_handling] + when :demodulize + self.name.demodulize + when Proc + Neo4j::Config[:module_handling].call self.name + else + self.name + end + + name.to_sym + end end end end diff --git a/lib/neo4j/config.rb b/lib/neo4j/config.rb index 0976d4c57..fd558efeb 100644 --- a/lib/neo4j/config.rb +++ b/lib/neo4j/config.rb @@ -53,7 +53,6 @@ def use nil end - # Sets the value of a config entry. # # @param [Symbol] key the key to set the parameter for @@ -62,14 +61,12 @@ def []=(key, val) configuration[key.to_s] = val end - # @param [Symbol] key The key of the config entry value we want # @return the the value of a config entry def [](key) configuration[key.to_s] end - # Remove the value of a config entry. # # @param [Symbol] key the key of the configuration entry to delete @@ -78,7 +75,6 @@ def delete(key) configuration.delete(key) end - # Remove all configuration. This can be useful for testing purpose. # # @return nil @@ -86,7 +82,6 @@ def delete_all @configuration = nil end - # @return [Hash] The config as a hash. def to_hash configuration.to_hash @@ -105,6 +100,14 @@ def include_root_in_json # we use ternary because a simple || will always evaluate true Neo4j::Config[:include_root_in_json].nil? ? true : Neo4j::Config[:include_root_in_json] end + + def module_handling + Neo4j::Config[:module_handling] || :none + end + + def association_model_namespace + Neo4j::Config[:association_model_namespace] || nil + end end end end diff --git a/spec/e2e/module_handling.rb b/spec/e2e/module_handling.rb new file mode 100644 index 000000000..152a9e46c --- /dev/null +++ b/spec/e2e/module_handling.rb @@ -0,0 +1,71 @@ +require 'spec_helper' + +describe 'Module handling from config: :module_handling option' do + let(:clazz) do + Class.new do + include Neo4j::ActiveNode + end + end + + before { stub_const 'ModuleTest::Student', clazz } + after do + Neo4j::Config[:association_model_namespace] = nil + Neo4j::Config[:module_handling] = nil + end + + describe 'labels' do + context 'with config unspecified or neither :demodulize nor a proc' do + it 'are set using the full module and class name' do + expect(ModuleTest::Student.mapped_label_name).to eq :'ModuleTest::Student' + end + end + + context 'with config set to :demodulize' do + before { Neo4j::Config[:module_handling] = :demodulize } + + it 'strips module names from labels' do + expect(ModuleTest::Student.mapped_label_name).to eq :Student + end + end + + context 'with a proc' do + before do + Neo4j::Config[:module_handling] = proc do |name| + module_name = name.deconstantize + name.gsub(module_name, 'Foo') + end + end + + it 'lets you modify the name as you see fit' do + expect(ModuleTest::Student.mapped_label_name).to eq :'Foo::Student' + end + end + end + + describe 'association model locations' do + let(:discovered_model) { clazz.associations[:students].instance_variable_get(:@target_class_option) } + + context 'with config set to :none or unspecified' do + before { clazz.has_many :out, :students } + + it 'expects a class with the singular version of the association' do + expect(discovered_model).to eq '::Student' + end + end + + context ' with :association_model_namespace set' do + before do + Neo4j::Config[:association_model_namespace] = 'ModuleTest' + clazz.has_many :out, :students + end + + it 'expects namespacing and looks for a model in the same namespace as the source' do + expect(discovered_model).to eq '::ModuleTest::Student' + node1 = ModuleTest::Student.create + node2 = ModuleTest::Student.create + node1.students << node2 + expect(node1.students.to_a).to include node2 + end + end + end +end