diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 05285437c..b6b693494 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -17,10 +17,16 @@ jobs: strategy: fail-fast: false matrix: - ruby: [ jruby-9.3.10.0, jruby-9.4.2.0, ruby-3.1.4, ruby-3.2.2 ] - neo4j: [ 3.5.35, 4.0.12, 4.1.12, 4.2.19, 4.3.23, 4.4.19, 5.7.0 ] + ruby: [ jruby-9.4.5.0, ruby-3.1.4 ] + neo4j: [ 3.5.35, 4.0.12, 4.1.13, 4.2.19, 4.3.24, 4.4.29, 5.15.0 ] + active_model: [ 7.0.8, 7.1.2 ] + include: + - ruby: ruby-3.2.2 + neo4j: 5.15.0 + active_model: 7.1.2 env: NEO4J_VERSION: ${{ matrix.neo4j }} + ACTIVE_MODEL_VERSION: ${{ matrix.active_model }} JRUBY_OPTS: --debug -J-Xmx1280m -Xcompile.invokedynamic=false -J-XX:+TieredCompilation -J-XX:TieredStopAtLevel=1 -J-noverify -Xcompile.mode=OFF steps: - name: Start neo4j @@ -42,5 +48,5 @@ jobs: - name: Run tests run: bundle exec rspec - - name: Run tests - run: bundle exec rubocop +# - name: Run tests +# run: bundle exec rubocop diff --git a/.java-version b/.java-version new file mode 100644 index 000000000..03b6389f3 --- /dev/null +++ b/.java-version @@ -0,0 +1 @@ +17.0 diff --git a/CHANGELOG.md b/CHANGELOG.md index 62f9c4a7a..0daf6e61c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,10 +3,21 @@ All notable changes to this project will be documented in this file. This file should follow the standards specified on [http://keepachangelog.com/] This project adheres to [Semantic Versioning](http://semver.org/). -## [11.4.0] 2023-02-12 +## [11.5.0.beta.1] 2023-12-29 + +## Added + +- migrated to zeitwerk +- rails 7.1 support ## Fixed +- caching of has one relationships + +## [11.4.0] 2023-12-02 + +## Added + - Added support for cypher UNION ## [11.3.1] 2023-02-12 diff --git a/Gemfile b/Gemfile index bb211c357..0d865053a 100644 --- a/Gemfile +++ b/Gemfile @@ -2,10 +2,6 @@ source 'http://rubygems.org' gemspec -# gem 'neo4j-ruby-driver', path: '../neo4j-ruby-driver' - -gem 'listen', '< 3.1' - active_model_version = ENV['ACTIVE_MODEL_VERSION'] gem 'activemodel', "~> #{active_model_version}" if active_model_version&.length&.positive? diff --git a/activegraph.gemspec b/activegraph.gemspec index 32b8e6b79..f1a3dabec 100644 --- a/activegraph.gemspec +++ b/activegraph.gemspec @@ -30,11 +30,10 @@ DESCRIPTION 'bug_tracker_uri' => 'https://github.com/neo4jrb/activegraph/issues' } - s.add_dependency('activemodel', '>= 4.0') - s.add_dependency('activesupport', '>= 4.0') + s.add_dependency('activemodel', '>= 7') s.add_dependency('i18n', '!= 1.8.8') # https://github.com/jruby/jruby/issues/6547 - s.add_dependency('neo4j-ruby-driver', '>= 4.4.1') - s.add_dependency('orm_adapter', '~> 0.5.0') + s.add_dependency('neo4j-ruby-driver', '>= 4.4.1', '< 5') + s.add_dependency('orm_adapter', '>= 0.5.0') s.add_dependency('sorted_set') s.add_development_dependency('guard') s.add_development_dependency('guard-rspec') @@ -42,10 +41,10 @@ DESCRIPTION s.add_development_dependency('neo4j-rake_tasks', '>= 0.3.0') s.add_development_dependency('os') s.add_development_dependency('pry') - s.add_development_dependency('railties', '>= 4.0') + s.add_development_dependency('railties', '>= 7') s.add_development_dependency('rake') s.add_development_dependency('rubocop', '>= 0.56.0') s.add_development_dependency('yard') s.add_development_dependency('dryspec') - s.add_development_dependency('rspec', '< 3.10') # Cannot proxy frozen objects + s.add_development_dependency('rspec', '>= 3.10') end diff --git a/docs/activegraph.rb b/docs/activegraph.rb index 5e2f45cba..90324e5a7 100644 --- a/docs/activegraph.rb +++ b/docs/activegraph.rb @@ -1,6 +1,6 @@ # Usage: rails new myapp -m activegraph.rb -gem 'activegraph', '=> 11.1' +gem 'activegraph', '>= 11.1' gem_group :development do gem 'neo4j-rake_tasks' @@ -12,7 +12,7 @@ END end -gsub_file 'config/application.rb', "require 'rails'", '' +gsub_file 'config/application.rb', 'require "rails"', '' generator = %q[ # Enable ActiveGraph generators, e.g: rails generate model Admin --parent User diff --git a/lib/active_graph.rb b/lib/active_graph.rb index e45e2e7aa..fbe28a308 100644 --- a/lib/active_graph.rb +++ b/lib/active_graph.rb @@ -1,125 +1,42 @@ +require 'benchmark' +require 'bigdecimal' +require 'bigdecimal/util' +require 'date' require 'forwardable' -require 'active_graph/version' - -require 'active_graph/core' -require 'active_graph/core/query_ext' # From this gem - -require 'active_support/core_ext/module/attribute_accessors_per_thread' -require 'active_graph/secure_random_ext' -require 'active_graph/transactions' -require 'active_graph/base' -require 'active_graph/model_schema' - require 'active_model' -require 'active_support/concern' -require 'active_support/core_ext/class/attribute.rb' -require 'active_support/core_ext/class/subclasses.rb' +require 'active_model/attribute_set' +require 'active_support/core_ext/big_decimal/conversions' +require 'active_support/core_ext/class/attribute' +require 'active_support/core_ext/class/subclasses' require 'active_support/core_ext/module/attribute_accessors' +require 'active_support/core_ext/module/attribute_accessors_per_thread' +require 'active_support/core_ext/string/conversions' +require 'active_support/inflector' +require 'active_support/inflector/inflections' +require 'active_support/notifications' require 'json' - -require 'active_graph/lazy_attribute_hash' -require 'active_graph/attribute_set' -require 'active_graph/errors' -require 'active_graph/config' -require 'active_graph/wrapper' -require 'active_graph/relationship/rel_wrapper' -require 'active_graph/node/node_wrapper' -require 'active_graph/shared/type_converters' -require 'active_graph/shared/rel_type_converters' -require 'active_graph/shared/marshal' -require 'active_graph/type_converters' -require 'active_graph/paginated' -require 'active_graph/schema/operation' - -require 'active_graph/timestamps' -require 'active_graph/undeclared_properties' - -require 'active_graph/shared/callbacks' -require 'active_graph/shared/filtered_hash' -require 'active_graph/shared/declared_property/index' -require 'active_graph/shared/declared_property' -require 'active_graph/shared/declared_properties' -require 'active_graph/shared/enum' -require 'active_graph/shared/mass_assignment' -require 'active_graph/shared/attributes' -require 'active_graph/shared/typecasted_attributes' -require 'active_graph/shared/property' -require 'active_graph/shared/persistence' -require 'active_graph/shared/validations' -require 'active_graph/shared/identity' -require 'active_graph/shared/serialized_properties' -require 'active_graph/shared/typecaster' -require 'active_graph/shared/initialize' -require 'active_graph/shared/query_factory' -require 'active_graph/shared/cypher' -require 'active_graph/shared/permitted_attributes' -require 'active_graph/shared' - -require 'active_graph/relationship/callbacks' -require 'active_graph/relationship/initialize' -require 'active_graph/relationship/property' -require 'active_graph/relationship/persistence/query_factory' -require 'active_graph/relationship/persistence' -require 'active_graph/relationship/validations' -require 'active_graph/relationship/query' -require 'active_graph/relationship/related_node' -require 'active_graph/relationship/types' -require 'active_graph/relationship' - -require 'active_graph/node/dependent_callbacks' -require 'active_graph/node/node_list_formatter' -require 'active_graph/node/dependent' -require 'active_graph/node/dependent/query_proxy_methods' -require 'active_graph/node/dependent/association_methods' -require 'active_graph/node/enum' -require 'active_graph/node/query_methods' -require 'active_graph/node/query/query_proxy_methods' -require 'active_graph/node/query/query_proxy_methods_of_mass_updating' -require 'active_graph/node/query/query_proxy_enumerable' -require 'active_graph/node/query/query_proxy_find_in_batches' -require 'active_graph/node/query/query_proxy_eager_loading' -require 'active_graph/node/query/query_proxy_eager_loading/association_tree' -require 'active_graph/node/query/query_proxy_link' -require 'active_graph/node/labels/index' -require 'active_graph/node/labels/reloading' -require 'active_graph/node/labels' -require 'active_graph/node/id_property/accessor' -require 'active_graph/node/id_property' -require 'active_graph/node/callbacks' -require 'active_graph/node/initialize' -require 'active_graph/node/property' -require 'active_graph/node/persistence' -require 'active_graph/node/validations' -require 'active_graph/node/rels' -require 'active_graph/node/reflection' -require 'active_graph/node/unpersisted' -require 'active_graph/node/has_n' -require 'active_graph/node/has_n/association_cypher_methods' -require 'active_graph/node/has_n/association/rel_wrapper' -require 'active_graph/node/has_n/association/rel_factory' -require 'active_graph/node/has_n/association' -require 'active_graph/node/query/query_proxy' -require 'active_graph/node/query' -require 'active_graph/node/scope' -require 'active_graph/node' - -require 'active_support/concern' -require 'active_graph/core/cypher_error' -require 'active_graph/core/schema_errors' - -module ActiveGraph - extend ActiveSupport::Autoload - autoload :Migrations - autoload :Migration -end - -load 'active_graph/tasks/migration.rake' - -require 'active_graph/node/orm_adapter' -if defined?(Rails) - require 'rails/generators' - require 'rails/generators/active_graph_generator' -end - +require 'neo4j/driver' +require 'orm_adapter' +require 'rake' +require 'set' +require 'sorted_set' +require 'yaml' + +loader = Zeitwerk::Loader.for_gem +loader.ignore(File.expand_path('rails', __dir__)) +loader.ignore(File.expand_path('active_graph/railtie.rb', __dir__)) +loader.inflector.inflect("ansi" => "ANSI") +loader.setup +# loader.eager_load + +Neo4j::Driver::Result.prepend ActiveGraph::Core::Result +Neo4j::Driver::Record.prepend ActiveGraph::Core::Record Neo4j::Driver::Transaction.prepend ActiveGraph::Transaction +Neo4j::Driver::Types::Entity.include ActiveGraph::Core::Wrappable +Neo4j::Driver::Types::Entity.prepend ActiveGraph::Core::Entity +Neo4j::Driver::Types::Node.prepend ActiveGraph::Core::Node +Neo4j::Driver::Types::Node.wrapper_callback(&ActiveGraph::Node::Wrapping.method(:wrapper)) +Neo4j::Driver::Types::Relationship.wrapper_callback(&ActiveGraph::Relationship::Wrapping.method(:wrapper)) SecureRandom.singleton_class.prepend ActiveGraph::SecureRandomExt + +load 'active_graph/tasks/migration.rake' diff --git a/lib/active_graph/attribute_set.rb b/lib/active_graph/attribute_set.rb index cc10049af..ce59ef974 100644 --- a/lib/active_graph/attribute_set.rb +++ b/lib/active_graph/attribute_set.rb @@ -1,7 +1,3 @@ -# frozen_string_literal: true - -require 'active_model/attribute_set' - module ActiveGraph class AttributeSet < ActiveModel::AttributeSet def initialize(attr_hash, attr_list) diff --git a/lib/active_graph/base.rb b/lib/active_graph/base.rb index f42b53046..bfa1ada47 100644 --- a/lib/active_graph/base.rb +++ b/lib/active_graph/base.rb @@ -1,6 +1,3 @@ -require 'active_graph/core/querable' -require 'active_graph/core/schema' - module ActiveGraph # To contain any base login for Node/Relationship which # is external to the main classes diff --git a/lib/active_graph/config.rb b/lib/active_graph/config.rb index 07f4d94ce..51d37c23c 100644 --- a/lib/active_graph/config.rb +++ b/lib/active_graph/config.rb @@ -26,7 +26,6 @@ def default_file=(file_path) # @return [Hash] the default file loaded by yaml def defaults - require 'yaml' @defaults ||= ActiveSupport::HashWithIndifferentAccess.new(YAML.load_file(default_file)) end diff --git a/lib/active_graph/core.rb b/lib/active_graph/core.rb deleted file mode 100644 index a469373e5..000000000 --- a/lib/active_graph/core.rb +++ /dev/null @@ -1,14 +0,0 @@ -require 'active_graph/core/instrumentable' -require 'active_graph/core/entity' -require 'active_graph/core/node' -require 'active_graph/core/query' -require 'active_graph/core/record' -require 'active_graph/core/wrappable' -require 'active_graph/transaction' -require 'neo4j/driver' - -Neo4j::Driver::Types::Entity.include ActiveGraph::Core::Wrappable -Neo4j::Driver::Types::Entity.prepend ActiveGraph::Core::Entity -Neo4j::Driver::Types::Node.prepend ActiveGraph::Core::Node -Neo4j::Driver::Result.prepend ActiveGraph::Core::Result -Neo4j::Driver::Record.prepend ActiveGraph::Core::Record diff --git a/lib/active_graph/core/instrumentable.rb b/lib/active_graph/core/instrumentable.rb index 0c12d7e93..a1eeb8cee 100644 --- a/lib/active_graph/core/instrumentable.rb +++ b/lib/active_graph/core/instrumentable.rb @@ -1,8 +1,3 @@ -require 'active_support/concern' -require 'active_support/notifications' -require 'active_graph/ansi' -require 'active_graph/core/logging' - module ActiveGraph module Core module Instrumentable diff --git a/lib/active_graph/core/querable.rb b/lib/active_graph/core/querable.rb index d6b66ee43..4e10d2ea1 100644 --- a/lib/active_graph/core/querable.rb +++ b/lib/active_graph/core/querable.rb @@ -1,8 +1,3 @@ -require 'active_graph/core/instrumentable' -require 'active_graph/transaction' -require 'active_graph/core/query_builder' -require 'active_graph/core/record' - module ActiveGraph module Core module Querable diff --git a/lib/active_graph/core/query.rb b/lib/active_graph/core/query.rb index 90a4ab0af..a38fac719 100644 --- a/lib/active_graph/core/query.rb +++ b/lib/active_graph/core/query.rb @@ -1,7 +1,3 @@ -require 'active_graph/core/query_clauses' -require 'active_graph/core/query_find_in_batches' -require 'active_support/notifications' - module ActiveGraph module Core # Allows for generation of cypher queries via ruby method calls (inspired by ActiveRecord / arel syntax) @@ -15,9 +11,9 @@ module Core class Query include ActiveGraph::Core::QueryClauses include ActiveGraph::Core::QueryFindInBatches + include QueryExt DEFINED_CLAUSES = {} - attr_accessor :clauses class Parameters @@ -247,7 +243,7 @@ def match_nodes(hash, optional_match = false) neo_id = (node_object.respond_to?(:neo_id) ? node_object.neo_id : node_object) match_method = optional_match ? :optional_match : :match - query.send(match_method, variable).where(variable => {neo_id: neo_id}) + query.send(match_method, variable).where(variable => { neo_id: }) end end @@ -271,7 +267,6 @@ def each # @return [Array] # @raise [ActiveGraph::Server::CypherResponse::ResponseError] Raises errors from neo4j server - # Executes a query without returning the result # @return [Boolean] true if successful # @raise [ActiveGraph::Server::CypherResponse::ResponseError] Raises errors from neo4j server @@ -318,6 +313,7 @@ def return_query(columns) # @return [String] Resulting cypher query string EMPTY = ' ' NEWLINE = "\n" + def to_cypher(options = {}) join_string = options[:pretty] ? NEWLINE : EMPTY @@ -334,6 +330,7 @@ def to_cypher(options = {}) cypher_string = "CYPHER #{@options[:parser]} #{cypher_string}" if @options[:parser] cypher_string.tap(&:strip!) end + alias cypher to_cypher def pretty_cypher diff --git a/lib/active_graph/core/query_ext.rb b/lib/active_graph/core/query_ext.rb index 6dd349ac3..9c7c02b2e 100644 --- a/lib/active_graph/core/query_ext.rb +++ b/lib/active_graph/core/query_ext.rb @@ -1,6 +1,6 @@ module ActiveGraph module Core - class Query + module QueryExt # Creates a ActiveGraph::Node::Query::QueryProxy object that builds off of a Core::Query object. # # @param [Class] model An Node model to be used as the start of a new QueryuProxy chain diff --git a/lib/active_graph/core/record.rb b/lib/active_graph/core/record.rb index 562615d2f..624e4729e 100644 --- a/lib/active_graph/core/record.rb +++ b/lib/active_graph/core/record.rb @@ -1,8 +1,3 @@ -# frozen_string_literal: true - -require 'active_graph/core/result' -require 'active_support/core_ext/module/attribute_accessors' - module ActiveGraph module Core module Record diff --git a/lib/active_graph/core/result.rb b/lib/active_graph/core/result.rb index af8fbcd9a..b121af866 100644 --- a/lib/active_graph/core/result.rb +++ b/lib/active_graph/core/result.rb @@ -16,6 +16,11 @@ def each(&block) @records&.each(&block) || super end + ## To avoid to_a on Neo4j::Driver::Result as that one does not call the above block + def to_a + map.to_a + end + def store return if @records keys diff --git a/lib/active_graph/core/wrappable.rb b/lib/active_graph/core/wrappable.rb index 696c7558f..85a21a02a 100644 --- a/lib/active_graph/core/wrappable.rb +++ b/lib/active_graph/core/wrappable.rb @@ -8,7 +8,7 @@ def wrap end class_methods do - def wrapper_callback(proc) + def wrapper_callback(&proc) fail 'Callback already specified!' if @wrapper_callback @wrapper_callback = proc end diff --git a/lib/active_graph/errors.rb b/lib/active_graph/error.rb similarity index 100% rename from lib/active_graph/errors.rb rename to lib/active_graph/error.rb diff --git a/lib/active_graph/lazy_attribute_hash.rb b/lib/active_graph/lazy_attribute_hash.rb index dff5bafb4..ddfb3906f 100644 --- a/lib/active_graph/lazy_attribute_hash.rb +++ b/lib/active_graph/lazy_attribute_hash.rb @@ -1,5 +1,3 @@ -require 'active_model/attribute_set' - module ActiveGraph class LazyAttributeHash < ActiveModel::LazyAttributeHash def initialize(values, attr_list) diff --git a/lib/active_graph/migration.rb b/lib/active_graph/migration.rb index d3b509a48..79e3dd38d 100644 --- a/lib/active_graph/migration.rb +++ b/lib/active_graph/migration.rb @@ -1,6 +1,3 @@ -require 'benchmark' -require 'active_graph/migrations/helpers/id_property' - module ActiveGraph class Migration def migrate diff --git a/lib/active_graph/migrations.rb b/lib/active_graph/migrations.rb index ed2738d83..6e3fe51c6 100644 --- a/lib/active_graph/migrations.rb +++ b/lib/active_graph/migrations.rb @@ -1,13 +1,5 @@ module ActiveGraph module Migrations - extend ActiveSupport::Autoload - autoload :Helpers - autoload :MigrationFile - autoload :Base - autoload :Runner - autoload :SchemaMigration - autoload :CheckPending - class << self def check_for_pending_migrations! return if ActiveGraph::Config.configuration['skip_migration_check'] diff --git a/lib/active_graph/migrations/helpers.rb b/lib/active_graph/migrations/helpers.rb index b41ced237..47a752ddd 100644 --- a/lib/active_graph/migrations/helpers.rb +++ b/lib/active_graph/migrations/helpers.rb @@ -1,14 +1,7 @@ -require 'benchmark' - module ActiveGraph module Migrations module Helpers extend ActiveSupport::Concern - extend ActiveSupport::Autoload - - autoload :Schema - autoload :IdProperty - autoload :Relationships PROPERTY_ALREADY_DEFINED = 'Property `%{new_property}` is already defined in `%{label}`. '\ 'To overwrite, call `remove_property(:%{label}, :%{new_property})` before this method.'.freeze diff --git a/lib/active_graph/migrations/runner.rb b/lib/active_graph/migrations/runner.rb index c73584a8f..a7821db08 100644 --- a/lib/active_graph/migrations/runner.rb +++ b/lib/active_graph/migrations/runner.rb @@ -1,7 +1,3 @@ -# frozen_string_literal: true - -require 'sorted_set' - module ActiveGraph module Migrations class Runner diff --git a/lib/active_graph/model_schema.rb b/lib/active_graph/model_schema.rb index fb98dbbc9..3ef1425df 100644 --- a/lib/active_graph/model_schema.rb +++ b/lib/active_graph/model_schema.rb @@ -1,4 +1,3 @@ -require 'set' module ActiveGraph # This is here to support the removed functionality of being able to # defined indexes and constraints on models diff --git a/lib/active_graph/node.rb b/lib/active_graph/node.rb index b70dbbfed..c905bc8a2 100644 --- a/lib/active_graph/node.rb +++ b/lib/active_graph/node.rb @@ -66,6 +66,7 @@ def self.loaded_classes end module ClassMethods + include ::OrmAdapter::ToAdapter def nodeify(object) if object.is_a?(::ActiveGraph::Node) || object.nil? object diff --git a/lib/active_graph/node/has_n.rb b/lib/active_graph/node/has_n.rb index 00ce6a7e9..7140dbf80 100644 --- a/lib/active_graph/node/has_n.rb +++ b/lib/active_graph/node/has_n.rb @@ -111,13 +111,17 @@ def init_cache def add_to_cache(object, rel = nil) (@cached_rels ||= []) << rel if rel - (@cached_result ||= []).tap { |results| results << object if object && !results.include?(object) } + (@cached_result ||= []).tap { |results| results << object if !results.include?(object) } if object end def rels @cached_rels || super.tap { |rels| rels.each { |rel| add_to_cache(nil, rel) } } end + def rel + rels.first + end + def cache_query_proxy_result (result_cache_proc_cache || @query_proxy).to_a.tap { |result| cache_result(result) } end diff --git a/lib/active_graph/node/has_n/association.rb b/lib/active_graph/node/has_n/association.rb index d8d399b9e..b01f71680 100644 --- a/lib/active_graph/node/has_n/association.rb +++ b/lib/active_graph/node/has_n/association.rb @@ -1,6 +1,3 @@ -require 'active_support/inflector/inflections' -require 'active_graph/class_arguments' - module ActiveGraph module Node module HasN diff --git a/lib/active_graph/node/labels.rb b/lib/active_graph/node/labels.rb index 2658c5fa5..aaeb0977a 100644 --- a/lib/active_graph/node/labels.rb +++ b/lib/active_graph/node/labels.rb @@ -1,5 +1,3 @@ -require 'active_graph/core/label' - module ActiveGraph module Node # Provides a mapping between neo4j labels and Ruby classes @@ -76,7 +74,7 @@ def self.model_for_labels(labels) def self.clear_wrapped_models MODELS_FOR_LABELS_CACHE.clear - ActiveGraph::NodeWrapping::CONSTANTS_FOR_LABELS_CACHE.clear + ActiveGraph::Node::Wrapping::CONSTANTS_FOR_LABELS_CACHE.clear end module ClassMethods diff --git a/lib/active_graph/node/node_wrapper.rb b/lib/active_graph/node/node_wrapper.rb deleted file mode 100644 index 71866fd32..000000000 --- a/lib/active_graph/node/node_wrapper.rb +++ /dev/null @@ -1,54 +0,0 @@ -require 'active_support/inflector' -require 'active_graph/core/node' - -wrapping_proc = proc do |node| - found_class = ActiveGraph::NodeWrapping.class_to_wrap(node.labels) - next node if not found_class - - found_class.new.tap do |wrapped_node| - wrapped_node.init_on_load(node, node.properties) - end -end -Neo4j::Driver::Types::Node.wrapper_callback(wrapping_proc) - -module ActiveGraph - module NodeWrapping - # Only load classes once for performance - CONSTANTS_FOR_LABELS_CACHE = {} - - class << self - def class_to_wrap(labels) - load_classes_from_labels(labels) - ActiveGraph::Node::Labels.model_for_labels(labels).tap do |model_class| - populate_constants_for_labels_cache(model_class, labels) - end - end - - private - - def load_classes_from_labels(labels) - labels.each { |label| constant_for_label(label) } - end - - def constant_for_label(label) - CONSTANTS_FOR_LABELS_CACHE[label] || CONSTANTS_FOR_LABELS_CACHE[label] = constantized_label(label) - end - - def constantized_label(label) - "#{association_model_namespace}::#{label}".constantize - rescue NameError, LoadError - nil - end - - def populate_constants_for_labels_cache(model_class, labels) - labels.each do |label| - CONSTANTS_FOR_LABELS_CACHE[label] = model_class if CONSTANTS_FOR_LABELS_CACHE[label].nil? - end - end - - def association_model_namespace - ActiveGraph::Config.association_model_namespace_string - end - end - end -end diff --git a/lib/active_graph/node/orm_adapter.rb b/lib/active_graph/node/orm_adapter.rb index d9c3978ca..5579c8fae 100644 --- a/lib/active_graph/node/orm_adapter.rb +++ b/lib/active_graph/node/orm_adapter.rb @@ -1,11 +1,5 @@ -require 'orm_adapter' - module ActiveGraph module Node - module ClassMethods - include OrmAdapter::ToAdapter - end - class OrmAdapter < ::OrmAdapter::Base module ClassMethods include ActiveModel::Callbacks diff --git a/lib/active_graph/node/query/query_proxy_link.rb b/lib/active_graph/node/query/query_proxy/link.rb similarity index 100% rename from lib/active_graph/node/query/query_proxy_link.rb rename to lib/active_graph/node/query/query_proxy/link.rb diff --git a/lib/active_graph/node/query/query_proxy_methods_of_mass_updating.rb b/lib/active_graph/node/query/query_proxy_methods_of_mass_updating.rb index e3d1ae71a..e926a3383 100644 --- a/lib/active_graph/node/query/query_proxy_methods_of_mass_updating.rb +++ b/lib/active_graph/node/query/query_proxy_methods_of_mass_updating.rb @@ -51,6 +51,7 @@ def delete_all_rels def replace_with(node_or_nodes) node_or_nodes = Array(node_or_nodes).map { |arg| arg.is_a?(ActiveGraph::Node) ? arg : @model.find(arg) } original_ids = self.pluck(:id) + ActiveGraph::Base.lock_node(start_object) unless start_object.new_record? delete_rels_for_nodes(original_ids, node_or_nodes.collect(&:id)) add_rels(node_or_nodes, original_ids) end diff --git a/lib/active_graph/node/scope.rb b/lib/active_graph/node/scope.rb index af00fb089..209657897 100644 --- a/lib/active_graph/node/scope.rb +++ b/lib/active_graph/node/scope.rb @@ -1,9 +1,11 @@ -require 'active_support/per_thread_registry' - module ActiveGraph::Node module Scope extend ActiveSupport::Concern + included do + thread_mattr_accessor :current_scope + end + module ClassMethods # Similar to ActiveRecord scope # @@ -86,14 +88,6 @@ def _call_scope_context(eval_context, *query_params, **kwargs, &proc) end end - def current_scope #:nodoc: - ScopeRegistry.value_for(:current_scope, base_class.to_s) - end - - def current_scope=(scope) #:nodoc: - ScopeRegistry.set_value_for(:current_scope, base_class.to_s, scope) - end - def all(new_var = nil) var = new_var || (current_scope ? current_scope.node_identity : :n) if current_scope @@ -139,38 +133,5 @@ def query_proxy_or_target @query_proxy_or_target ||= @query_proxy || @target end end - - - # Stolen from ActiveRecord - # https://github.com/rails/rails/blob/08754f12e65a9ec79633a605e986d0f1ffa4b251/activerecord/lib/active_record/scoping.rb#L57 - class ScopeRegistry # :nodoc: - extend ActiveSupport::PerThreadRegistry - - VALID_SCOPE_TYPES = [:current_scope, :ignore_default_scope] - - def initialize - @registry = Hash.new { |hash, key| hash[key] = {} } - end - - # Obtains the value for a given +scope_name+ and +variable_name+. - def value_for(scope_type, variable_name) - raise_invalid_scope_type!(scope_type) - @registry[scope_type][variable_name] - end - - # Sets the +value+ for a given +scope_type+ and +variable_name+. - def set_value_for(scope_type, variable_name, value) - raise_invalid_scope_type!(scope_type) - @registry[scope_type][variable_name] = value - end - - private - - def raise_invalid_scope_type!(scope_type) - return if VALID_SCOPE_TYPES.include?(scope_type) - - fail ArgumentError, "Invalid scope type '#{scope_type}' sent to the registry. Scope types must be included in VALID_SCOPE_TYPES" - end - end end end diff --git a/lib/active_graph/node/wrapping.rb b/lib/active_graph/node/wrapping.rb new file mode 100644 index 000000000..2aa69bd82 --- /dev/null +++ b/lib/active_graph/node/wrapping.rb @@ -0,0 +1,52 @@ +module ActiveGraph + module Node + module Wrapping + # Only load classes once for performance + CONSTANTS_FOR_LABELS_CACHE = {} + + class << self + def wrapper(node) + found_class = class_to_wrap(node.labels) + return node unless found_class + + found_class.new.tap do |wrapped_node| + wrapped_node.init_on_load(node, node.properties) + end + end + + def class_to_wrap(labels) + load_classes_from_labels(labels) + ActiveGraph::Node::Labels.model_for_labels(labels).tap do |model_class| + populate_constants_for_labels_cache(model_class, labels) + end + end + + private + + def load_classes_from_labels(labels) + labels.each { |label| constant_for_label(label) } + end + + def constant_for_label(label) + CONSTANTS_FOR_LABELS_CACHE[label] ||= constantized_label(label) + end + + def constantized_label(label) + "#{association_model_namespace}::#{label}".constantize + rescue NameError, LoadError + nil + end + + def populate_constants_for_labels_cache(model_class, labels) + labels.each do |label| + CONSTANTS_FOR_LABELS_CACHE[label] ||= model_class + end + end + + def association_model_namespace + ActiveGraph::Config.association_model_namespace_string + end + end + end + end +end diff --git a/lib/active_graph/railtie.rb b/lib/active_graph/railtie.rb index af3700d71..7f94a7c7b 100644 --- a/lib/active_graph/railtie.rb +++ b/lib/active_graph/railtie.rb @@ -1,7 +1,11 @@ -require 'active_support/notifications' -require 'rails/railtie' # Need the action_dispatch railtie to have action_dispatch.rescue_responses initialized correctly require 'action_dispatch/railtie' +require 'rails/generators' +require 'rails/generators/active_model' +require 'rails/generators/named_base' +require 'rails/railtie' +require File.expand_path('../rails/generators/migration_helper.rb', __dir__) +Rails::Generators::GeneratedAttribute.include ActiveGraph::Generators::GeneratedAttribute require 'active_graph' module ActiveGraph diff --git a/lib/active_graph/relationship/property.rb b/lib/active_graph/relationship/property.rb index 69214dda0..31c24883a 100644 --- a/lib/active_graph/relationship/property.rb +++ b/lib/active_graph/relationship/property.rb @@ -1,5 +1,3 @@ -require 'active_graph/class_arguments' - module ActiveGraph::Relationship module Property extend ActiveSupport::Concern diff --git a/lib/active_graph/relationship/rel_wrapper.rb b/lib/active_graph/relationship/rel_wrapper.rb deleted file mode 100644 index c108186cd..000000000 --- a/lib/active_graph/relationship/rel_wrapper.rb +++ /dev/null @@ -1,31 +0,0 @@ -# frozen_string_literal: true - -wrapping_proc = proc do |relationship| - ActiveGraph::RelWrapping.wrapper(relationship) -end -Neo4j::Driver::Types::Relationship.wrapper_callback(wrapping_proc) - -module ActiveGraph - module RelWrapping - class << self - def wrapper(rel) - rel.properties.symbolize_keys! - begin - most_concrete_class = class_from_type(rel.type).constantize - return rel unless most_concrete_class < ActiveGraph::Relationship - most_concrete_class.new - rescue NameError => e - raise e unless e.message =~ /(uninitialized|wrong) constant/ - - return rel - end.tap do |wrapped_rel| - wrapped_rel.init_on_load(rel, rel.start_node_id, rel.end_node_id, rel.type) - end - end - - def class_from_type(type) - ActiveGraph::Relationship::Types::WRAPPED_CLASSES[type] || ActiveGraph::Relationship::Types::WRAPPED_CLASSES[type] = type.to_s.downcase.camelize - end - end - end -end diff --git a/lib/active_graph/relationship/wrapping.rb b/lib/active_graph/relationship/wrapping.rb new file mode 100644 index 000000000..3e8932793 --- /dev/null +++ b/lib/active_graph/relationship/wrapping.rb @@ -0,0 +1,26 @@ +module ActiveGraph + module Relationship + module Wrapping + class << self + def wrapper(rel) + rel.properties.symbolize_keys! + begin + most_concrete_class = class_from_type(rel.type).constantize + return rel unless most_concrete_class < ActiveGraph::Relationship + most_concrete_class.new + rescue NameError => e + raise e unless e.message =~ /(uninitialized|wrong) constant/ + + return rel + end.tap do |wrapped_rel| + wrapped_rel.init_on_load(rel, rel.start_node_id, rel.end_node_id, rel.type) + end + end + + def class_from_type(type) + ActiveGraph::Relationship::Types::WRAPPED_CLASSES[type] || ActiveGraph::Relationship::Types::WRAPPED_CLASSES[type] = type.to_s.downcase.camelize + end + end + end + end +end diff --git a/lib/active_graph/shared/attributes.rb b/lib/active_graph/shared/attributes.rb index edde76147..b3f11d334 100644 --- a/lib/active_graph/shared/attributes.rb +++ b/lib/active_graph/shared/attributes.rb @@ -18,9 +18,10 @@ module Attributes # Methods deprecated on the Object class which can be safely overridden DEPRECATED_OBJECT_METHODS = %w(id type) + ATTRIBUTES_METHOD_PATTERNS = Gem::Requirement.create('>= 7.1').satisfied_by?(Gem.loaded_specs["activesupport"].version) ? :attribute_method_patterns : :attribute_method_matchers included do - attribute_method_suffix '' if attribute_method_matchers.none? { |matcher| matcher.prefix == '' && matcher.suffix == '' } + attribute_method_suffix '' if send(ATTRIBUTES_METHOD_PATTERNS).none? { |matcher| matcher.prefix == '' && matcher.suffix == '' } attribute_method_suffix '=' attribute_method_suffix '?' end @@ -204,7 +205,7 @@ def instance_method_already_implemented?(method_name) # Expand an attribute name into its generated methods names def attribute_methods(name) - attribute_method_matchers.map { |matcher| matcher.method_name name } + send(ATTRIBUTES_METHOD_PATTERNS).map { |matcher| matcher.method_name name } end # Ruby inherited hook to assign superclass attributes to subclasses diff --git a/lib/active_graph/shared/node_query_factory.rb b/lib/active_graph/shared/node_query_factory.rb new file mode 100644 index 000000000..c831c1a6c --- /dev/null +++ b/lib/active_graph/shared/node_query_factory.rb @@ -0,0 +1,15 @@ +module ActiveGraph::Shared + class NodeQueryFactory < QueryFactory + protected + + def match_string + "(#{identifier})" + end + + def create_query + return match_query if graph_object.persisted? + labels = graph_object.labels_for_create.map { |l| ":`#{l}`" }.join + base_query.create("(#{identifier}#{labels} $#{identifier}_params)").params(identifier_params => graph_object.props_for_create) + end + end +end diff --git a/lib/active_graph/shared/persistence.rb b/lib/active_graph/shared/persistence.rb index 4d55b0a86..131aca199 100644 --- a/lib/active_graph/shared/persistence.rb +++ b/lib/active_graph/shared/persistence.rb @@ -217,7 +217,7 @@ def cache_key if self.new_record? "#{model_cache_key}/new" elsif self.respond_to?(:updated_at) && !self.updated_at.blank? - "#{model_cache_key}/#{neo_id}-#{self.updated_at.utc.to_s(:number)}" + "#{model_cache_key}/#{neo_id}-#{self.updated_at.utc.to_fs(:number)}" else "#{model_cache_key}/#{neo_id}" end diff --git a/lib/active_graph/shared/query_factory.rb b/lib/active_graph/shared/query_factory.rb index 5a3c9f4e9..599fdeb7d 100644 --- a/lib/active_graph/shared/query_factory.rb +++ b/lib/active_graph/shared/query_factory.rb @@ -59,64 +59,4 @@ def identifier_params @identifier_params ||= "#{identifier}_params" end end - - class NodeQueryFactory < QueryFactory - protected - - def match_string - "(#{identifier})" - end - - def create_query - return match_query if graph_object.persisted? - labels = graph_object.labels_for_create.map { |l| ":`#{l}`" }.join - base_query.create("(#{identifier}#{labels} $#{identifier}_params)").params(identifier_params => graph_object.props_for_create) - end - end - - class RelQueryFactory < QueryFactory - protected - - def match_string - "(#{graph_object.from_node_identifier})-[#{identifier}]->()" - end - - def create_query - return match_query if graph_object.persisted? - create_props, set_props = filtered_props - base_query.send(graph_object.create_method, query_string(create_props)).break - .set(identifier => set_props) - .params(params(create_props)) - end - - private - - def filtered_props - ActiveGraph::Shared::FilteredHash.new(graph_object.props_for_create, graph_object.creates_unique_option).filtered_base - end - - def query_string(create_props) - "(#{graph_object.from_node_identifier})-[#{identifier}:`#{graph_object.type}` #{pattern(create_props)}]->(#{graph_object.to_node_identifier})" - end - - def params(create_props) - unique? ? create_props.transform_keys { |key| scoped(key).to_sym } : { namespace.to_sym => create_props } - end - - def unique? - graph_object.create_method == :create_unique - end - - def pattern(create_props) - unique? ? "{#{create_props.keys.map { |key| "#{key}: $#{scoped(key)}" }.join(', ')}}" : "$#{namespace}" - end - - def scoped(key) - "#{namespace}_#{key}" - end - - def namespace - "#{identifier}_create_props" - end - end end diff --git a/lib/active_graph/shared/rel_query_factory.rb b/lib/active_graph/shared/rel_query_factory.rb new file mode 100644 index 000000000..c31376fd6 --- /dev/null +++ b/lib/active_graph/shared/rel_query_factory.rb @@ -0,0 +1,47 @@ +module ActiveGraph::Shared + class RelQueryFactory < QueryFactory + protected + + def match_string + "(#{graph_object.from_node_identifier})-[#{identifier}]->()" + end + + def create_query + return match_query if graph_object.persisted? + create_props, set_props = filtered_props + base_query.send(graph_object.create_method, query_string(create_props)).break + .set(identifier => set_props) + .params(params(create_props)) + end + + private + + def filtered_props + ActiveGraph::Shared::FilteredHash.new(graph_object.props_for_create, graph_object.creates_unique_option).filtered_base + end + + def query_string(create_props) + "(#{graph_object.from_node_identifier})-[#{identifier}:`#{graph_object.type}` #{pattern(create_props)}]->(#{graph_object.to_node_identifier})" + end + + def params(create_props) + unique? ? create_props.transform_keys { |key| scoped(key).to_sym } : { namespace.to_sym => create_props } + end + + def unique? + graph_object.create_method == :create_unique + end + + def pattern(create_props) + unique? ? "{#{create_props.keys.map { |key| "#{key}: $#{scoped(key)}" }.join(', ')}}" : "$#{namespace}" + end + + def scoped(key) + "#{namespace}_#{key}" + end + + def namespace + "#{identifier}_create_props" + end + end +end diff --git a/lib/active_graph/shared/type_converters.rb b/lib/active_graph/shared/type_converters.rb index fd1bf1537..666cc4d85 100644 --- a/lib/active_graph/shared/type_converters.rb +++ b/lib/active_graph/shared/type_converters.rb @@ -1,9 +1,3 @@ -require 'date' -require 'bigdecimal' -require 'bigdecimal/util' -require 'active_support/core_ext/big_decimal/conversions' -require 'active_support/core_ext/string/conversions' - module ActiveGraph::Shared class Boolean; end diff --git a/lib/active_graph/tasks/migration.rake b/lib/active_graph/tasks/migration.rake index 08f335d75..f81be1aca 100644 --- a/lib/active_graph/tasks/migration.rake +++ b/lib/active_graph/tasks/migration.rake @@ -1,7 +1,3 @@ -require 'rake' -require 'active_support/concern' -require 'active_graph/migration' - if !defined?(Rails) && !Rake::Task.task_defined?('environment') desc 'Run a script against the database to perform system-wide changes' task :environment do diff --git a/lib/active_graph/timestamps.rb b/lib/active_graph/timestamps.rb index 8f13215be..e7e603f55 100644 --- a/lib/active_graph/timestamps.rb +++ b/lib/active_graph/timestamps.rb @@ -1,6 +1,3 @@ -require 'active_graph/timestamps/created' -require 'active_graph/timestamps/updated' - module ActiveGraph # This mixin includes timestamps in the included class module Timestamps diff --git a/lib/active_graph/transactions.rb b/lib/active_graph/transactions.rb index 44f01cf2e..3ba5e888f 100644 --- a/lib/active_graph/transactions.rb +++ b/lib/active_graph/transactions.rb @@ -28,6 +28,10 @@ def read_transaction(**config, &block) alias transaction write_transaction + def lock_node(node) + node.as(:n).query.remove('n._AGLOCK_').exec if tx&.open? + end + private def send_transaction(method, **config, &block) diff --git a/lib/active_graph/version.rb b/lib/active_graph/version.rb index 9a2bd53e2..45268098d 100644 --- a/lib/active_graph/version.rb +++ b/lib/active_graph/version.rb @@ -1,3 +1,3 @@ module ActiveGraph - VERSION = '11.4.0' + VERSION = '11.5.0.beta.2' end diff --git a/lib/active_graph/wrapper.rb b/lib/active_graph/wrapper.rb deleted file mode 100644 index 970a6a518..000000000 --- a/lib/active_graph/wrapper.rb +++ /dev/null @@ -1,4 +0,0 @@ -module ActiveGraph - module ClassWrapper - end -end diff --git a/lib/rails/generators/active_graph/migration/migration_generator.rb b/lib/rails/generators/active_graph/migration/migration_generator.rb index 753fea4b8..e91e45e3c 100644 --- a/lib/rails/generators/active_graph/migration/migration_generator.rb +++ b/lib/rails/generators/active_graph/migration/migration_generator.rb @@ -1,12 +1,11 @@ -# frozen_string_literal: true - -require File.join(File.dirname(__FILE__), '..', '..', '..', '..', 'active_graph.rb') +require_relative '../../migration_helper' +require_relative '../../source_path_helper' module ActiveGraph module Generators class MigrationGenerator < ::Rails::Generators::NamedBase - include ::ActiveGraph::Generators::SourcePathHelper - include ::ActiveGraph::Generators::MigrationHelper + include ActiveGraph::Generators::SourcePathHelper + include ActiveGraph::Generators::MigrationHelper def create_migration_file migration_template 'migration.erb' diff --git a/lib/rails/generators/active_graph/model/model_generator.rb b/lib/rails/generators/active_graph/model/model_generator.rb index de9b84a0e..45026f955 100644 --- a/lib/rails/generators/active_graph/model/model_generator.rb +++ b/lib/rails/generators/active_graph/model/model_generator.rb @@ -1,10 +1,9 @@ -# frozen_string_literal: true - -require File.join(File.dirname(__FILE__), '..', '..', '..', '..', 'active_graph.rb') +require_relative '../../migration_helper' +require_relative '../../source_path_helper' class ActiveGraph::Generators::ModelGenerator < Rails::Generators::NamedBase #:nodoc: - include ::ActiveGraph::Generators::SourcePathHelper - include ::ActiveGraph::Generators::MigrationHelper + include ActiveGraph::Generators::SourcePathHelper + include ActiveGraph::Generators::MigrationHelper argument :attributes, type: :array, default: [], banner: 'field:type field:type' diff --git a/lib/rails/generators/active_graph/upgrade_v8/upgrade_v8_generator.rb b/lib/rails/generators/active_graph/upgrade_v8/upgrade_v8_generator.rb index 5eba6ecbf..3740d37b5 100644 --- a/lib/rails/generators/active_graph/upgrade_v8/upgrade_v8_generator.rb +++ b/lib/rails/generators/active_graph/upgrade_v8/upgrade_v8_generator.rb @@ -1,12 +1,11 @@ -# frozen_string_literal: true - -require File.join(File.dirname(__FILE__), '..', '..', '..', '..', 'active_graph.rb') +require_relative '../../migration_helper' +require_relative '../../source_path_helper' module ActiveGraph module Generators class UpgradeV8Generator < ::Rails::Generators::Base - include ::ActiveGraph::Generators::SourcePathHelper - include ::ActiveGraph::Generators::MigrationHelper + include ActiveGraph::Generators::SourcePathHelper + include ActiveGraph::Generators::MigrationHelper def create_upgrade_v8_file @schema = load_all_models_schema! diff --git a/lib/rails/generators/active_graph_generator.rb b/lib/rails/generators/migration_helper.rb similarity index 84% rename from lib/rails/generators/active_graph_generator.rb rename to lib/rails/generators/migration_helper.rb index 9081069d2..5a2df2875 100644 --- a/lib/rails/generators/active_graph_generator.rb +++ b/lib/rails/generators/migration_helper.rb @@ -1,8 +1,3 @@ -# frozen_string_literal: true - -require 'rails/generators/named_base' -require 'rails/generators/active_model' - module ActiveGraph module Generators #:nodoc: end @@ -55,18 +50,6 @@ def migration_template(template_name, prefix = '') end end -module ActiveGraph::Generators::SourcePathHelper - extend ActiveSupport::Concern - - module ClassMethods - def source_root - @_neo4j_source_root ||= File.expand_path(File.join(File.dirname(__FILE__), - 'active_graph', generator_name, 'templates')) - end - end -end - - class ActiveGraph::Generators::ActiveModel < Rails::Generators::ActiveModel #:nodoc: def self.all(klass) "#{klass}.all" @@ -101,10 +84,9 @@ def destroy end end - -module Rails +module ActiveGraph module Generators - class GeneratedAttribute #:nodoc: + module GeneratedAttribute #:nodoc: def type_class case type.to_s.downcase when 'any' then 'any' diff --git a/lib/rails/generators/source_path_helper.rb b/lib/rails/generators/source_path_helper.rb new file mode 100644 index 000000000..49f3f9145 --- /dev/null +++ b/lib/rails/generators/source_path_helper.rb @@ -0,0 +1,10 @@ +module ActiveGraph::Generators::SourcePathHelper + extend ActiveSupport::Concern + + module ClassMethods + def source_root + @_neo4j_source_root ||= File.expand_path(File.join(File.dirname(__FILE__), + 'active_graph', generator_name, 'templates')) + end + end +end diff --git a/spec/active_graph/base_spec.rb b/spec/active_graph/base_spec.rb index b8cbb4949..6e5ae5308 100644 --- a/spec/active_graph/base_spec.rb +++ b/spec/active_graph/base_spec.rb @@ -1,5 +1,3 @@ -require 'spec_helper' - describe ActiveGraph::Base do before { described_class.query('MATCH (n) OPTIONAL MATCH (n)-[r]-() DELETE n, r') } @@ -286,7 +284,7 @@ def get_object_by_id(id) klass = Neo4j::Driver::Types.const_get(core_class) @procs[core_class] = klass.instance_variable_get(:@wrapper_callback) klass.clear_wrapper_callback - klass.wrapper_callback(WrapperClass.method(:new)) + klass.wrapper_callback(&WrapperClass.method(:new)) end end diff --git a/spec/active_graph/core/cypher_error_spec.rb b/spec/active_graph/core/cypher_error_spec.rb index cf231ec9b..9a4f9554a 100644 --- a/spec/active_graph/core/cypher_error_spec.rb +++ b/spec/active_graph/core/cypher_error_spec.rb @@ -1,5 +1,3 @@ -require 'spec_helper' - module ActiveGraph module Core describe CypherError do diff --git a/spec/active_graph/core/query_parameters_spec.rb b/spec/active_graph/core/query_parameters_spec.rb index 8f13ba7f0..b467c7ab2 100644 --- a/spec/active_graph/core/query_parameters_spec.rb +++ b/spec/active_graph/core/query_parameters_spec.rb @@ -1,5 +1,3 @@ -require 'spec_helper' - describe ActiveGraph::Core::Query::Parameters do let(:parameters) { ActiveGraph::Core::Query::Parameters.new } diff --git a/spec/active_graph/core/query_spec.rb b/spec/active_graph/core/query_spec.rb index 57722eb13..ce924a02c 100644 --- a/spec/active_graph/core/query_spec.rb +++ b/spec/active_graph/core/query_spec.rb @@ -1,5 +1,3 @@ -require 'spec_helper' - class DocGenerator QUERY_DOCS_PATH = File.join('..', 'neo4j', 'docs') diff --git a/spec/active_graph/core/wrappable_spec.rb b/spec/active_graph/core/wrappable_spec.rb index 2c861ddb8..65790f826 100644 --- a/spec/active_graph/core/wrappable_spec.rb +++ b/spec/active_graph/core/wrappable_spec.rb @@ -1,6 +1,3 @@ -require 'spec_helper' -require 'active_graph/core/wrappable' - describe ActiveGraph::Core::Wrappable do before do stub_const 'WrapperClass', (Class.new do @@ -18,10 +15,10 @@ def initialize(obj) describe '.wrapper_callback' do it 'does not allow for two callbacks' do - WrappableClass.wrapper_callback(->(obj) { WrapperClass.new(obj) }) + WrappableClass.wrapper_callback(&WrapperClass.method(:new)) expect do - WrappableClass.wrapper_callback(-> {}) + WrappableClass.wrapper_callback {} end.to raise_error(/Callback already specified!/) end @@ -31,7 +28,7 @@ def initialize(obj) end it 'allow users to specify a callback which will create a wrapper object' do - WrappableClass.wrapper_callback(->(obj) { WrapperClass.new(obj) }) + WrappableClass.wrapper_callback(&WrapperClass.method(:new)) obj = WrappableClass.new wrapper_obj = obj.wrap diff --git a/spec/active_graph/transactions_spec.rb b/spec/active_graph/transactions_spec.rb index afed3c6ab..93dd0a2b0 100644 --- a/spec/active_graph/transactions_spec.rb +++ b/spec/active_graph/transactions_spec.rb @@ -1,5 +1,3 @@ -require 'spec_helper' - describe ActiveGraph::Transactions do let(:read_query) { 'RETURN 1' } let(:write_query) { 'CREATE (n:Student) RETURN count(n)' } diff --git a/spec/e2e/association_proxy_spec.rb b/spec/e2e/association_proxy_spec.rb index 6ed861b28..5f7b9339f 100644 --- a/spec/e2e/association_proxy_spec.rb +++ b/spec/e2e/association_proxy_spec.rb @@ -12,12 +12,13 @@ has_one :out, :favorite_lesson, type: nil, model_class: :Lesson has_many :out, :homework, type: :HOMEWORK, model_class: %w[Lesson Exam] has_many :out, :friends, type: :friend, model_class: :Student + has_one :out, :school, rel_class: :SchoolRel end stub_relationship_class('LessonEnrollment') do from_class :Student to_class :Lesson - type :has_studet + type :has_student property :grade end @@ -34,6 +35,17 @@ has_many :in, :lessons, model_class: :Lesson, origin: :exams_given has_many :out, :students, type: :has_student, model_class: :Student end + + stub_relationship_class('SchoolRel') do + from_class :Student + to_class :School + type :school + end + + stub_node_class('School') do + property :name + has_many :out, :students, type: :school, model_class: :School + end end let(:billy) { Student.create(name: 'Billy') } @@ -44,6 +56,7 @@ let(:science_exam2) { Exam.create(name: 'Science Exam 2') } let(:leszek) { Student.create(name: 'Leszek', friends: [zinto]) } let(:zinto) { Student.create(name: 'Zinto') } + let(:code_academy) { School.create(name: 'Code Academy') } before do [math, science].each { |lesson| billy.lessons << lesson } @@ -52,6 +65,7 @@ science.exams_given << science_exam science.exams_given << science_exam2 billy.favorite_lesson = math + billy.school = code_academy end context 'self referencing relationships' do @@ -297,7 +311,6 @@ end context 'when requiring "active_support/core_ext/enumerable"' do - require 'active_support/core_ext/enumerable' let(:lessons) { billy.lessons } it 'uses the correct `pluck` method' do @@ -314,11 +327,17 @@ end describe '#rels' do - it 'caches results for consecutive calls' do + it 'caches multi rels for consecutive calls' do expect_queries(1) do 2.times { billy.lessons.rels } end end + + it 'caches single rel for consecutive calls' do + expect_queries(1) do + 2.times { billy.school(chainable: true).rel } + end + end end describe '#inspect' do diff --git a/spec/e2e/generators_spec.rb b/spec/e2e/generators_spec.rb index a5bc94560..668fa77ed 100644 --- a/spec/e2e/generators_spec.rb +++ b/spec/e2e/generators_spec.rb @@ -1,5 +1,4 @@ require 'rails/generators' -require 'rails/generators/active_graph_generator' require 'rails/generators/active_graph/model/model_generator' require 'rails/generators/active_graph/migration/migration_generator' require 'rails/generators/active_graph/upgrade_v8/upgrade_v8_generator' diff --git a/spec/e2e/migration_spec.rb b/spec/e2e/migration_spec.rb index ee81fe8df..2b72bd61a 100644 --- a/spec/e2e/migration_spec.rb +++ b/spec/e2e/migration_spec.rb @@ -1,4 +1,3 @@ - describe 'migration tasks' do let_env_variable('MIGRATIONS_SILENCED') { 'true' } diff --git a/spec/e2e/node_spec.rb b/spec/e2e/node_spec.rb index f9f1b5cc0..3cced63e3 100644 --- a/spec/e2e/node_spec.rb +++ b/spec/e2e/node_spec.rb @@ -739,7 +739,7 @@ def true_results?(node, verb) let(:model) { IceLolly.create(flavour: 'vanilla', required_on_create: true, required_on_update: true) } it 'should respond with a valid cache key' do - expect(model.cache_key).to eq "#{model.class.model_name.cache_key}/#{model.neo_id}-#{model.updated_at.utc.to_s(:number)}" + expect(model.cache_key).to eq "#{model.class.model_name.cache_key}/#{model.neo_id}-#{model.updated_at.utc.to_fs(:number)}" end context 'when changed' do diff --git a/spec/e2e/query_spec.rb b/spec/e2e/query_spec.rb index 1af2cb8c2..f3bd7d80b 100644 --- a/spec/e2e/query_spec.rb +++ b/spec/e2e/query_spec.rb @@ -1,5 +1,3 @@ -require 'set' - describe 'Query API' do before(:each) do clear_model_memory_caches diff --git a/spec/e2e/railtie_spec.rb b/spec/e2e/railtie_spec.rb index a06ea7871..20e7a4a3f 100644 --- a/spec/e2e/railtie_spec.rb +++ b/spec/e2e/railtie_spec.rb @@ -1,9 +1,7 @@ -require 'ostruct' +require 'active_graph/railtie' module Rails describe 'railtie' do - require 'active_graph/railtie' - after(:context) { set_default_driver } describe '#setup!' do diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 8bca23393..3fd14c0ae 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -21,15 +21,18 @@ end require 'bundler/setup' -require 'rspec' require 'its' require 'fileutils' require 'tmpdir' require 'logger' -require 'active_graph/core' +require 'active_support/core_ext/enumerable' + +require 'rails' require 'active_graph' require 'unique_class' +require 'ostruct' +require 'rspec' require 'pry' if ENV['APP_ENV'] == 'debug' diff --git a/spec/unit/load_hooks_spec.rb b/spec/unit/load_hooks_spec.rb index 7d3d2c8d4..ce3d0977b 100644 --- a/spec/unit/load_hooks_spec.rb +++ b/spec/unit/load_hooks_spec.rb @@ -1,6 +1,4 @@ describe 'load hooks' do - require 'active_support' - module HookedIn def hooked_in; end end diff --git a/spec/unit/migrations/schema_spec.rb b/spec/unit/migrations/schema_spec.rb index dc48348fc..0e2d0291b 100644 --- a/spec/unit/migrations/schema_spec.rb +++ b/spec/unit/migrations/schema_spec.rb @@ -1,5 +1,3 @@ -require 'active_graph/migrations/schema' - describe ActiveGraph::Migrations::Schema do before { delete_schema } diff --git a/spec/unit/node/validation_spec.rb b/spec/unit/node/validation_spec.rb index 4edf55022..c9bc1fd47 100644 --- a/spec/unit/node/validation_spec.rb +++ b/spec/unit/node/validation_spec.rb @@ -41,8 +41,8 @@ def self.fetch_upstream_primitive(_attr) o.serialized_properties allow(clazz).to receive(:default_property_values).and_return({}) expect(node).to receive(:properties).and_return(name: 'kalle2', age: '43') - expect(o).to receive(:_create_node).with(name: 'kalle', age: 42).and_return(node) - expect(o).to receive(:init_on_load).with(node, age: '43', name: 'kalle2') + expect(o).to receive(:_create_node).with({ name: 'kalle', age: 42 }).and_return(node) + expect(o).to receive(:init_on_load).with(node, { age: '43', name: 'kalle2' }) allow(Object).to receive(:serialized_properties_keys).and_return([]) expect(o.save).to be true end diff --git a/spec/unit/relationship/rel_wrapper_spec.rb b/spec/unit/relationship/rel_wrapper_spec.rb index cb6bc2900..52d082f2a 100644 --- a/spec/unit/relationship/rel_wrapper_spec.rb +++ b/spec/unit/relationship/rel_wrapper_spec.rb @@ -1,4 +1,4 @@ -describe ActiveGraph::RelWrapping do +describe ActiveGraph::Relationship::Wrapping do let(:id) { 1 } let(:type) { :DEFAULT } let(:properties) { {} } @@ -6,7 +6,7 @@ let(:end_node_id) { 2 } let(:rel) { double(start_node_id: start_node_id, end_node_id: end_node_id, type: type, properties: properties) } - subject { ActiveGraph::RelWrapping.wrapper(rel) } + subject { ActiveGraph::Relationship::Wrapping.wrapper(rel) } it { should eq(rel) }