activegraph 10.0.0.pre.alpha.6

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.
Files changed (142) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +1989 -0
  3. data/CONTRIBUTORS +12 -0
  4. data/Gemfile +24 -0
  5. data/README.md +107 -0
  6. data/bin/rake +17 -0
  7. data/config/locales/en.yml +5 -0
  8. data/config/neo4j/add_classnames.yml +1 -0
  9. data/config/neo4j/config.yml +38 -0
  10. data/lib/neo4j.rb +116 -0
  11. data/lib/neo4j/active_base.rb +89 -0
  12. data/lib/neo4j/active_node.rb +108 -0
  13. data/lib/neo4j/active_node/callbacks.rb +8 -0
  14. data/lib/neo4j/active_node/dependent.rb +11 -0
  15. data/lib/neo4j/active_node/dependent/association_methods.rb +49 -0
  16. data/lib/neo4j/active_node/dependent/query_proxy_methods.rb +51 -0
  17. data/lib/neo4j/active_node/enum.rb +26 -0
  18. data/lib/neo4j/active_node/has_n.rb +612 -0
  19. data/lib/neo4j/active_node/has_n/association.rb +278 -0
  20. data/lib/neo4j/active_node/has_n/association/rel_factory.rb +61 -0
  21. data/lib/neo4j/active_node/has_n/association/rel_wrapper.rb +23 -0
  22. data/lib/neo4j/active_node/has_n/association_cypher_methods.rb +108 -0
  23. data/lib/neo4j/active_node/id_property.rb +224 -0
  24. data/lib/neo4j/active_node/id_property/accessor.rb +62 -0
  25. data/lib/neo4j/active_node/initialize.rb +21 -0
  26. data/lib/neo4j/active_node/labels.rb +207 -0
  27. data/lib/neo4j/active_node/labels/index.rb +37 -0
  28. data/lib/neo4j/active_node/labels/reloading.rb +21 -0
  29. data/lib/neo4j/active_node/node_list_formatter.rb +13 -0
  30. data/lib/neo4j/active_node/node_wrapper.rb +54 -0
  31. data/lib/neo4j/active_node/orm_adapter.rb +82 -0
  32. data/lib/neo4j/active_node/persistence.rb +187 -0
  33. data/lib/neo4j/active_node/property.rb +60 -0
  34. data/lib/neo4j/active_node/query.rb +76 -0
  35. data/lib/neo4j/active_node/query/query_proxy.rb +374 -0
  36. data/lib/neo4j/active_node/query/query_proxy_eager_loading.rb +177 -0
  37. data/lib/neo4j/active_node/query/query_proxy_eager_loading/association_tree.rb +75 -0
  38. data/lib/neo4j/active_node/query/query_proxy_enumerable.rb +110 -0
  39. data/lib/neo4j/active_node/query/query_proxy_find_in_batches.rb +19 -0
  40. data/lib/neo4j/active_node/query/query_proxy_link.rb +139 -0
  41. data/lib/neo4j/active_node/query/query_proxy_methods.rb +302 -0
  42. data/lib/neo4j/active_node/query/query_proxy_methods_of_mass_updating.rb +86 -0
  43. data/lib/neo4j/active_node/query_methods.rb +68 -0
  44. data/lib/neo4j/active_node/reflection.rb +86 -0
  45. data/lib/neo4j/active_node/rels.rb +11 -0
  46. data/lib/neo4j/active_node/scope.rb +166 -0
  47. data/lib/neo4j/active_node/unpersisted.rb +48 -0
  48. data/lib/neo4j/active_node/validations.rb +59 -0
  49. data/lib/neo4j/active_rel.rb +67 -0
  50. data/lib/neo4j/active_rel/callbacks.rb +15 -0
  51. data/lib/neo4j/active_rel/initialize.rb +28 -0
  52. data/lib/neo4j/active_rel/persistence.rb +134 -0
  53. data/lib/neo4j/active_rel/persistence/query_factory.rb +95 -0
  54. data/lib/neo4j/active_rel/property.rb +95 -0
  55. data/lib/neo4j/active_rel/query.rb +101 -0
  56. data/lib/neo4j/active_rel/rel_wrapper.rb +31 -0
  57. data/lib/neo4j/active_rel/related_node.rb +87 -0
  58. data/lib/neo4j/active_rel/types.rb +82 -0
  59. data/lib/neo4j/active_rel/validations.rb +8 -0
  60. data/lib/neo4j/ansi.rb +14 -0
  61. data/lib/neo4j/class_arguments.rb +39 -0
  62. data/lib/neo4j/config.rb +135 -0
  63. data/lib/neo4j/core.rb +14 -0
  64. data/lib/neo4j/core/connection_failed_error.rb +6 -0
  65. data/lib/neo4j/core/cypher_error.rb +37 -0
  66. data/lib/neo4j/core/driver.rb +66 -0
  67. data/lib/neo4j/core/has_uri.rb +63 -0
  68. data/lib/neo4j/core/instrumentable.rb +36 -0
  69. data/lib/neo4j/core/label.rb +158 -0
  70. data/lib/neo4j/core/logging.rb +44 -0
  71. data/lib/neo4j/core/node.rb +23 -0
  72. data/lib/neo4j/core/querable.rb +88 -0
  73. data/lib/neo4j/core/query.rb +487 -0
  74. data/lib/neo4j/core/query_builder.rb +32 -0
  75. data/lib/neo4j/core/query_clauses.rb +727 -0
  76. data/lib/neo4j/core/query_ext.rb +24 -0
  77. data/lib/neo4j/core/query_find_in_batches.rb +49 -0
  78. data/lib/neo4j/core/relationship.rb +13 -0
  79. data/lib/neo4j/core/responses.rb +50 -0
  80. data/lib/neo4j/core/result.rb +33 -0
  81. data/lib/neo4j/core/schema.rb +30 -0
  82. data/lib/neo4j/core/schema_errors.rb +12 -0
  83. data/lib/neo4j/core/wrappable.rb +30 -0
  84. data/lib/neo4j/errors.rb +57 -0
  85. data/lib/neo4j/migration.rb +148 -0
  86. data/lib/neo4j/migrations.rb +27 -0
  87. data/lib/neo4j/migrations/base.rb +77 -0
  88. data/lib/neo4j/migrations/check_pending.rb +20 -0
  89. data/lib/neo4j/migrations/helpers.rb +105 -0
  90. data/lib/neo4j/migrations/helpers/id_property.rb +75 -0
  91. data/lib/neo4j/migrations/helpers/relationships.rb +66 -0
  92. data/lib/neo4j/migrations/helpers/schema.rb +51 -0
  93. data/lib/neo4j/migrations/migration_file.rb +24 -0
  94. data/lib/neo4j/migrations/runner.rb +195 -0
  95. data/lib/neo4j/migrations/schema.rb +44 -0
  96. data/lib/neo4j/migrations/schema_migration.rb +14 -0
  97. data/lib/neo4j/model_schema.rb +139 -0
  98. data/lib/neo4j/paginated.rb +27 -0
  99. data/lib/neo4j/railtie.rb +105 -0
  100. data/lib/neo4j/schema/operation.rb +102 -0
  101. data/lib/neo4j/shared.rb +60 -0
  102. data/lib/neo4j/shared/attributes.rb +216 -0
  103. data/lib/neo4j/shared/callbacks.rb +68 -0
  104. data/lib/neo4j/shared/cypher.rb +37 -0
  105. data/lib/neo4j/shared/declared_properties.rb +204 -0
  106. data/lib/neo4j/shared/declared_property.rb +109 -0
  107. data/lib/neo4j/shared/declared_property/index.rb +37 -0
  108. data/lib/neo4j/shared/enum.rb +167 -0
  109. data/lib/neo4j/shared/filtered_hash.rb +79 -0
  110. data/lib/neo4j/shared/identity.rb +34 -0
  111. data/lib/neo4j/shared/initialize.rb +64 -0
  112. data/lib/neo4j/shared/marshal.rb +23 -0
  113. data/lib/neo4j/shared/mass_assignment.rb +64 -0
  114. data/lib/neo4j/shared/permitted_attributes.rb +28 -0
  115. data/lib/neo4j/shared/persistence.rb +282 -0
  116. data/lib/neo4j/shared/property.rb +240 -0
  117. data/lib/neo4j/shared/query_factory.rb +102 -0
  118. data/lib/neo4j/shared/rel_type_converters.rb +43 -0
  119. data/lib/neo4j/shared/serialized_properties.rb +30 -0
  120. data/lib/neo4j/shared/type_converters.rb +433 -0
  121. data/lib/neo4j/shared/typecasted_attributes.rb +98 -0
  122. data/lib/neo4j/shared/typecaster.rb +53 -0
  123. data/lib/neo4j/shared/validations.rb +44 -0
  124. data/lib/neo4j/tasks/migration.rake +202 -0
  125. data/lib/neo4j/timestamps.rb +11 -0
  126. data/lib/neo4j/timestamps/created.rb +9 -0
  127. data/lib/neo4j/timestamps/updated.rb +9 -0
  128. data/lib/neo4j/transaction.rb +139 -0
  129. data/lib/neo4j/type_converters.rb +7 -0
  130. data/lib/neo4j/undeclared_properties.rb +53 -0
  131. data/lib/neo4j/version.rb +3 -0
  132. data/lib/neo4j/wrapper.rb +4 -0
  133. data/lib/rails/generators/neo4j/migration/migration_generator.rb +14 -0
  134. data/lib/rails/generators/neo4j/migration/templates/migration.erb +9 -0
  135. data/lib/rails/generators/neo4j/model/model_generator.rb +88 -0
  136. data/lib/rails/generators/neo4j/model/templates/migration.erb +9 -0
  137. data/lib/rails/generators/neo4j/model/templates/model.erb +15 -0
  138. data/lib/rails/generators/neo4j/upgrade_v8/templates/migration.erb +17 -0
  139. data/lib/rails/generators/neo4j/upgrade_v8/upgrade_v8_generator.rb +32 -0
  140. data/lib/rails/generators/neo4j_generator.rb +119 -0
  141. data/neo4j.gemspec +51 -0
  142. metadata +421 -0
