neo4j 8.0.0.alpha.1 → 8.0.0.alpha.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ae62feb1503e7f65d35cbc182034a65a28c9361b
4
- data.tar.gz: 5ddeb82fa562a1868c1a6b5d8898c5b0dca80276
3
+ metadata.gz: 74a57c27a28cfa8eda0a7a71413d428e40d2802a
4
+ data.tar.gz: 51e4a2ef533ae4fbe85daa56365f8e7244ffa7f3
5
5
  SHA512:
6
- metadata.gz: 8acc45e1a6fa2cc4c314558f12b737ab2ffa0c30b914244672839da5f7ab9a64ea4194f39d9e4b90cb6f85e7a32b10b2f73c52eedd28c5d4bf072828881d4acc
7
- data.tar.gz: 47c95259853558c3637073ef6150d3f53c6af8bf54c9866cee1fcff543561482e48e38bdc723916ba41c6125fa073dfb53f26689a15f497cd211b57deb8109af
6
+ metadata.gz: 8f0bdf1994b8871a6c5f333661fa6f77d46f3cb7a0c811b1612580509d8829979dac47461a5978758a732fd04fa940489ba53da41bdf78b8d14303545a8c94f7
7
+ data.tar.gz: 68279d917be513365d828850cd5967c350d574fe9d0bbf0d4b7aff6d7fb767e2720c0a911e9076a7bd89706401e775a6545801a0ca632fd17dc06aa51b51a102
data/CHANGELOG.md CHANGED
@@ -3,7 +3,20 @@ All notable changes to this project will be documented in this file.
3
3
  This file should follow the standards specified on [http://keepachangelog.com/]
4
4
  This project adheres to [Semantic Versioning](http://semver.org/).
5
5
 
6
- ## [Unreleased] Unreleased
6
+ ## [8.0.0.alpha.2] 2016-08-05
7
+
8
+ ### Changed
9
+
10
+ - Improve migration output format / show execution time in migrations
11
+
12
+ ### Fixed
13
+
14
+ - Caching of model index and constraint checks
15
+ - Error when running schema migrations. Migrations now give a warning and instructions if a migration fails and cannot be recovered
16
+ - Error when running rake tasks to generate "force" creations of indexes / constraints and there is no migration directory
17
+ - `WARNING` is no longer displayed for constraints defined from `id_property` (either one which is implict or explict)
18
+
19
+ ## [8.0.0.alpha.1] 2016-08-02
7
20
 
8
21
  ### Changed
9
22
 
data/Gemfile CHANGED
@@ -7,8 +7,10 @@ gemspec
7
7
  if branch = ENV['TRAVIS_BRANCH']
8
8
  same_branch_exists = `curl --head https://github.com/neo4jrb/neo4j-core/tree/#{branch} | head -1`.match(/200 OK/)
9
9
  gem 'neo4j-core', github: 'neo4jrb/neo4j-core', branch: same_branch_exists ? branch : 'master'
10
+ elsif ENV['USE_LOCAL_CORE']
11
+ gem 'neo4j-core', path: '../neo4j-core'
10
12
  else
11
- gem 'neo4j-core', github: 'neo4jrb/neo4j-core'
13
+ gem 'neo4j-core'
12
14
  end
13
15
 
14
16
  # gem 'active_attr', github: 'neo4jrb/active_attr', branch: 'performance'
@@ -56,6 +56,12 @@ module Neo4j
56
56
  _persisted_obj || fail('Tried to access native neo4j object on a non persisted object')
57
57
  end
58
58
 
59
+ LOADED_CLASSES = []
60
+
61
+ def self.loaded_classes
62
+ LOADED_CLASSES
63
+ end
64
+
59
65
  module ClassMethods
60
66
  def nodeify(object)
61
67
  if object.is_a?(::Neo4j::ActiveNode) || object.nil?
@@ -68,12 +74,14 @@ module Neo4j
68
74
 
69
75
  included do
70
76
  include Neo4j::Timestamps if Neo4j::Config[:record_timestamps]
77
+ LOADED_CLASSES << self
71
78
 
72
79
  def self.inherited?
73
80
  !!@inherited
74
81
  end
75
82
 
76
83
  def self.inherited(other)
84
+ LOADED_CLASSES << other
77
85
  other.instance_variable_set('@inherited', true)
78
86
  inherit_id_property(other)
79
87
  attributes.each_pair do |k, v|
@@ -11,17 +11,8 @@ module Neo4j
11
11
  end
12
12
 
13
13
  def migrate(method)
14
- ensure_schema_migration_constraint
15
14
  Benchmark.realtime do
16
- ActiveBase.run_transaction(transactions?) do
17
- if method == :up
18
- up
19
- SchemaMigration.create!(migration_id: @migration_id)
20
- else
21
- down
22
- SchemaMigration.find_by!(migration_id: @migration_id).destroy
23
- end
24
- end
15
+ method == :up ? migrate_up : migrate_down
25
16
  end
26
17
  end
27
18
 
@@ -35,9 +26,48 @@ module Neo4j
35
26
 
36
27
  private
37
28
 
38
- def ensure_schema_migration_constraint
39
- SchemaMigration.first
40
- Neo4j::Core::Label.wait_for_schema_changes(ActiveBase.current_session)
29
+ def migrate_up
30
+ schema = SchemaMigration.create!(migration_id: @migration_id, incomplete: true)
31
+ begin
32
+ run_migration(:up)
33
+ rescue StandardError => e
34
+ schema.destroy
35
+ handle_migration_error!(e)
36
+ end
37
+ schema.update!(incomplete: nil)
38
+ end
39
+
40
+ def migrate_down
41
+ schema = SchemaMigration.find_by!(migration_id: @migration_id)
42
+ schema.update!(incomplete: true)
43
+ begin
44
+ run_migration(:down)
45
+ rescue StandardError => e
46
+ schema.update!(incomplete: nil)
47
+ handle_migration_error!(e)
48
+ end
49
+ schema.destroy
50
+ end
51
+
52
+ def run_migration(direction)
53
+ migration_transaction { log_queries { public_send(direction) } }
54
+ end
55
+
56
+ def handle_migration_error!(e)
57
+ fail e unless e.message =~ /Cannot perform data updates in a transaction that has performed schema updates./
58
+ fail MigrationError,
59
+ "#{e.message}. Please add `disable_transactions!` in your migration file."
60
+ end
61
+
62
+ def migration_transaction(&block)
63
+ ActiveBase.run_transaction(transactions?, &block)
64
+ end
65
+
66
+ def log_queries
67
+ subscriber = Neo4j::Core::CypherSession::Adaptors::Base.subscribe_to_query(&method(:output))
68
+ yield
69
+ ensure
70
+ ActiveSupport::Notifications.unsubscribe(subscriber) if subscriber
41
71
  end
42
72
  end
43
73
  end
@@ -8,9 +8,10 @@ module Neo4j
8
8
 
9
9
  def add_constraint(label, property, options = {})
10
10
  force = options[:force] || false
11
+ type = options[:type] || :uniqueness
11
12
  label_object = ActiveBase.label_object(label)
12
13
  fail_duplicate_constraint_or_index!(:constraint, label, property) if !force && label_object.constraint?(property)
13
- label_object.create_constraint(property, type: :uniqueness)
14
+ label_object.create_constraint(property, type: type)
14
15
  end
15
16
 
16
17
  def add_index(label, property, options = {})
@@ -20,14 +21,11 @@ module Neo4j
20
21
  label_object.create_index(property)
21
22
  end
22
23
 
23
- def force_add_index(label, property)
24
- add_index(label, property)
25
- end
26
-
27
- def drop_constraint(label, property)
24
+ def drop_constraint(label, property, options = {})
25
+ type = options[:type] || :uniqueness
28
26
  label_object = ActiveBase.label_object(label)
29
27
  fail_missing_constraint_or_index!(:constraint, label, property) if !label_object.constraint?(property)
30
- label_object.drop_constraint(property, type: :uniqueness)
28
+ label_object.drop_constraint(property, type: type)
31
29
  end
32
30
 
33
31
  def drop_index(label, property)
@@ -7,15 +7,18 @@ module Neo4j
7
7
  STATUS_TABLE_HEADER = ['Status'.freeze, 'Migration ID'.freeze, 'Migration Name'.freeze].freeze
8
8
  UP_MESSAGE = 'up'.freeze
9
9
  DOWN_MESSAGE = 'down'.freeze
10
+ INCOMPLETE_MESSAGE = 'incomplete'.freeze
10
11
  MIGRATION_RUNNING = {up: 'running'.freeze, down: 'reverting'.freeze}.freeze
11
12
  MIGRATION_DONE = {up: 'migrated'.freeze, down: 'reverted'.freeze}.freeze
12
13
 
13
14
  def initialize
14
15
  SchemaMigration.mapped_label.create_constraint(:migration_id, type: :unique)
15
- @up_versions = SortedSet.new(SchemaMigration.all.pluck(:migration_id))
16
+ @schema_migrations = SchemaMigration.all.to_a
17
+ @up_versions = SortedSet.new(@schema_migrations.map(&:migration_id))
16
18
  end
17
19
 
18
20
  def all
21
+ handle_incomplete_states!
19
22
  migration_files.each do |migration_file|
20
23
  next if up?(migration_file.version)
21
24
  migrate(:up, migration_file)
@@ -23,18 +26,21 @@ module Neo4j
23
26
  end
24
27
 
25
28
  def up(version)
29
+ handle_incomplete_states!
26
30
  migration_file = find_by_version!(version)
27
31
  return if up?(version)
28
32
  migrate(:up, migration_file)
29
33
  end
30
34
 
31
35
  def down(version)
36
+ handle_incomplete_states!
32
37
  migration_file = find_by_version!(version)
33
38
  return unless up?(version)
34
39
  migrate(:down, migration_file)
35
40
  end
36
41
 
37
42
  def rollback(steps)
43
+ handle_incomplete_states!
38
44
  @up_versions.to_a.reverse.first(steps).each do |version|
39
45
  down(version)
40
46
  end
@@ -44,15 +50,55 @@ module Neo4j
44
50
  output STATUS_TABLE_FORMAT, *STATUS_TABLE_HEADER
45
51
  output SEPARATOR
46
52
  all_migrations.each do |version|
47
- status = up?(version) ? UP_MESSAGE : DOWN_MESSAGE
53
+ status = migration_status(version)
48
54
  migration_file = find_by_version(version)
49
55
  migration_name = migration_file ? migration_file.class_name : FILE_MISSING
50
56
  output STATUS_TABLE_FORMAT, status, version, migration_name
51
57
  end
52
58
  end
53
59
 
60
+ def resolve(version)
61
+ SchemaMigration.find_by!(migration_id: version).update!(incomplete: false)
62
+ output "Migration #{version} resolved."
63
+ end
64
+
65
+ def reset(version)
66
+ SchemaMigration.find_by!(migration_id: version).destroy
67
+ output "Migration #{version} reset."
68
+ end
69
+
54
70
  private
55
71
 
72
+ def migration_status(version)
73
+ return DOWN_MESSAGE unless up?(version)
74
+ incomplete_states.find { |v| v.migration_id == version } ? INCOMPLETE_MESSAGE : UP_MESSAGE
75
+ end
76
+
77
+ def handle_incomplete_states!
78
+ return unless incomplete_states.any?
79
+ incomplete_versions = incomplete_states.map(&:migration_id)
80
+ fail MigrationError, <<-MSG
81
+ There are migrations struck in an incomplete states, that could not be fixed automatically:
82
+ #{incomplete_versions.join('\n')}
83
+ This can happen when there's a critical error inside a migration.
84
+
85
+ If you think they were was completed correctly, run:
86
+
87
+ #{task_migration_messages('resolve', incomplete_versions)}
88
+
89
+ If you want to reset and run the migration again, run:
90
+
91
+ #{task_migration_messages('reset', incomplete_versions)}
92
+
93
+ MSG
94
+ end
95
+
96
+ def task_migration_messages(type, versions)
97
+ versions.map do |version|
98
+ "rake neo4j:migrate:#{type} VERSION=#{version}"
99
+ end.join("\n")
100
+ end
101
+
56
102
  def up?(version)
57
103
  @up_versions.include?(version)
58
104
  end
@@ -65,15 +111,22 @@ module Neo4j
65
111
  end
66
112
 
67
113
  def migration_message(direction, migration)
68
- output "== #{migration.version} #{migration.class_name}: #{MIGRATION_RUNNING[direction]}... ========="
69
- yield
70
- output "== #{migration.version} #{migration.class_name}: #{MIGRATION_DONE[direction]} ========="
114
+ output_migration_message "#{migration.version} #{migration.class_name}: #{MIGRATION_RUNNING[direction]}..."
115
+ time = format('%.4fs', yield)
116
+ output_migration_message "#{migration.version} #{migration.class_name}: #{MIGRATION_DONE[direction]} (#{time})"
117
+ output ''
71
118
  end
72
119
 
73
120
  def output(*string_format)
74
121
  puts format(*string_format) unless !!ENV['MIGRATIONS_SILENCED']
75
122
  end
76
123
 
124
+ def output_migration_message(message)
125
+ out = "== #{message} "
126
+ tail = '=' * [0, 80 - out.length].max
127
+ output "#{out}#{tail}"
128
+ end
129
+
77
130
  def find_by_version!(version)
78
131
  find_by_version(version) || fail(UnknownMigrationVersionError, "No such migration #{version}")
79
132
  end
@@ -94,6 +147,10 @@ module Neo4j
94
147
  files.map { |file_path| MigrationFile.new(file_path) }
95
148
  end
96
149
 
150
+ def incomplete_states
151
+ @incomplete_states ||= SortedSet.new(@schema_migrations.select(&:incomplete?))
152
+ end
153
+
97
154
  def files
98
155
  Dir[files_path].sort
99
156
  end
@@ -4,6 +4,11 @@ module Neo4j
4
4
  include Neo4j::ActiveNode
5
5
  id_property :migration_id
6
6
  property :migration_id, type: String
7
+ property :incomplete, type: Boolean
8
+
9
+ def <=>(other)
10
+ migration_id <=> other.migration_id
11
+ end
7
12
  end
8
13
  end
9
14
  end
@@ -30,22 +30,26 @@ module Neo4j
30
30
  end
31
31
 
32
32
  def model_constraints
33
+ return @model_constraints if @model_constraints
34
+
33
35
  constraints = Neo4j::ActiveBase.current_session.constraints.each_with_object({}) do |row, result|
34
36
  result[row[:label]] ||= []
35
37
  result[row[:label]] << row[:properties]
36
38
  end
37
39
 
38
- schema_elements_list(MODEL_CONSTRAINTS, constraints)
40
+ @model_constraints = schema_elements_list(MODEL_CONSTRAINTS, constraints)
39
41
  end
40
42
 
41
43
  def model_indexes
44
+ return @model_indexes if @model_indexes
45
+
42
46
  indexes = Neo4j::ActiveBase.current_session.indexes.each_with_object({}) do |row, result|
43
47
  result[row[:label]] ||= []
44
48
  result[row[:label]] << row[:properties]
45
49
  end
46
50
 
47
- schema_elements_list(MODEL_INDEXES, indexes) +
48
- schema_elements_list(REQUIRED_INDEXES, indexes).reject(&:last)
51
+ @model_indexes = schema_elements_list(MODEL_INDEXES, indexes) +
52
+ schema_elements_list(REQUIRED_INDEXES, indexes).reject(&:last)
49
53
  # reject required indexes which are already in the DB
50
54
  end
51
55
 
@@ -60,12 +64,28 @@ module Neo4j
60
64
  end
61
65
  end
62
66
 
67
+ def ensure_model_data_state!
68
+ # If we load a new model, reset everything
69
+ if @previously_loaded_models_count != Neo4j::ActiveNode.loaded_classes.size
70
+ # Make sure we've finalized id_property details and have called
71
+ # add_ constraint/index methods above
72
+ Neo4j::ActiveNode.loaded_classes.each(&:ensure_id_property_info!)
73
+ reload_models_data!
74
+ end
75
+ end
76
+
77
+ def reload_models_data!
78
+ @previously_loaded_models_count = Neo4j::ActiveNode.loaded_classes.size
79
+ @model_indexes = @model_constraints = nil
80
+ end
81
+
63
82
  def validate_model_schema!
83
+ ensure_model_data_state!
64
84
  messages = {index: [], constraint: []}
65
85
  [[:constraint, model_constraints], [:index, model_indexes]].each do |type, schema_elements|
66
86
  schema_elements.map do |model, label, property_name, exists|
67
87
  if exists
68
- log_warning!(type, model, property_name)
88
+ log_warning!(type, model, property_name) if model.id_property_name.to_sym != property_name
69
89
  else
70
90
  messages[type] << force_add_message(type, label, property_name)
71
91
  end
@@ -79,11 +99,13 @@ module Neo4j
79
99
 
80
100
  def validation_error_message(messages)
81
101
  <<MSG
82
- Some schema elements were defined by the model (which is no longer support), but they do not exist in the database. Run the following to create them:
102
+ Some schema elements were defined by the model (which is no longer supported), but they do not exist in the database. Run the following to create them:
83
103
 
84
104
  #{messages[:constraint].join("\n")}
85
105
  #{messages[:index].join("\n")}
86
106
 
107
+ And then run `rake neo4j:migrate`
108
+
87
109
  (zshell users may need to escape the brackets)
88
110
  MSG
89
111
  end
@@ -123,7 +123,7 @@ module Neo4j
123
123
  puts
124
124
  return session
125
125
  end
126
- rescue Faraday::ConnectionFailed => e
126
+ rescue Neo4j::Core::CypherSession::ConnectionFailedError
127
127
  raise e if !wait
128
128
 
129
129
  putc '.'
@@ -113,7 +113,7 @@ module Neo4j::Shared
113
113
 
114
114
  def_delegators :declared_properties, :serialized_properties, :serialized_properties=, :serialize, :declared_property_defaults
115
115
 
116
- VALID_PROPERTY_OPTIONS = %w(type default index constraint serializer typecaster null).map(&:to_sym)
116
+ VALID_PROPERTY_OPTIONS = %w(type default index constraint serializer typecaster).map(&:to_sym)
117
117
  # Defines a property on the class
118
118
  #
119
119
  # See active_attr gem for allowed options, e.g which type
@@ -62,6 +62,20 @@ namespace :neo4j do
62
62
  runner = Neo4j::Migrations::Runner.new
63
63
  runner.status
64
64
  end
65
+
66
+ desc 'Resolve an incomplete version state'
67
+ task resolve: :environment do
68
+ version = ENV['VERSION'] || fail(ArgumentError, 'VERSION is required')
69
+ runner = Neo4j::Migrations::Runner.new
70
+ runner.resolve version
71
+ end
72
+
73
+ desc 'Resolve an incomplete version state'
74
+ task reset: :environment do
75
+ version = ENV['VERSION'] || fail(ArgumentError, 'VERSION is required')
76
+ runner = Neo4j::Migrations::Runner.new
77
+ runner.reset version
78
+ end
65
79
  end
66
80
 
67
81
  desc 'Rollbacks migrations given a STEP number'
@@ -84,6 +98,8 @@ namespace :neo4j do
84
98
 
85
99
  migration_class_name = "ForceCreate#{label.camelize}#{property_name.camelize}#{index_or_constraint.capitalize}"
86
100
 
101
+ require 'fileutils'
102
+ FileUtils.mkdir_p('db/neo4j/migrate')
87
103
  path = File.join('db/neo4j/migrate', "#{DateTime.now.utc.strftime('%Y%m%d%H%M%S')}_#{migration_class_name.underscore}.rb")
88
104
 
89
105
  content = <<-CONTENT
data/lib/neo4j/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Neo4j
2
- VERSION = '8.0.0.alpha.1'
2
+ VERSION = '8.0.0.alpha.2'
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: neo4j
3
3
  version: !ruby/object:Gem::Version
4
- version: 8.0.0.alpha.1
4
+ version: 8.0.0.alpha.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andreas Ronge, Brian Underwood, Chris Grigg
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-08-02 00:00:00.000000000 Z
11
+ date: 2016-08-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: orm_adapter
@@ -210,8 +210,10 @@ dependencies:
210
210
  - - "~>"
211
211
  - !ruby/object:Gem::Version
212
212
  version: 0.39.0
213
- description: |
214
- A Neo4j OGM (Object-Graph-Mapper) for use in Ruby on Rails and Rack frameworks heavily inspired by ActiveRecord.
213
+ description: 'A Neo4j OGM (Object-Graph-Mapper) for use in Ruby on Rails and Rack
214
+ frameworks heavily inspired by ActiveRecord.
215
+
216
+ '
215
217
  email: andreas.ronge@gmail.com, brian@brian-underwood.codes, chris@subvertallmedia.com
216
218
  executables:
217
219
  - neo4j-jars
@@ -358,7 +360,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
358
360
  version: 1.3.1
359
361
  requirements: []
360
362
  rubyforge_project: neo4j
361
- rubygems_version: 2.4.5.1
363
+ rubygems_version: 2.5.1
362
364
  signing_key:
363
365
  specification_version: 4
364
366
  summary: A graph database for Ruby