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

Sign up to get free protection for your applications and to get access to all the features.
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