@@ -0,0 +1,98 @@
1
+ module Neo4j::Shared
2
+ # TypecastedAttributes allows types to be declared for your attributes
3
+ #
4
+ # Types are declared by passing the :type option to the attribute class
5
+ # method. After a type is declared, attribute readers will convert any
6
+ # assigned attribute value to the declared type. If the assigned value
7
+ # cannot be cast, nil will be returned instead. You can access the original
8
+ # assigned value using the before_type_cast methods.
9
+ #
10
+ # See {Typecasting} for the currently supported types.
11
+ #
12
+ # @example Usage
13
+ # class Person
14
+ # include Neo4j::Shared::TypecastedAttributes
15
+ # attribute :age, :type => Integer
16
+ # end
17
+ #
18
+ # person = Person.new
19
+ # person.age = "29"
20
+ # person.age #=> 29
21
+ # person.age_before_type_cast #=> "29"
22
+ #
23
+ # Originally part of ActiveAttr, https://github.com/cgriego/active_attr
24
+ module TypecastedAttributes
25
+ extend ActiveSupport::Concern
26
+ include Neo4j::Shared::Attributes
27
+
28
+ included do
29
+ attribute_method_suffix '_before_type_cast'
30
+ end
31
+
32
+ # Read the raw attribute value
33
+ #
34
+ # @example Reading a raw age value
35
+ # person.age = "29"
36
+ # person.attribute_before_type_cast(:age) #=> "29"
37
+ #
38
+ # @param [String, Symbol, #to_s] name Attribute name
39
+ #
40
+ # @return [Object, nil] The attribute value before typecasting
41
+ def attribute_before_type_cast(name)
42
+ @attributes ||= {}
43
+ @attributes[name.to_s]
44
+ end
45
+
46
+ private
47
+
48
+ # Reads the attribute and typecasts the result
49
+ def attribute(name)
50
+ typecast_attribute(_attribute_typecaster(name), super)
51
+ end
52
+
53
+ def typecast_attribute(typecaster, value)
54
+ self.class.typecast_attribute(typecaster, value)
55
+ end
56
+
57
+ # Calculates an attribute type
58
+ #
59
+ # @private
60
+ def _attribute_type(attribute_name)
61
+ self.class._attribute_type(attribute_name)
62
+ end
63
+
64
+ # Resolve an attribute typecaster
65
+ #
66
+ # @private
67
+ def _attribute_typecaster(attribute_name)
68
+ type = _attribute_type(attribute_name)
69
+ caster = self.class.attributes[attribute_name].typecaster || Neo4j::Shared::TypeConverters.typecaster_for(type)
70
+ caster || fail(Neo4j::UnknownTypeConverterError, "Unable to cast to type #{type}")
71
+ end
72
+
73
+ module ClassMethods
74
+ # Returns the class name plus its attribute names and types
75
+ #
76
+ # @example Inspect the model's definition.
77
+ # Person.inspect
78
+ #
79
+ # @return [String] Human-readable presentation of the attributes
80
+ def inspect
81
+ inspected_attributes = attribute_names.sort.map { |name| "#{name}: #{_attribute_type(name)}" }
82
+ attributes_list = "(#{inspected_attributes.join(', ')})" unless inspected_attributes.empty?
83
+ "#{name}#{attributes_list}"
84
+ end
85
+
86
+ # Calculates an attribute type
87
+ #
88
+ # @private
89
+ def _attribute_type(attribute_name)
90
+ attributes[attribute_name].type || Object
91
+ end
92
+
93
+ def typecast_attribute(typecaster, value)
94
+ Neo4j::Shared::TypeConverters.typecast_attribute(typecaster, value)
95
+ end
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,53 @@
1
+ module Neo4j
2
+ module Shared
3
+ # This module provides a convenient way of registering a custom Typecasting class. Custom Typecasters all follow a simple pattern.
4
+ #
5
+ # EXAMPLE:
6
+ #
7
+ # .. code-block:: ruby
8
+ #
9
+ # class RangeConverter
10
+ # class << self
11
+ # def primitive_type
12
+ # String
13
+ # end
14
+ #
15
+ # def convert_type
16
+ # Range
17
+ # end
18
+ #
19
+ # def to_db(value)
20
+ # value.to_s
21
+ # end
22
+ #
23
+ # def to_ruby(value)
24
+ # ends = value.to_s.split('..').map { |d| Integer(d) }
25
+ # ends[0]..ends[1]
26
+ # end
27
+ # alias_method :call, :to_ruby
28
+ # end
29
+ #
30
+ # include Neo4j::Shared::Typecaster
31
+ # end
32
+ #
33
+ # This would allow you to use `property :my_prop, type: Range` in a model.
34
+ # Each method and the `alias_method` call is required. Make sure the module inclusion happens at the end of the file.
35
+ #
36
+ # `primitive_type` is used to fool ActiveAttr's type converters, which only recognize a few basic Ruby classes.
37
+ #
38
+ # `convert_type` must match the constant given to the `type` option.
39
+ #
40
+ # `to_db` provides logic required to transform your value into the class defined by `primitive_type`
41
+ #
42
+ # `to_ruby` provides logic to transform the DB-provided value back into the class expected by code using the property.
43
+ # In other words, it should match the `convert_type`.
44
+ #
45
+ # Note that `alias_method` is used to make `to_ruby` respond to `call`. This is to provide compatibility with ActiveAttr.
46
+
47
+ module Typecaster
48
+ def self.included(other)
49
+ Neo4j::Shared::TypeConverters.register_converter(other)
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,44 @@
1
+ module Neo4j
2
+ module Shared
3
+ module Validations
4
+ extend ActiveSupport::Concern
5
+ include ActiveModel::Validations
6
+ # Implements the ActiveModel::Validation hook method.
7
+ # @see http://rubydoc.info/docs/rails/ActiveModel/Validations:read_attribute_for_validation
8
+ def read_attribute_for_validation(key)
9
+ respond_to?(key) ? send(key) : self[key]
10
+ end
11
+
12
+ # The validation process on save can be skipped by passing false. The regular Model#save method is
13
+ # replaced with this when the validations module is mixed in, which it is by default.
14
+ # @param [Hash] options the options to create a message with.
15
+ # @option options [true, false] :validate if false no validation will take place
16
+ # @return [Boolean] true if it saved it successfully
17
+ def save(options = {})
18
+ perform_validations(options) ? super : false
19
+ end
20
+
21
+ # @return [Boolean] true if valid
22
+ def valid?(context = nil)
23
+ context ||= (new_record? ? :create : :update)
24
+ super(context)
25
+ errors.empty?
26
+ end
27
+
28
+ private
29
+
30
+ def perform_validations(options = {})
31
+ perform_validation = case options
32
+ when Hash
33
+ options[:validate] != false
34
+ end
35
+
36
+ if perform_validation
37
+ valid?(options.is_a?(Hash) ? options[:context] : nil)
38
+ else
39
+ true
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,202 @@
1
+ require 'rake'
2
+ require 'active_support/concern'
3
+ require 'neo4j/migration'
4
+
5
+ if !defined?(Rails) && !Rake::Task.task_defined?('environment')
6
+ desc 'Run a script against the database to perform system-wide changes'
7
+ task :environment do
8
+ require 'neo4j/session_manager'
9
+ require 'ostruct'
10
+ neo4j_url = ENV['NEO4J_URL'] || 'http://localhost:7474'
11
+ $LOAD_PATH.unshift File.dirname('./')
12
+ Neo4j::ActiveBase.on_establish_session do
13
+ Neo4j::ActiveBase.new_driver(neo4j_url)
14
+ end
15
+ end
16
+ end
17
+
18
+ namespace :neo4j do
19
+ task :allow_migrations do
20
+ Neo4j::Migrations.currently_running_migrations = true
21
+ end
22
+ desc 'Run a script against the database to perform system-wide changes'
23
+ task :legacy_migrate, [:task_name, :subtask] => :environment do |_, args|
24
+ path = Rake.original_dir
25
+ migration_task = args[:task_name]
26
+ task_name_constant = migration_task.split('_').map(&:capitalize).join('')
27
+ begin
28
+ migration_class = "Neo4j::Migration::#{task_name_constant}".constantize
29
+ rescue NameError
30
+ load File.join(path, 'db', 'neo4j-migrate', "#{migration_task}.rb")
31
+ migration_class = task_name_constant.to_s.constantize
32
+ end
33
+ migration = migration_class.new(path)
34
+
35
+ subtask = args[:subtask]
36
+ if subtask
37
+ migration.send(subtask)
38
+ else
39
+ migration.migrate
40
+ end
41
+ end
42
+
43
+ desc 'A shortcut for neo4j::migrate::all'
44
+ task :migrate do
45
+ Rake::Task['neo4j:migrate:all'].invoke
46
+ end
47
+
48
+ # TODO: Make sure these tasks don't run in versions of Neo4j before 3.0
49
+ namespace :schema do
50
+ SCHEMA_YAML_PATH = 'db/neo4j/schema.yml'
51
+ SCHEMA_YAML_COMMENT = <<COMMENT
52
+ # This file is auto-generated from the current state of the database. Instead
53
+ # of editing this file, please use the migrations feature of ActiveNode to
54
+ # incrementally modify your database, and then regenerate this schema definition.
55
+ #
56
+ # Note that this schema.yml definition is the authoritative source for your
57
+ # database schema. If you need to create the application database on another
58
+ # system, you should be using neo4j:schema:load, not running all the migrations
59
+ # from scratch. The latter is a flawed and unsustainable approach (the more migrations
60
+ # you'll amass, the slower it'll run and the greater likelihood for issues).
61
+ #
62
+ # It's strongly recommended that you check this file into your version control system.
63
+
64
+ COMMENT
65
+
66
+ def check_neo4j_version_3
67
+ if Neo4j::ActiveBase.transaction.version > '3.0.0'
68
+ yield
69
+ else
70
+ puts 'WARNING: This task does not work for versions of Neo4j before 3.0.0'
71
+ end
72
+ end
73
+
74
+ desc 'Creates a db/neo4j/schema.yml file which represents the indexes / constraints in the Neo4j DB'
75
+ task dump: :environment do
76
+ check_neo4j_version_3 do
77
+ require 'neo4j/migrations/schema'
78
+
79
+ schema_data = Neo4j::Migrations::Schema.fetch_schema_data(Neo4j::ActiveBase.transaction)
80
+
81
+ runner = Neo4j::Migrations::Runner.new
82
+ schema_data[:versions] = runner.complete_migration_versions.sort
83
+
84
+ FileUtils.mkdir_p(File.dirname(SCHEMA_YAML_PATH))
85
+ File.open(SCHEMA_YAML_PATH, 'w') { |file| file << SCHEMA_YAML_COMMENT + schema_data.to_yaml }
86
+
87
+ puts "Dumped updated schema file to #{SCHEMA_YAML_PATH}"
88
+ end
89
+ end
90
+
91
+ desc "Loads a db/neo4j/schema.yml file into the database\nOptionally removes schema elements which aren't in the schema.yml file (defaults to false)"
92
+ task :load, [:remove_missing] => :environment do |_t, args|
93
+ check_neo4j_version_3 do
94
+ require 'neo4j/migrations/schema'
95
+
96
+ args.with_defaults(remove_missing: false)
97
+
98
+ schema_data = YAML.safe_load(File.read(SCHEMA_YAML_PATH), [Symbol])
99
+
100
+ Neo4j::Core::CypherSession::Adaptors::Base.subscribe_to_query(&method(:puts))
101
+
102
+ Neo4j::ActiveBase.run_transaction do
103
+ Neo4j::Migrations::Schema.synchronize_schema_data(Neo4j::ActiveBase.transaction, schema_data, args[:remove_missing])
104
+
105
+ runner = Neo4j::Migrations::Runner.new
106
+ runner.mark_versions_as_complete(schema_data[:versions]) # Run in test mode?
107
+ end
108
+ end
109
+ end
110
+ end
111
+
112
+ namespace :migrate do
113
+ desc 'Run all pending migrations'
114
+ task all: [:allow_migrations, :environment] do
115
+ runner = Neo4j::Migrations::Runner.new
116
+ runner.all
117
+
118
+ Rake::Task['neo4j:schema:dump'].invoke
119
+ end
120
+
121
+ desc 'Run a migration given its VERSION'
122
+ task up: [:allow_migrations, :environment] do
123
+ version = ENV['VERSION'] || fail(ArgumentError, 'VERSION is required')
124
+ runner = Neo4j::Migrations::Runner.new
125
+ runner.up version
126
+
127
+ Rake::Task['neo4j:schema:dump'].invoke
128
+ end
129
+
130
+ desc 'Revert a migration given its VERSION'
131
+ task down: [:allow_migrations, :environment] do
132
+ version = ENV['VERSION'] || fail(ArgumentError, 'VERSION is required')
133
+ runner = Neo4j::Migrations::Runner.new
134
+ runner.down version
135
+
136
+ Rake::Task['neo4j:schema:dump'].invoke
137
+ end
138
+
139
+ desc 'Print a report of migrations status'
140
+ task status: [:allow_migrations, :environment] do
141
+ runner = Neo4j::Migrations::Runner.new
142
+ runner.status
143
+ end
144
+
145
+ desc 'Resolve an incomplete version state'
146
+ task resolve: [:allow_migrations, :environment] do
147
+ version = ENV['VERSION'] || fail(ArgumentError, 'VERSION is required')
148
+ runner = Neo4j::Migrations::Runner.new
149
+ runner.resolve version
150
+ end
151
+
152
+ desc 'Resolve an incomplete version state'
153
+ task reset: [:allow_migrations, :environment] do
154
+ version = ENV['VERSION'] || fail(ArgumentError, 'VERSION is required')
155
+ runner = Neo4j::Migrations::Runner.new
156
+ runner.reset version
157
+ end
158
+ end
159
+
160
+ desc 'Rollbacks migrations given a STEP number'
161
+ task rollback: [:allow_migrations, :environment] do
162
+ steps = (ENV['STEP'] || 1).to_i
163
+ runner = Neo4j::Migrations::Runner.new
164
+ runner.rollback(steps)
165
+
166
+ Rake::Task['neo4j:schema:dump'].invoke
167
+ end
168
+
169
+ # Temporary to help people change to 8.0
170
+ desc 'Generates a migration for the specified constraint/index and label/property combination.'
171
+ task :generate_schema_migration, :index_or_constraint, :label, :property_name do |_t, args|
172
+ index_or_constraint, label, property_name = args.values_at(:index_or_constraint, :label, :property_name)
173
+
174
+ if !%w(index constraint).include?(index_or_constraint)
175
+ fail "Invalid schema element type: #{index_or_constraint} (should be either `index` or `constraint`)"
176
+ end
177
+ fail 'Label must be specified' if label.blank?
178
+ fail 'Property name must be specified' if property_name.blank?
179
+
180
+ migration_class_name = "ForceCreate#{label.camelize}#{property_name.camelize}#{index_or_constraint.capitalize}".gsub('::', '').underscore.camelize
181
+
182
+ require 'fileutils'
183
+ FileUtils.mkdir_p('db/neo4j/migrate')
184
+ path = File.join('db/neo4j/migrate', "#{DateTime.now.utc.strftime('%Y%m%d%H%M%S')}_#{migration_class_name.underscore}.rb")
185
+
186
+ content = <<-CONTENT
187
+ class #{migration_class_name} < Neo4j::Migrations::Base
188
+ def up
189
+ add_#{index_or_constraint} #{label.to_sym.inspect}, #{property_name.to_sym.inspect}, force: true
190
+ end
191
+
192
+ def down
193
+ drop_#{index_or_constraint} #{label.to_sym.inspect}, #{property_name.to_sym.inspect}
194
+ end
195
+ end
196
+ CONTENT
197
+
198
+ File.open(path, 'w') { |f| f << content }
199
+
200
+ puts "Generated #{path}"
201
+ end
202
+ end
@@ -0,0 +1,11 @@
1
+ require 'neo4j/timestamps/created'
2
+ require 'neo4j/timestamps/updated'
3
+
4
+ module Neo4j
5
+ # This mixin includes timestamps in the included class
6
+ module Timestamps
7
+ extend ActiveSupport::Concern
8
+ include Created
9
+ include Updated
10
+ end
11
+ end
@@ -0,0 +1,9 @@
1
+ module Neo4j
2
+ module Timestamps
3
+ # This mixin includes a created_at timestamp property
4
+ module Created
5
+ extend ActiveSupport::Concern
6
+ included { property :created_at }
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module Neo4j
2
+ module Timestamps
3
+ # This mixin includes a updated_at timestamp property
4
+ module Updated
5
+ extend ActiveSupport::Concern
6
+ included { property :updated_at }
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,139 @@
1
+ require 'active_support/core_ext/module/delegation'
2
+ require 'active_support/core_ext/module/attribute_accessors_per_thread'
3
+ require 'neo4j/core/querable'
4
+ require 'neo4j/core/schema'
5
+
6
+ module Neo4j
7
+ class Transaction
8
+ include Neo4j::Core::Querable
9
+ extend Neo4j::Core::Schema
10
+
11
+ thread_mattr_accessor :stack
12
+ attr_reader :root
13
+ attr_reader :driver_tx, :driver_session
14
+
15
+ class << self
16
+ # Runs the given block in a new transaction.
17
+ # @param [Boolean] run_in_tx if true a new transaction will not be created, instead if will simply yield to the given block
18
+ # @@yield [Neo4j::Transaction::Instance]
19
+ def run(driver, run_in_tx)
20
+ return yield(nil) unless run_in_tx
21
+
22
+ tx = Neo4j::Transaction.new
23
+ yield tx
24
+ rescue Exception => e # rubocop:disable Lint/RescueException
25
+ tx.mark_failed unless tx.nil?
26
+ raise e
27
+ ensure
28
+ tx.close unless tx.nil?
29
+ end
30
+
31
+ def root
32
+ initialized_stack.first
33
+ end
34
+
35
+ def initialized_stack
36
+ self.stack ||= []
37
+ end
38
+ end
39
+
40
+ def initialize(_options = {})
41
+ self.class.initialized_stack << self
42
+ @root = stack.first
43
+ return unless root?
44
+ @driver_session = ActiveBase.current_driver.driver.session(Neo4j::Driver::AccessMode::WRITE)
45
+ @driver_tx = @driver_session.begin_transaction
46
+ rescue StandardError => e
47
+ self.stack = []
48
+ @driver_tx.close if @driver_tx
49
+ @driver_session.close if @driver_session
50
+ raise e
51
+ end
52
+
53
+ # Commits or marks this transaction for rollback, depending on whether #mark_failed has been previously invoked.
54
+ def close
55
+ fail 'Tried closing when transaction stack is empty (maybe you closed too many?)' if stack.empty?
56
+ fail "Closed transaction which wasn't the most recent on the stack (maybe you forgot to close one?)" if stack.pop != self
57
+
58
+ post_close! if stack.empty?
59
+ end
60
+
61
+ # Marks this transaction as failed,
62
+ # which means that it will unconditionally be rolled back
63
+ # when #close is called.
64
+ # Aliased for legacy purposes.
65
+ def mark_failed
66
+ root.mark_failed if root && root != self
67
+ @failure = true
68
+ end
69
+
70
+ alias failure mark_failed
71
+
72
+ # If it has been marked as failed.
73
+ # Aliased for legacy purposes.
74
+ def failed?
75
+ !!@failure
76
+ end
77
+
78
+ alias failure? failed?
79
+
80
+ def root?
81
+ @root == self
82
+ end
83
+
84
+ def query(*args)
85
+ options = if args[0].is_a?(::Neo4j::Core::Query)
86
+ args[1] ||= {}
87
+ else
88
+ args[1] ||= {}
89
+ args[2] ||= {}
90
+ end
91
+ options[:transaction] ||= self
92
+
93
+ self.class.query(*args)
94
+ end
95
+
96
+ def queries(options = {}, &block)
97
+ self.class.queries({ transaction: self }.merge(options), &block)
98
+ end
99
+
100
+ def after_commit_registry
101
+ @after_commit_registry ||= []
102
+ end
103
+
104
+ def after_commit(&block)
105
+ after_commit_registry << block
106
+ end
107
+
108
+ def commit
109
+ return unless root?
110
+ begin
111
+ @driver_tx.success
112
+ @driver_tx.close
113
+ ensure
114
+ @driver_session.close
115
+ end
116
+ end
117
+
118
+ def delete
119
+ root.driver_tx.failure
120
+ root.driver_tx.close
121
+ root.driver_session.close
122
+ end
123
+
124
+ def root_tx
125
+ root.driver_tx
126
+ end
127
+
128
+ private
129
+
130
+ def post_close!
131
+ if failed?
132
+ delete
133
+ else
134
+ commit
135
+ after_commit_registry.each(&:call)
136
+ end
137
+ end
138
+ end
139
+ end