activegraph 10.0.0.pre.alpha.6

Sign up to get free protection for your applications and to get access to all the features.
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,195 @@
1
+ module Neo4j
2
+ module Migrations
3
+ class Runner
4
+ STATUS_TABLE_FORMAT = '%-10s %-20s %s'.freeze
5
+ SEPARATOR = '--------------------------------------------------'.freeze
6
+ FILE_MISSING = '**** file missing ****'.freeze
7
+ STATUS_TABLE_HEADER = ['Status'.freeze, 'Migration ID'.freeze, 'Migration Name'.freeze].freeze
8
+ UP_MESSAGE = 'up'.freeze
9
+ DOWN_MESSAGE = 'down'.freeze
10
+ INCOMPLETE_MESSAGE = 'incomplete'.freeze
11
+ MIGRATION_RUNNING = {up: 'running'.freeze, down: 'reverting'.freeze}.freeze
12
+ MIGRATION_DONE = {up: 'migrated'.freeze, down: 'reverted'.freeze}.freeze
13
+
14
+ def initialize(options = {})
15
+ @silenced = options[:silenced] || !!ENV['MIGRATIONS_SILENCED']
16
+ label = SchemaMigration.mapped_label
17
+ label.create_constraint(:migration_id, type: :unique) unless label.constraint?(:migration_id)
18
+ @schema_migrations = SchemaMigration.all.to_a
19
+ @up_versions = SortedSet.new(@schema_migrations.map(&:migration_id))
20
+ end
21
+
22
+ def all
23
+ handle_incomplete_states!
24
+ migration_files.each do |migration_file|
25
+ next if up?(migration_file.version)
26
+ migrate(:up, migration_file)
27
+ end
28
+ end
29
+
30
+ def up(version)
31
+ handle_incomplete_states!
32
+ migration_file = find_by_version!(version)
33
+ return if up?(version)
34
+ migrate(:up, migration_file)
35
+ end
36
+
37
+ def down(version)
38
+ handle_incomplete_states!
39
+ migration_file = find_by_version!(version)
40
+ return unless up?(version)
41
+ migrate(:down, migration_file)
42
+ end
43
+
44
+ def rollback(steps)
45
+ handle_incomplete_states!
46
+ @up_versions.to_a.reverse.first(steps).each do |version|
47
+ down(version)
48
+ end
49
+ end
50
+
51
+ def pending_migrations
52
+ all_migrations.select { |migration| !up?(migration) }
53
+ end
54
+
55
+ def complete_migration_versions
56
+ @schema_migrations.map(&:migration_id)
57
+ end
58
+
59
+ def mark_versions_as_complete(versions)
60
+ Neo4j::ActiveBase.new_query
61
+ .with('{versions} AS versions').params(versions: versions).break
62
+ .unwind(version: :versions).break
63
+ .merge('(:`Neo4j::Migrations::SchemaMigration` {migration_id: version})')
64
+ .exec
65
+ end
66
+
67
+ def status
68
+ output STATUS_TABLE_FORMAT, *STATUS_TABLE_HEADER
69
+ output SEPARATOR
70
+ all_migrations.each do |version|
71
+ status = migration_status(version)
72
+ migration_file = find_by_version(version)
73
+ migration_name = migration_file ? migration_file.class_name : FILE_MISSING
74
+ output STATUS_TABLE_FORMAT, status, version, migration_name
75
+ end
76
+ end
77
+
78
+ def resolve(version)
79
+ SchemaMigration.find_by!(migration_id: version).update!(incomplete: false)
80
+ output "Migration #{version} resolved."
81
+ end
82
+
83
+ def reset(version)
84
+ SchemaMigration.find_by!(migration_id: version).destroy
85
+ output "Migration #{version} reset."
86
+ end
87
+
88
+ private
89
+
90
+ def migration_status(version)
91
+ return DOWN_MESSAGE unless up?(version)
92
+ incomplete_states.find { |v| v.migration_id == version } ? INCOMPLETE_MESSAGE : UP_MESSAGE
93
+ end
94
+
95
+ def handle_incomplete_states!
96
+ return unless incomplete_states.any?
97
+ incomplete_versions = incomplete_states.map(&:migration_id)
98
+ fail MigrationError, <<-MSG
99
+ There are migrations struck in an incomplete states, that could not be fixed automatically:
100
+ #{incomplete_versions.join('\n')}
101
+ This can happen when there's a critical error inside a migration.
102
+
103
+ If you think they were was completed correctly, run:
104
+
105
+ #{task_migration_messages('resolve', incomplete_versions)}
106
+
107
+ If you want to reset and run the migration again, run:
108
+
109
+ #{task_migration_messages('reset', incomplete_versions)}
110
+
111
+ MSG
112
+ end
113
+
114
+ def task_migration_messages(type, versions)
115
+ versions.map do |version|
116
+ "rake neo4j:migrate:#{type} VERSION=#{version}"
117
+ end.join("\n")
118
+ end
119
+
120
+ def up?(version)
121
+ @up_versions.include?(version)
122
+ end
123
+
124
+ def migrate(direction, migration_file)
125
+ migration_message(direction, migration_file) do
126
+ migration = migration_file.create(silenced: @silenced)
127
+ migration.migrate(direction)
128
+ end
129
+ end
130
+
131
+ def migration_message(direction, migration)
132
+ output_migration_message "#{migration.version} #{migration.class_name}: #{MIGRATION_RUNNING[direction]}..."
133
+ time = format('%.4fs', yield)
134
+ output_migration_message "#{migration.version} #{migration.class_name}: #{MIGRATION_DONE[direction]} (#{time})"
135
+ output ''
136
+ end
137
+
138
+ def output(*string_format)
139
+ puts format(*string_format) unless @silenced
140
+ end
141
+
142
+ def output_migration_message(message)
143
+ out = "== #{message} "
144
+ tail = '=' * [0, 80 - out.length].max
145
+ output "#{out}#{tail}"
146
+ end
147
+
148
+ def find_by_version!(version)
149
+ find_by_version(version) || fail(UnknownMigrationVersionError, "No such migration #{version}")
150
+ end
151
+
152
+ def find_by_version(version)
153
+ migration_files.find { |file| file.version == version }
154
+ end
155
+
156
+ def all_migrations
157
+ @up_versions + migration_files_versions
158
+ end
159
+
160
+ def incomplete_states
161
+ @incomplete_states ||= SortedSet.new(@schema_migrations.select(&:incomplete?))
162
+ end
163
+
164
+ delegate :migration_files, :migration_files_versions, to: :class
165
+
166
+ class <<self
167
+ def migration_files_versions
168
+ migration_files.map!(&:version)
169
+ end
170
+
171
+ def migration_files
172
+ files.map! { |file_path| MigrationFile.new(file_path) }
173
+ end
174
+
175
+ def latest_migration
176
+ migration_files.last
177
+ end
178
+
179
+ def files
180
+ Dir[files_path].sort
181
+ end
182
+
183
+ private
184
+
185
+ def files_path
186
+ app_root.join('db', 'neo4j', 'migrate', '*.rb')
187
+ end
188
+
189
+ def app_root
190
+ defined?(Rails) ? Rails.root : Pathname.new('.')
191
+ end
192
+ end
193
+ end
194
+ end
195
+ end
@@ -0,0 +1,44 @@
1
+ module Neo4j
2
+ module Migrations
3
+ module Schema
4
+ class << self
5
+ def fetch_schema_data(session)
6
+ {constraints: fetch_constraint_descriptions(session).sort,
7
+ indexes: fetch_index_descriptions(session).sort}
8
+ end
9
+
10
+ def synchronize_schema_data(session, schema_data, remove_missing)
11
+ queries = []
12
+ queries += drop_and_create_queries(fetch_constraint_descriptions(session), schema_data[:constraints], remove_missing)
13
+ queries += drop_and_create_queries(fetch_index_descriptions(session), schema_data[:indexes], remove_missing)
14
+ session.queries do
15
+ queries.each { |query| append query }
16
+ end
17
+ end
18
+
19
+ private
20
+
21
+ def fetch_constraint_descriptions(session)
22
+ session.query('CALL db.constraints()').map(&:description)
23
+ end
24
+
25
+ def fetch_index_descriptions(session)
26
+ session.query('CALL db.indexes()').reject do |row|
27
+ # These indexes are created automagically when the corresponding constraints are created
28
+ row.type == 'node_unique_property'
29
+ end.map(&:description)
30
+ end
31
+
32
+ def drop_and_create_queries(existing, specified, remove_missing)
33
+ [].tap do |queries|
34
+ if remove_missing
35
+ (existing - specified).each { |description| queries << "DROP #{description}" }
36
+ end
37
+
38
+ (specified - existing).each { |description| queries << "CREATE #{description}" }
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,14 @@
1
+ module Neo4j
2
+ module Migrations
3
+ class SchemaMigration
4
+ include Neo4j::ActiveNode
5
+ id_property :migration_id
6
+ property :migration_id, type: String
7
+ property :incomplete, type: Boolean
8
+
9
+ def <=>(other)
10
+ migration_id <=> other.migration_id
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,139 @@
1
+ require 'set'
2
+ module Neo4j
3
+ # This is here to support the removed functionality of being able to
4
+ # defined indexes and constraints on models
5
+ # This code should be removed later
6
+ module ModelSchema
7
+ MODEL_INDEXES = {}
8
+ MODEL_CONSTRAINTS = {}
9
+ REQUIRED_INDEXES = {}
10
+
11
+ class << self
12
+ def add_defined_constraint(model, property_name)
13
+ MODEL_CONSTRAINTS[model] ||= Set.new
14
+ MODEL_CONSTRAINTS[model] << property_name.to_sym
15
+ end
16
+
17
+ def add_defined_index(model, property_name)
18
+ MODEL_INDEXES[model] ||= Set.new
19
+ MODEL_INDEXES[model] << property_name.to_sym
20
+ end
21
+
22
+ def add_required_index(model, property_name)
23
+ REQUIRED_INDEXES[model] ||= Set.new
24
+ REQUIRED_INDEXES[model] << property_name.to_sym
25
+ end
26
+
27
+ def defined_constraint?(model, property_name)
28
+ MODEL_CONSTRAINTS[model] &&
29
+ MODEL_CONSTRAINTS[model].include?(property_name.to_sym)
30
+ end
31
+
32
+ def model_constraints
33
+ return @model_constraints if @model_constraints
34
+
35
+ constraints = Neo4j::Transaction.constraints.each_with_object({}) do |row, result|
36
+ result[row[:label]] ||= []
37
+ result[row[:label]] << row[:properties]
38
+ end
39
+
40
+ @model_constraints = schema_elements_list(MODEL_CONSTRAINTS, constraints)
41
+ end
42
+
43
+ def model_indexes
44
+ return @model_indexes if @model_indexes
45
+
46
+ indexes = Neo4j::Transaction.indexes.each_with_object({}) do |row, result|
47
+ result[row[:label]] ||= []
48
+ result[row[:label]] << row[:properties]
49
+ end
50
+
51
+ @model_indexes = schema_elements_list(MODEL_INDEXES, indexes) +
52
+ schema_elements_list(REQUIRED_INDEXES, indexes).reject(&:last)
53
+ # reject required indexes which are already in the DB
54
+ end
55
+
56
+ # should be private
57
+ def schema_elements_list(by_model, db_results)
58
+ by_model.flat_map do |model, property_names|
59
+ label = model.mapped_label_name.to_sym
60
+ property_names.map do |property_name|
61
+ exists = db_results[label] && db_results[label].include?([property_name])
62
+ [model, label, property_name, exists]
63
+ end
64
+ end
65
+ end
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
+
82
+ def legacy_model_schema_informations
83
+ ensure_model_data_state!
84
+ data = {index: [], constraint: []}
85
+ each_schema_element do |type, model, label, property_name|
86
+ data[type] << {label: label, property_name: property_name, model: model}
87
+ end
88
+ data
89
+ end
90
+
91
+ def validate_model_schema!
92
+ ensure_model_data_state!
93
+ messages = {index: [], constraint: []}
94
+ each_schema_element do |type, model, label, property_name, exists|
95
+ if exists
96
+ log_warning!(type, model, property_name) if model.id_property_name.to_sym != property_name
97
+ else
98
+ messages[type] << force_add_message(type, label, property_name)
99
+ end
100
+ end
101
+
102
+ return if messages.values.all?(&:empty?)
103
+
104
+ fail ::Neo4j::DeprecatedSchemaDefinitionError, validation_error_message(messages)
105
+ end
106
+
107
+ def validation_error_message(messages)
108
+ <<MSG
109
+ 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 if you haven't already:
110
+
111
+ #{messages[:constraint].join("\n")}
112
+ #{messages[:index].join("\n")}
113
+
114
+ And then run `rake neo4j:migrate`
115
+
116
+ (zshell users may need to escape the brackets)
117
+ MSG
118
+ end
119
+
120
+ def force_add_message(index_or_constraint, label, property_name)
121
+ "rake neo4j:generate_schema_migration[#{index_or_constraint},#{label},#{property_name}]"
122
+ end
123
+
124
+ def log_warning!(index_or_constraint, model, property_name)
125
+ Neo4j::ActiveBase.logger.warn "WARNING: The #{index_or_constraint} option is no longer supported (Defined on #{model.name} for #{property_name})"
126
+ end
127
+
128
+ private
129
+
130
+ def each_schema_element
131
+ [[:constraint, model_constraints], [:index, model_indexes]].each do |type, schema_elements|
132
+ schema_elements.each do |args|
133
+ yield(type, *args)
134
+ end
135
+ end
136
+ end
137
+ end
138
+ end
139
+ end
@@ -0,0 +1,27 @@
1
+ module Neo4j
2
+ class Paginated
3
+ include Enumerable
4
+ attr_reader :items, :total, :current_page
5
+
6
+ def initialize(items, total, current_page)
7
+ @items = items
8
+ @total = total
9
+ @current_page = current_page
10
+ end
11
+
12
+ def self.create_from(source, page, per_page, order = nil)
13
+ target = source.node_var || source.identity
14
+ partial = source.skip((page - 1) * per_page).limit(per_page)
15
+ ordered_partial, ordered_source = if order
16
+ [partial.order_by(order), source.query.with("#{target} as #{target}").pluck("COUNT(#{target})").first]
17
+ else
18
+ [partial, source.count]
19
+ end
20
+ Paginated.new(ordered_partial, ordered_source, page)
21
+ end
22
+
23
+ delegate :each, to: :items
24
+ delegate :pluck, to: :items
25
+ delegate :size, :[], to: :items
26
+ end
27
+ end
@@ -0,0 +1,105 @@
1
+ require 'active_support/notifications'
2
+ require 'rails/railtie'
3
+ # Need the action_dispatch railtie to have action_dispatch.rescue_responses initialized correctly
4
+ require 'action_dispatch/railtie'
5
+ require 'neo4j/core/driver'
6
+
7
+ module Neo4j
8
+ class Railtie < ::Rails::Railtie
9
+ def empty_config
10
+ ActiveSupport::OrderedOptions.new.tap { |cfg| cfg.session = ActiveSupport::OrderedOptions.new }
11
+ end
12
+
13
+ config.neo4j = empty_config
14
+
15
+ if defined?(ActiveSupport::Reloader)
16
+ ActiveSupport::Reloader.to_prepare do
17
+ Neo4j::ActiveNode::Labels::Reloading.reload_models!
18
+ end
19
+ elsif const_defined?(:ActionDispatch)
20
+ ActionDispatch::Reloader.to_prepare do
21
+ Neo4j::ActiveNode::Labels::Reloading.reload_models!
22
+ end
23
+ end
24
+
25
+ # Rescue responses similar to ActiveRecord.
26
+ config.action_dispatch.rescue_responses.merge!(
27
+ 'Neo4j::RecordNotFound' => :not_found,
28
+ 'Neo4j::ActiveNode::Labels::RecordNotFound' => :not_found
29
+ )
30
+
31
+ # Add ActiveModel translations to the I18n load_path
32
+ initializer 'i18n' do
33
+ config.i18n.load_path += Dir[File.join(File.dirname(__FILE__), '..', '..', '..', 'config', 'locales', '*.{rb,yml}')]
34
+ end
35
+
36
+ console do
37
+ Neo4j::Config[:logger] = ActiveSupport::Logger.new(STDOUT)
38
+ Neo4j::Config[:verbose_query_logs] = false
39
+ end
40
+
41
+ # Starting Neo after :load_config_initializers allows apps to
42
+ # register migrations in config/initializers
43
+ initializer 'neo4j.start', after: :load_config_initializers do |app|
44
+ app.config.neo4j.skip_migration_check = true if Rails.env.test?
45
+
46
+ neo4j_config = ActiveSupport::OrderedOptions.new
47
+ app.config.neo4j.each { |k, v| neo4j_config[k] = v } if app.config.neo4j
48
+
49
+ Neo4j::Config.configuration.merge!(neo4j_config.to_h)
50
+
51
+ Neo4j::ActiveBase.on_establish_session { setup! neo4j_config }
52
+
53
+ Neo4j::Config[:logger] ||= Rails.logger
54
+
55
+ if Neo4j::Config.fail_on_pending_migrations
56
+ config.app_middleware.insert_after ::ActionDispatch::Callbacks, Neo4j::Migrations::CheckPending
57
+ end
58
+ end
59
+
60
+ def setup!(neo4j_config = empty_config)
61
+ url, path, options = final_session_config!(neo4j_config).values_at(:url, :path, :options)
62
+ options ||= {}
63
+ register_neo4j_cypher_logging
64
+
65
+ Neo4j::ActiveBase.new_driver( url || path || default_session_path_or_url, options)
66
+ end
67
+
68
+ def final_session_config!(neo4j_config)
69
+ (neo4j_config[:session].empty? ? yaml_config_data : neo4j_config[:session]).dup
70
+ end
71
+
72
+ def default_session_path_or_url
73
+ ENV['NEO4J_URL'] || ENV['NEO4J_PATH'] || 'http://localhost:7474'
74
+ end
75
+
76
+ def yaml_config_data
77
+ @yaml_config_data ||= if yaml_path
78
+ HashWithIndifferentAccess.new(YAML.load(ERB.new(yaml_path.read).result)[Rails.env])
79
+ else
80
+ {}
81
+ end
82
+ end
83
+
84
+ def yaml_path
85
+ return unless defined?(Rails)
86
+ @yaml_path ||= %w(config/neo4j.yml config/neo4j.yaml).map do |path|
87
+ Rails.root.join(path)
88
+ end.detect(&:exist?)
89
+ end
90
+
91
+ def register_neo4j_cypher_logging
92
+ return if @neo4j_cypher_logging_registered
93
+
94
+ Neo4j::Core::Query.pretty_cypher = Neo4j::Config[:pretty_logged_cypher_queries]
95
+
96
+ logger_proc = ->(message) do
97
+ (Neo4j::Config[:logger] ||= Rails.logger).debug message
98
+ end
99
+ Neo4j::Transaction.subscribe_to_query(&logger_proc)
100
+ Neo4j::Transaction.subscribe_to_request(&logger_proc)
101
+
102
+ @neo4j_cypher_logging_registered = true
103
+ end
104
+ end
105
+ end