rails_age 0.2.0 → 0.3.1
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 +4 -4
- data/CHANGELOG.md +28 -5
- data/README.md +104 -17
- data/db/migrate/{20240521062349_configure_apache_age.rb → 20240521062349_add_apache_age.rb} +1 -1
- data/db/schema.rb +1 -1
- data/lib/apache_age/entities/class_methods.rb +26 -19
- data/lib/apache_age/validators/unique_edge_validator.rb +1 -1
- data/lib/rails_age/version.rb +1 -1
- data/lib/tasks/copy_migrations.rake +33 -0
- data/lib/tasks/database_config.rake +99 -0
- data/lib/tasks/install.original.rake +257 -0
- data/lib/tasks/install.rake +11 -83
- data/lib/tasks/schema_config.rake +95 -0
- metadata +9 -6
- data/lib/tasks/rails_age_tasks.rake +0 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 272f8908dc8fb8a98adb443411e4b439eff914c8173b5eb64b53fa5362fa1572
|
4
|
+
data.tar.gz: bf1921611698ffc1b651787f2e1b1a5d1bc503cbe1300639b2f9dada8202cdde
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 51592c31e872a9b8dc6e96fe8fa42374241496e4da6a17ce70bf63f5f118ac078e81200259c048529e7e16b2147c8c5bbef00315cee8f26237295d04ffaea887
|
7
|
+
data.tar.gz: 0b24560f7703e4dc4f97d3d36de626906a47a2cda5f5137cc6c1840a5281bbcf2693743666fb6301b26723c9723244bd1854c9c8b21d5d657eb621d4caf0d864
|
data/CHANGELOG.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# Change Log
|
2
2
|
|
3
|
-
## VERSION 0.
|
3
|
+
## VERSION 0.4.0 - 2024-xx-xx
|
4
4
|
|
5
5
|
- **cypher**
|
6
6
|
* query support
|
@@ -9,10 +9,33 @@
|
|
9
9
|
- **Paths**
|
10
10
|
* ?
|
11
11
|
|
12
|
+
## VERSION 0.3.1 - 2024-xx-xx
|
13
|
+
|
14
|
+
- **Generators**
|
15
|
+
* add `rails generate apache_age:node` to create a node model (with its type in initializer)
|
16
|
+
* add `rails generate apache_age:edge` to create an edge model (with its type in initializer)
|
17
|
+
- **Installer**
|
18
|
+
* refactor into multiple independent tasks with tests
|
19
|
+
- **Documentation**
|
20
|
+
* updated README with additional information
|
21
|
+
* added `db/structure.sql` config to README
|
22
|
+
|
23
|
+
## VERSION 0.3.0 - 2024-05-28
|
24
|
+
|
25
|
+
- **Edges**
|
26
|
+
* `find_by(start_node:, :end_node:, properties:)` to find an edge with specific nodes & properties (deprecated `find_edge`)
|
27
|
+
- **Installer** (`rails generate apache_age:install`)
|
28
|
+
* copy Age PG Extenstion migration to `db/migrate`
|
29
|
+
* run the AGE PG Migration
|
30
|
+
* repair `db/schema.rb` (rails mangles schema after running pg extension)
|
31
|
+
* update `database.yml` with schema search paths
|
32
|
+
|
33
|
+
NOTE: the `rails generate apache_age:install` can be run at any time to repair the schema (or other config) file if needed.
|
34
|
+
|
12
35
|
## VERSION 0.2.0 - 2024-05-26
|
13
36
|
|
14
37
|
- **Edges**
|
15
|
-
* add class methods to find_edge(with {properties, end_id, start_id})
|
38
|
+
* add class methods to `find_edge` (with {properties, end_id, start_id})
|
16
39
|
* add missing methods to use in rails controllers
|
17
40
|
* validate edge start- & end-nodes are valid
|
18
41
|
* add unique edge validations
|
@@ -25,13 +48,13 @@
|
|
25
48
|
Initial release has the following features:
|
26
49
|
|
27
50
|
- **Nodes:**
|
28
|
-
*
|
51
|
+
* `.create`, `.read`, `.update`, `.delete`, `.all`, `.find(by id)`, `.find_by(age_properties)`
|
29
52
|
* verified with usage in a controller and views
|
30
53
|
- **Edges:**
|
31
|
-
|
54
|
+
*`.create`, `.read`, `.update`, `.delete`, `.all`, `.find(by id)`, `.find_by(age_properties)`
|
32
55
|
* verified with usage in a controller and views
|
33
56
|
- **Entities:**
|
34
|
-
* find
|
57
|
+
* `.all`, `.find(id)`, `.find_by(age_property)` use these when class, label, edge, node
|
35
58
|
|
36
59
|
These can be used within Rails applications using a Rails APIs including within controllers and views.
|
37
60
|
See the [README](README.md) for more information.
|
data/README.md
CHANGED
@@ -12,24 +12,30 @@ Add this line to your application's Gemfile:
|
|
12
12
|
gem "rails_age"
|
13
13
|
```
|
14
14
|
|
15
|
-
|
15
|
+
### Quick Install
|
16
|
+
|
17
|
+
using the installer, creates the migration to install age, runs the migration, and adjusts the schema file, and updates the `config/database.yml` file.
|
16
18
|
|
17
19
|
```bash
|
18
20
|
$ bundle
|
21
|
+
$ bin/rails apache_age:install
|
22
|
+
$ git add .
|
23
|
+
$ git commit -m "Add Apache Age to Rails"
|
19
24
|
```
|
20
25
|
|
21
|
-
|
26
|
+
NOTE: it is important to commit the `db/schema.rb` to git because `rails db:migrate` inappropriately modifies the schema file (I haven't yet tested `db/structure.sql`). **You can run `bin/rails apache_age:install` at any time to repair the schema file as needed.**
|
22
27
|
|
23
|
-
|
24
|
-
$ gem install rails_age
|
25
|
-
```
|
28
|
+
For now, if you are using `db/structure.sql` you will need to manually configure Apache Age (RailsAge) as described below.
|
26
29
|
|
27
|
-
|
28
|
-
https://github.com/marpori/rails_age/blob/main/db/migrate/20240521062349_configure_apache_age.rb
|
30
|
+
### Manual Install
|
29
31
|
|
32
|
+
create a migration to add the Apache Age extension to your database
|
30
33
|
```bash
|
31
|
-
|
32
|
-
|
34
|
+
$ bin/rails g migration AddApacheAge
|
35
|
+
```
|
36
|
+
copy the contents of https://github.com/marpori/rails_age/blob/main/db/migrate/20240521062349_add_apache_age.rb
|
37
|
+
```ruby
|
38
|
+
class AddApacheAge < ActiveRecord::Migration[7.1]
|
33
39
|
def up
|
34
40
|
# Allow age extension
|
35
41
|
execute('CREATE EXTENSION IF NOT EXISTS age;')
|
@@ -65,16 +71,48 @@ class ConfigureApacheAge < ActiveRecord::Migration[7.1]
|
|
65
71
|
end
|
66
72
|
end
|
67
73
|
```
|
74
|
+
into your new migration file
|
68
75
|
|
69
|
-
|
70
|
-
|
76
|
+
then run the migration
|
77
|
+
```bash
|
78
|
+
$ bin/rails db:migrate
|
79
|
+
```
|
71
80
|
|
81
|
+
Rails migrate will mangle the schema `db/schema.rb` file. You need to remove the lines that look like:
|
72
82
|
```ruby
|
73
|
-
# db/schema.rb
|
74
83
|
ActiveRecord::Schema[7.1].define(version: 2024_05_21_062349) do
|
84
|
+
create_schema "ag_catalog"
|
85
|
+
create_schema "age_schema"
|
86
|
+
|
75
87
|
# These are extensions that must be enabled in order to support this database
|
88
|
+
enable_extension "age"
|
76
89
|
enable_extension "plpgsql"
|
77
90
|
|
91
|
+
# Could not dump table "_ag_label_edge" because of following StandardError
|
92
|
+
# Unknown type 'graphid' for column 'id'
|
93
|
+
|
94
|
+
# Could not dump table "_ag_label_vertex" because of following StandardError
|
95
|
+
# Unknown type 'graphid' for column 'id'
|
96
|
+
|
97
|
+
# Could not dump table "ag_graph" because of following StandardError
|
98
|
+
# Unknown type 'regnamespace' for column 'namespace'
|
99
|
+
|
100
|
+
# Could not dump table "ag_label" because of following StandardError
|
101
|
+
# Unknown type 'regclass' for column 'relation'
|
102
|
+
|
103
|
+
add_foreign_key "ag_label", "ag_graph", column: "graph", primary_key: "graphid", name: "fk_graph_oid"
|
104
|
+
|
105
|
+
# other migrations
|
106
|
+
# ...
|
107
|
+
end
|
108
|
+
```
|
109
|
+
|
110
|
+
and replace them with the following lines:
|
111
|
+
```ruby
|
112
|
+
ActiveRecord::Schema[7.1].define(version: 2024_05_21_062349) do
|
113
|
+
# These are extensions that must be enabled in order to support this database
|
114
|
+
enable_extension 'plpgsql'
|
115
|
+
|
78
116
|
# Allow age extension
|
79
117
|
execute('CREATE EXTENSION IF NOT EXISTS age;')
|
80
118
|
|
@@ -92,9 +130,35 @@ ActiveRecord::Schema[7.1].define(version: 2024_05_21_062349) do
|
|
92
130
|
end
|
93
131
|
```
|
94
132
|
|
133
|
+
NOTE: if using `db/structure.sql` use:
|
134
|
+
```sql
|
135
|
+
-- These are extensions that must be enabled in order to support this database
|
136
|
+
CREATE EXTENSION IF NOT EXISTS plpgsql WITH SCHEMA public;
|
137
|
+
|
138
|
+
-- Allow age extension (if not already enabled), this builds the age_catalog schema
|
139
|
+
CREATE EXTENSION IF NOT EXISTS age;
|
140
|
+
|
141
|
+
-- Load the age module
|
142
|
+
LOAD 'age';
|
143
|
+
|
144
|
+
-- Load the ag_catalog into the search path
|
145
|
+
SET search_path = ag_catalog, "$user", public;
|
146
|
+
|
147
|
+
-- Create age_schema graph if it doesn't exist
|
148
|
+
SELECT create_graph('age_schema');
|
149
|
+
|
150
|
+
# other migrations
|
151
|
+
# ...
|
152
|
+
|
153
|
+
INSERT INTO "schema_migrations" (version) VALUES
|
154
|
+
('20110315075839'),
|
155
|
+
--- ...
|
156
|
+
('20240521062349');
|
157
|
+
```
|
158
|
+
|
95
159
|
## Contributing
|
96
160
|
|
97
|
-
Create an
|
161
|
+
Create an merge request (with tests) and I will review it/merge it when ready.
|
98
162
|
|
99
163
|
## License
|
100
164
|
|
@@ -102,9 +166,12 @@ The gem is available as open source under the terms of the [MIT License](https:/
|
|
102
166
|
|
103
167
|
## Usage
|
104
168
|
|
169
|
+
I suggest you creat a folder create a folder called `app/nodes` and `app/edges` to keep the code organized.
|
170
|
+
I frequently use the `app/graphs` folder to keep all the graph related code together in a Module (as is done in the [rails age demo app](https://github.com/marpori/rails_age_demo_app))
|
171
|
+
|
105
172
|
I suggest you creat a folder within app called `graphs` and under that create a folder called `nodes` and `edges`. This will help you keep your code organized.
|
106
173
|
|
107
|
-
A
|
174
|
+
A trival, but fully functional [rails age demo app](https://github.com/marpori/rails_age_demo_app), based on the Flintstones Commic, is available for reference.
|
108
175
|
|
109
176
|
### Nodes
|
110
177
|
|
@@ -115,7 +182,12 @@ module Nodes
|
|
115
182
|
include ApacheAge::Entities::Vertex
|
116
183
|
|
117
184
|
attribute :company_name, :string
|
185
|
+
|
118
186
|
validates :company_name, presence: true
|
187
|
+
validates_with(
|
188
|
+
ApacheAge::Validators::UniqueVertexValidator,
|
189
|
+
attributes: [:company_name]
|
190
|
+
)
|
119
191
|
end
|
120
192
|
end
|
121
193
|
```
|
@@ -148,15 +220,30 @@ end
|
|
148
220
|
### Edges
|
149
221
|
|
150
222
|
```ruby
|
151
|
-
# app/graphs/edges/
|
223
|
+
# app/graphs/edges/has_job.rb
|
152
224
|
module Edges
|
153
225
|
class HasJob
|
154
226
|
include ApacheAge::Entities::Edge
|
155
227
|
|
156
228
|
attribute :employee_role, :string
|
157
|
-
attribute :start_node, :person
|
158
|
-
|
229
|
+
attribute :start_node, :person
|
230
|
+
attribute :end_node, :company
|
231
|
+
|
159
232
|
validates :employee_role, presence: true
|
233
|
+
validate :validate_unique
|
234
|
+
# or with a one-liner
|
235
|
+
# validates_with(
|
236
|
+
# ApacheAge::Validators::UniqueEdgeValidator,
|
237
|
+
# attributes: %i[employee_role start_node end_node]
|
238
|
+
# )
|
239
|
+
|
240
|
+
private
|
241
|
+
|
242
|
+
def validate_unique
|
243
|
+
ApacheAge::Validators::UniqueEdgeValidator
|
244
|
+
.new(attributes: %i[employee_role start_node end_node])
|
245
|
+
.validate(self)
|
246
|
+
end
|
160
247
|
end
|
161
248
|
end
|
162
249
|
```
|
data/db/schema.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
ActiveRecord::Schema[7.1].define(version: 2024_05_21_062349) do
|
2
2
|
# These are extensions that must be enabled in order to support this database
|
3
|
-
enable_extension
|
3
|
+
enable_extension 'plpgsql'
|
4
4
|
|
5
5
|
# Allow age extension
|
6
6
|
execute('CREATE EXTENSION IF NOT EXISTS age;')
|
@@ -8,29 +8,15 @@ module ApacheAge
|
|
8
8
|
instance
|
9
9
|
end
|
10
10
|
|
11
|
-
def
|
12
|
-
|
13
|
-
attributes
|
14
|
-
.compact
|
15
|
-
.except(:end_id, :start_id, :end_node, :start_node)
|
16
|
-
.map { |k, v| "find.#{k} = '#{v}'" }.join(' AND ')
|
17
|
-
where_attribs = where_attribs.empty? ? nil : where_attribs
|
18
|
-
|
19
|
-
end_id = attributes[:end_id] || attributes[:end_node]&.id
|
20
|
-
start_id = attributes[:start_id] || attributes[:start_node]&.id
|
21
|
-
where_end_id = end_id ? "id(end_node) = #{end_id}" : nil
|
22
|
-
where_start_id = start_id ? "id(start_node) = #{start_id}" : nil
|
23
|
-
|
24
|
-
where_clause = [where_attribs, where_start_id, where_end_id].compact.join(' AND ')
|
25
|
-
return nil if where_clause.empty?
|
11
|
+
def find_by(attributes)
|
12
|
+
return nil if attributes.reject{ |k,v| v.blank? }.empty?
|
26
13
|
|
27
|
-
|
28
|
-
|
29
|
-
end
|
14
|
+
edge_keys = [:start_id, :start_node, :end_id, :end_node]
|
15
|
+
return find_edge(attributes) if edge_keys.any? { |key| attributes.include?(key) }
|
30
16
|
|
31
|
-
def find_by(attributes)
|
32
17
|
where_clause = attributes.map { |k, v| "find.#{k} = '#{v}'" }.join(' AND ')
|
33
18
|
cypher_sql = find_sql(where_clause)
|
19
|
+
|
34
20
|
execute_find(cypher_sql)
|
35
21
|
end
|
36
22
|
|
@@ -55,6 +41,27 @@ module ApacheAge
|
|
55
41
|
|
56
42
|
# Private stuff
|
57
43
|
|
44
|
+
def find_edge(attributes)
|
45
|
+
where_attribs =
|
46
|
+
attributes
|
47
|
+
.compact
|
48
|
+
.except(:end_id, :start_id, :end_node, :start_node)
|
49
|
+
.map { |k, v| "find.#{k} = '#{v}'" }.join(' AND ')
|
50
|
+
where_attribs = where_attribs.empty? ? nil : where_attribs
|
51
|
+
|
52
|
+
end_id = attributes[:end_id] || attributes[:end_node]&.id
|
53
|
+
start_id = attributes[:start_id] || attributes[:start_node]&.id
|
54
|
+
where_end_id = end_id ? "id(end_node) = #{end_id}" : nil
|
55
|
+
where_start_id = start_id ? "id(start_node) = #{start_id}" : nil
|
56
|
+
|
57
|
+
where_clause = [where_attribs, where_start_id, where_end_id].compact.join(' AND ')
|
58
|
+
return nil if where_clause.empty?
|
59
|
+
|
60
|
+
cypher_sql = find_edge_sql(where_clause)
|
61
|
+
|
62
|
+
execute_find(cypher_sql)
|
63
|
+
end
|
64
|
+
|
58
65
|
def age_graph = 'age_schema'
|
59
66
|
def age_label = name.gsub('::', '__')
|
60
67
|
def age_type = name.constantize.new.age_type
|
@@ -32,7 +32,7 @@ module ApacheAge
|
|
32
32
|
end
|
33
33
|
return if attributes.blank? && (end_query.blank? || start_query.blank?)
|
34
34
|
|
35
|
-
query = record.class.
|
35
|
+
query = record.class.find_by(edge_attribs.compact)
|
36
36
|
return if query.blank? || (query.id == record.id)
|
37
37
|
|
38
38
|
record.errors.add(:base, 'attribute combination not unique')
|
data/lib/rails_age/version.rb
CHANGED
@@ -0,0 +1,33 @@
|
|
1
|
+
# lib/tasks/install.rake
|
2
|
+
# Usage:
|
3
|
+
# * `bin/rails apache_age:copy_migrations`
|
4
|
+
# * `bundle exec rails apache_age:copy_migrations[destination_path.to_s]`
|
5
|
+
# * `bundle exec rails apache_age:copy_migrations.invoke(destination_path.to_s)`
|
6
|
+
namespace :apache_age do
|
7
|
+
desc "Copy migrations from rails_age to application and update schema"
|
8
|
+
task :copy_migrations, [:destination_path] => :environment do |t, args|
|
9
|
+
source = File.expand_path('../../../db/migrate', __FILE__)
|
10
|
+
destination_path =
|
11
|
+
File.expand_path(args[:destination_path].presence || "#{Rails.root}/db/migrate", __FILE__)
|
12
|
+
|
13
|
+
FileUtils.mkdir_p(destination_path) unless File.exist?(destination_path)
|
14
|
+
existing_migrations =
|
15
|
+
Dir.glob("#{destination_path}/*.rb").map { |file| File.basename(file).sub(/^\d+/, '') }
|
16
|
+
|
17
|
+
Dir.glob("#{source}/*.rb").each do |file|
|
18
|
+
filename = File.basename(file)
|
19
|
+
test_name = filename.sub(/^\d+/, '')
|
20
|
+
|
21
|
+
if existing_migrations.include?(test_name)
|
22
|
+
puts "Skipping migration: '#{filename}', it already exists"
|
23
|
+
else
|
24
|
+
migration_version = Time.now.utc.strftime("%Y_%m_%d_%H%M%S")
|
25
|
+
file_version = migration_version.delete('_')
|
26
|
+
new_filename = filename.sub(/^\d+/, file_version)
|
27
|
+
destination_file = File.join(destination_path, new_filename)
|
28
|
+
FileUtils.cp(file, destination_file)
|
29
|
+
puts "Created migration: '#{new_filename}'"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
# lib/tasks/install.rake
|
2
|
+
# Usage: `rake apache_age:copy_migrations`
|
3
|
+
#
|
4
|
+
namespace :apache_age do
|
5
|
+
desc "Ensure the database.yml file is properly configured for Apache Age"
|
6
|
+
task :database_config => :environment do
|
7
|
+
|
8
|
+
db_config_file = File.expand_path("#{Rails.root}/config/database.yml", __FILE__)
|
9
|
+
|
10
|
+
# Read the file
|
11
|
+
lines = File.readlines(db_config_file)
|
12
|
+
|
13
|
+
# any uncommented "schema_search_path:" lines?
|
14
|
+
path_index = lines.find_index { |line| !line.include?('#') && line.include?('schema_search_path:') }
|
15
|
+
default_start_index = lines.index { |line| line.strip.start_with?('default:') }
|
16
|
+
|
17
|
+
# when it finds an existing schema_search_path, it updates it
|
18
|
+
if path_index && lines[path_index].include?('ag_catalog,age_schema')
|
19
|
+
puts "the schema_search_path in config/database.yml is already properly set, nothing to do."
|
20
|
+
else
|
21
|
+
if path_index
|
22
|
+
key, val = lines[path_index].split(': ')
|
23
|
+
# remove any unwanted characters
|
24
|
+
val = val.gsub(/[ "\s\"\"'\n]/, '')
|
25
|
+
lines[path_index] = "#{key}: ag_catalog,age_schema,#{val}\n"
|
26
|
+
puts "added ag_catalog,age_schema to schema_search_path in config/database.yml"
|
27
|
+
elsif default_start_index
|
28
|
+
puts "the schema_search_path in config/database.yml is now properly set."
|
29
|
+
sections_index = lines.map.with_index { |line, index| index if !line.start_with?(' ') }.compact.sort
|
30
|
+
|
31
|
+
# find the start of the default section
|
32
|
+
next_section_in_list = sections_index.index(default_start_index) + 1
|
33
|
+
|
34
|
+
# find the end of the default section (before the next section starts)
|
35
|
+
path_insert_index = sections_index[next_section_in_list]
|
36
|
+
|
37
|
+
lines.insert(path_insert_index, " schema_search_path: ag_catalog,age_schema,public\n")
|
38
|
+
else
|
39
|
+
puts "didn't find a default section in database.yml, please add the following line:"
|
40
|
+
puts " schema_search_path: ag_catalog,age_schema,public"
|
41
|
+
puts "to the apprpriate section of your database.yml"
|
42
|
+
end
|
43
|
+
|
44
|
+
# Write the modified lines back to the file
|
45
|
+
File.open(db_config_file, 'w') { |file| file.write(lines.join) }
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# # lib/tasks/install.rake
|
51
|
+
# # Usage: `rake apache_age:copy_migrations`
|
52
|
+
# #
|
53
|
+
# namespace :apache_age do
|
54
|
+
# desc "Ensure the database.yml file is properly configured for Apache Age"
|
55
|
+
# task :database_config, [:destination_path] => :environment do |t, args|
|
56
|
+
# destination_path =
|
57
|
+
# File.expand_path(args[:destination_path].presence || "#{Rails.root}/config", __FILE__)
|
58
|
+
|
59
|
+
# db_config_file = File.expand_path("#{destination_path.to_s}/database.yml", __FILE__)
|
60
|
+
|
61
|
+
# # Read the file
|
62
|
+
# lines = File.readlines(db_config_file)
|
63
|
+
|
64
|
+
# # any uncommented "schema_search_path:" lines?
|
65
|
+
# path_index = lines.find_index { |line| !line.include?('#') && line.include?('schema_search_path:') }
|
66
|
+
# default_start_index = lines.index { |line| line.strip.start_with?('default:') }
|
67
|
+
|
68
|
+
# # when it finds an existing schema_search_path, it updates it
|
69
|
+
# if path_index && lines[path_index].include?('ag_catalog,age_schema')
|
70
|
+
# puts "the schema_search_path in config/database.yml is already properly set, nothing to do."
|
71
|
+
# else
|
72
|
+
# if path_index
|
73
|
+
# key, val = lines[path_index].split(': ')
|
74
|
+
# # remove any unwanted characters
|
75
|
+
# val = val.gsub(/[ "\s\"\"'\n]/, '')
|
76
|
+
# lines[path_index] = "#{key}: ag_catalog,age_schema,#{val}\n"
|
77
|
+
# puts "added ag_catalog,age_schema to schema_search_path in config/database.yml"
|
78
|
+
# elsif default_start_index
|
79
|
+
# puts "the schema_search_path in config/database.yml is now properly set."
|
80
|
+
# sections_index = lines.map.with_index { |line, index| index if !line.start_with?(' ') }.compact.sort
|
81
|
+
|
82
|
+
# # find the start of the default section
|
83
|
+
# next_section_in_list = sections_index.index(default_start_index) + 1
|
84
|
+
|
85
|
+
# # find the end of the default section (before the next section starts)
|
86
|
+
# path_insert_index = sections_index[next_section_in_list]
|
87
|
+
|
88
|
+
# lines.insert(path_insert_index, " schema_search_path: ag_catalog,age_schema,public\n")
|
89
|
+
# else
|
90
|
+
# puts "didn't find a default section in database.yml, please add the following line:"
|
91
|
+
# puts " schema_search_path: ag_catalog,age_schema,public"
|
92
|
+
# puts "to the apprpriate section of your database.yml"
|
93
|
+
# end
|
94
|
+
|
95
|
+
# # Write the modified lines back to the file
|
96
|
+
# File.open(db_config_file, 'w') { |file| file.write(lines.join) }
|
97
|
+
# end
|
98
|
+
# end
|
99
|
+
# end
|
@@ -0,0 +1,257 @@
|
|
1
|
+
# lib/tasks/install.rake
|
2
|
+
# Usage: `rake apache_age:install`
|
3
|
+
#
|
4
|
+
namespace :apache_age do
|
5
|
+
desc "Install & configure Apache Age within Rails (updates migrations, schema & database.yml)"
|
6
|
+
task :install_old => :environment do
|
7
|
+
source_schema = File.expand_path('../../../db/schema.rb', __FILE__)
|
8
|
+
destination_schema = File.expand_path("#{Rails.root}/db/schema.rb", __FILE__)
|
9
|
+
source_migrations = File.expand_path('../../../db/migrate', __FILE__)
|
10
|
+
destination_migrations = File.expand_path("#{Rails.root}/db/migrate", __FILE__)
|
11
|
+
# create the migrations folder if needed
|
12
|
+
FileUtils.mkdir_p(destination_migrations) unless File.exist?(destination_migrations)
|
13
|
+
original_migrations =
|
14
|
+
Dir.glob("#{destination_migrations}/*.rb").map { |file| File.basename(file).sub(/^\d+/, '') }
|
15
|
+
|
16
|
+
# # check if the schema is non-existent or blank (NEW) we need to know how to handle schema
|
17
|
+
# is_schema_blank = !File.exist?(destination_schema) || blank_schema?(destination_schema)
|
18
|
+
# puts "Schema is blank: #{is_schema_blank}"
|
19
|
+
|
20
|
+
# ensure we have a schema file
|
21
|
+
unless File.exist?(destination_schema)
|
22
|
+
run_db_create
|
23
|
+
run_db_migrate
|
24
|
+
end
|
25
|
+
|
26
|
+
# copy our migrations to the application (last_migration_version is nil if no migration necessary)
|
27
|
+
# last_migration_version = copy_migrations
|
28
|
+
Rake::Task["apache_age:copy_migrations"].invoke
|
29
|
+
|
30
|
+
updated_migrations =
|
31
|
+
Dir.glob("#{destination_migrations}/*.rb").map { |file| File.basename(file).sub(/^\d+/, '') }
|
32
|
+
|
33
|
+
# # run our new migrations (unless we have not added any new migrations)
|
34
|
+
# if original_migrations == updated_migrations
|
35
|
+
# puts "no new migrations were copied, skipping migrations"
|
36
|
+
# else
|
37
|
+
# puts "added Apache Age migrations, running migrations"
|
38
|
+
# run_db_migrate
|
39
|
+
# end
|
40
|
+
run_db_migrate
|
41
|
+
|
42
|
+
# adjust the schema file (unfortunately rails mangles the schema file)
|
43
|
+
# if is_schema_blank
|
44
|
+
# puts "creating new schema..."
|
45
|
+
# create_new_schema(last_migration_version, destination_schema, source_schema)
|
46
|
+
# else
|
47
|
+
# puts "updating existing schema..."
|
48
|
+
# update_existing_schema(last_migration_version, destination_schema, source_schema)
|
49
|
+
# end
|
50
|
+
Rake::Task["apache_age:schema_config"].invoke
|
51
|
+
|
52
|
+
# ensure the config/database.yml file has the proper configurations
|
53
|
+
# update_database_yml
|
54
|
+
Rake::Task["apache_age:database_config"].invoke
|
55
|
+
end
|
56
|
+
|
57
|
+
def run_db_create
|
58
|
+
puts "Running db:create..."
|
59
|
+
Rake::Task["db:create"].invoke
|
60
|
+
end
|
61
|
+
|
62
|
+
def run_db_migrate
|
63
|
+
puts "Running db:migrate..."
|
64
|
+
Rake::Task["db:migrate"].invoke
|
65
|
+
end
|
66
|
+
|
67
|
+
def copy_migrations
|
68
|
+
migration_version = nil
|
69
|
+
|
70
|
+
source = File.expand_path('../../../db/migrate', __FILE__)
|
71
|
+
destination = File.expand_path("#{Rails.root}/db/migrate", __FILE__)
|
72
|
+
|
73
|
+
FileUtils.mkdir_p(destination) unless File.exist?(destination)
|
74
|
+
existing_migrations =
|
75
|
+
Dir.glob("#{destination}/*.rb").map { |file| File.basename(file).sub(/^\d+/, '') }
|
76
|
+
|
77
|
+
Dir.glob("#{source}/*.rb").each do |file|
|
78
|
+
filename = File.basename(file)
|
79
|
+
test_name = filename.sub(/^\d+/, '')
|
80
|
+
|
81
|
+
if existing_migrations.include?(test_name)
|
82
|
+
puts "Skipping #{filename}, it already exists"
|
83
|
+
else
|
84
|
+
migration_version = Time.now.utc.strftime("%Y_%m_%d_%H%M%S")
|
85
|
+
file_version = migration_version.delete('_')
|
86
|
+
new_filename = filename.sub(/^\d+/, file_version)
|
87
|
+
destination_file = File.join(destination, new_filename)
|
88
|
+
FileUtils.cp(file, destination_file)
|
89
|
+
puts "Copied #{filename} to #{destination} as #{new_filename}"
|
90
|
+
end
|
91
|
+
end
|
92
|
+
migration_version
|
93
|
+
end
|
94
|
+
|
95
|
+
def blank_schema?(destination_schema)
|
96
|
+
return false unless File.exist?(destination_schema)
|
97
|
+
|
98
|
+
content = File.read(destination_schema)
|
99
|
+
content.include?('define(version: 0)') &&
|
100
|
+
(content.include?("enable_extension 'plpgsql'") || content.include?('enable_extension "plpgsql"')) &&
|
101
|
+
content.scan(/enable_extension/).size == 1
|
102
|
+
end
|
103
|
+
|
104
|
+
def schema_rails_version(destination_schema)
|
105
|
+
if File.exist?(destination_schema)
|
106
|
+
content = File.read(destination_schema)
|
107
|
+
version_match = content.match(/ActiveRecord::Schema\[(.*?)\]/)
|
108
|
+
return version_match[1] if version_match
|
109
|
+
else
|
110
|
+
full_version = Rails.version
|
111
|
+
primary_secondary_version = full_version.split('.')[0..1].join('.')
|
112
|
+
primary_secondary_version
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def create_new_schema(last_migration_version, destination_schema, source_schema)
|
117
|
+
if File.exist?(source_schema) && File.exist?(destination_schema)
|
118
|
+
rails_version = schema_rails_version(destination_schema)
|
119
|
+
source_content = File.read(source_schema)
|
120
|
+
|
121
|
+
# ensure we use the Rails version from the destination schema
|
122
|
+
source_content.gsub!(
|
123
|
+
/ActiveRecord::Schema\[\d+\.\d+\]/,
|
124
|
+
"ActiveRecord::Schema[#{rails_version}]"
|
125
|
+
)
|
126
|
+
# ensure we use the last migration version (not the source schema version)
|
127
|
+
source_content.gsub!(
|
128
|
+
/define\(version: \d{4}(?:_\d{2}){2}(?:_\d{6})?\) do/,
|
129
|
+
"define(version: #{last_migration_version}) do"
|
130
|
+
)
|
131
|
+
|
132
|
+
File.write(destination_schema, source_content)
|
133
|
+
puts "Created new schema in #{destination_schema} with necessary extensions and configurations."
|
134
|
+
else
|
135
|
+
puts "local db/schema.rb file not found."
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
def update_existing_schema(last_migration_version, destination_schema, source_schema)
|
140
|
+
if File.exist?(source_schema) && File.exist?(destination_schema)
|
141
|
+
rails_version = schema_rails_version(destination_schema)
|
142
|
+
source_content = File.read(source_schema)
|
143
|
+
new_content =
|
144
|
+
source_content.gsub(
|
145
|
+
/.*ActiveRecord::Schema\[\d+\.\d+\]\.define\(version: \d{4}(?:_\d{2}){2}(?:_\d{6})?\) do\n|\nend$/,
|
146
|
+
''
|
147
|
+
)
|
148
|
+
|
149
|
+
destination_content = File.read(destination_schema)
|
150
|
+
|
151
|
+
# Remove unwanted schema statements
|
152
|
+
destination_content.gsub!(%r{^.*?create_schema "ag_catalog".*?\n}, '')
|
153
|
+
destination_content.gsub!(%r{^.*?create_schema "age_schema".*?\n}, '')
|
154
|
+
destination_content.gsub!(%r{^.*?enable_extension "age".*?\n}, '')
|
155
|
+
destination_content.gsub!(%r{^.*?enable_extension "plpgsql".*?\n}, '')
|
156
|
+
destination_content.gsub!(%r{^.*?# Could not dump table "ag_graph" because of following StandardError.*?\n}, '')
|
157
|
+
destination_content.gsub!(%r{^.*?# Unknown type 'regnamespace' for column 'namespace'.*?\n}, '')
|
158
|
+
destination_content.gsub!(%r{^.*?# Could not dump table "ag_label" because of following StandardError.*?\n}, '')
|
159
|
+
destination_content.gsub!(%r{^.*?# Unknown type 'regclass' for column 'relation'.*?\n}, '')
|
160
|
+
destination_content.gsub!(%r{^.*?# Unknown type 'graphid' for column 'id'.*?\n}, '')
|
161
|
+
destination_content.gsub!(
|
162
|
+
%r{^.*?# Could not dump table "_ag_label_edge" because of following StandardError.*?\n}, ''
|
163
|
+
)
|
164
|
+
destination_content.gsub!(
|
165
|
+
%r{^.*?# Could not dump table "_ag_label_vertex" because of following StandardError.*?\n}, ''
|
166
|
+
)
|
167
|
+
destination_content.gsub!(%r{^.*?# Could not dump table "ag_graph" because of following StandardError.*?\n}, '')
|
168
|
+
destination_content.gsub!(%r{^.*?# Could not dump table "ag_label" because of following StandardError.*?\n}, '')
|
169
|
+
destination_content.gsub!(%r{^.*?add_foreign_key "ag_label", "ag_graph".*?\n}, '')
|
170
|
+
|
171
|
+
# add new wanted schema statements (at the top of the schema)
|
172
|
+
unless destination_content.include?(%{execute("LOAD 'age';")}) &&
|
173
|
+
destination_content.include?(%{enable_extension 'plpgsql'}) &&
|
174
|
+
destination_content.include?(%{execute("SELECT create_graph('age_schema');")}) &&
|
175
|
+
destination_content.include?(%{execute('CREATE EXTENSION IF NOT EXISTS age;')}) &&
|
176
|
+
destination_content.include?(%{execute('SET search_path = ag_catalog, "$user", public;')})
|
177
|
+
# if not all are found then remove any found
|
178
|
+
destination_content.gsub!(%r{^.*?execute("LOAD 'age';")*?\n}, '')
|
179
|
+
destination_content.gsub!(%r{^.*?enable_extension 'plpgsql'*?\n}, '')
|
180
|
+
destination_content.gsub!(%r{^.*?execute("SELECT create_graph('age_schema');")*?\n}, '')
|
181
|
+
destination_content.gsub!(%r{^.*?execute('CREATE EXTENSION IF NOT EXISTS age;')*?\n}, '')
|
182
|
+
destination_content.gsub!(%r{^.*?execute('SET search_path = ag_catalog, "$user", public;')*?\n}, '')
|
183
|
+
destination_content.gsub!(%r{^.*?# Allow age extension*?\n}, '')
|
184
|
+
destination_content.gsub!(%r{^.*?# Load the ag_catalog into the search path*?\n}, '')
|
185
|
+
destination_content.gsub!(%r{^.*?# Create age_schema graph if it doesn't exist*?\n}, '')
|
186
|
+
destination_content.gsub!(%r{^.*?# These are extensions that must be enabled in order to support this database*?\n}, '')
|
187
|
+
|
188
|
+
# add all of the correct settings back in
|
189
|
+
destination_content.sub!(
|
190
|
+
%r{(ActiveRecord::Schema\[\d+\.\d+\]\.define\(version: \d{4}(?:_\d{2}){2}(?:_\d{6})?\) do\n)},
|
191
|
+
"\\1#{new_content}\n"
|
192
|
+
)
|
193
|
+
puts 'db/schema.rb has been updated with the necessary configuration.'
|
194
|
+
else
|
195
|
+
puts 'db/schema.rb has the necessary configuration, no adjustments necessary'
|
196
|
+
end
|
197
|
+
|
198
|
+
existing_version = destination_content.match(/define\(version: (\d{4}(?:_\d{2}){2}(?:_\d{6})?)\)/)[1].gsub('_', '')
|
199
|
+
current_version = last_migration_version ? last_migration_version.gsub('_', '') : existing_version
|
200
|
+
|
201
|
+
# ensure we use the last migration version (not the source schema version)
|
202
|
+
if current_version.to_i > existing_version.to_i
|
203
|
+
destination_content.gsub!(
|
204
|
+
/define\(version: \d{4}(?:_\d{2}){2}(?:_\d{6})?\) do/,
|
205
|
+
"define(version: #{last_migration_version}) do"
|
206
|
+
)
|
207
|
+
puts "Updated schema version to the migration version #{last_migration_version}"
|
208
|
+
end
|
209
|
+
|
210
|
+
File.write(destination_schema, destination_content)
|
211
|
+
puts "Updated #{destination_schema} with necessary extensions and configurations."
|
212
|
+
else
|
213
|
+
puts "local db/schema.rb file not found."
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
def update_database_yml
|
218
|
+
db_config_file = File.expand_path("#{Rails.root}/config/database.yml", __FILE__)
|
219
|
+
|
220
|
+
# Read the file
|
221
|
+
lines = File.readlines(db_config_file)
|
222
|
+
|
223
|
+
# any uncommented "schema_search_path:" lines?
|
224
|
+
path_index = lines.find_index { |line| !line.include?('#') && line.include?('schema_search_path:') }
|
225
|
+
default_start_index = lines.index { |line| line.strip.start_with?('default:') }
|
226
|
+
|
227
|
+
# when it finds an existing schema_search_path, it updates it
|
228
|
+
if path_index && lines[path_index].include?('ag_catalog,age_schema')
|
229
|
+
puts "schema_search_path already set to ag_catalog,age_schema nothing to do."
|
230
|
+
return
|
231
|
+
elsif path_index
|
232
|
+
key, val = lines[path_index].split(': ')
|
233
|
+
# remove any unwanted characters
|
234
|
+
val = val.gsub(/[ "\s\"\"'\n]/, '')
|
235
|
+
lines[path_index] = "#{key}: ag_catalog,age_schema,#{val}\n"
|
236
|
+
puts "add ag_catalog,age_schema to schema_search_path"
|
237
|
+
elsif default_start_index
|
238
|
+
puts "add ag_catalog,age_schema,public to schema_search_path in the default section of database.yml"
|
239
|
+
sections_index = lines.map.with_index { |line, index| index if !line.start_with?(' ') }.compact.sort
|
240
|
+
|
241
|
+
# find the start of the default section
|
242
|
+
next_section_in_list = sections_index.index(default_start_index) + 1
|
243
|
+
|
244
|
+
# find the end of the default section (before the next section starts)
|
245
|
+
path_insert_index = sections_index[next_section_in_list]
|
246
|
+
|
247
|
+
lines.insert(path_insert_index, " schema_search_path: ag_catalog,age_schema,public\n")
|
248
|
+
else
|
249
|
+
puts "didn't find a default section in database.yml, please add the following line:"
|
250
|
+
puts " schema_search_path: ag_catalog,age_schema,public"
|
251
|
+
puts "to the apprpriate section of your database.yml"
|
252
|
+
end
|
253
|
+
|
254
|
+
# Write the modified lines back to the file
|
255
|
+
File.open(db_config_file, 'w') { |file| file.write(lines.join) }
|
256
|
+
end
|
257
|
+
end
|
data/lib/tasks/install.rake
CHANGED
@@ -1,91 +1,19 @@
|
|
1
1
|
# lib/tasks/install.rake
|
2
|
-
# Usage: `rake
|
2
|
+
# Usage: `rake apache_age:install`
|
3
3
|
#
|
4
|
-
namespace :
|
5
|
-
desc "
|
4
|
+
namespace :apache_age do
|
5
|
+
desc "Install & configure Apache Age within Rails (updates migrations, schema & database.yml)"
|
6
6
|
task :install => :environment do
|
7
|
-
|
8
|
-
|
7
|
+
# copy our migrations to the application (if needed)
|
8
|
+
Rake::Task["apache_age:copy_migrations"].invoke
|
9
9
|
|
10
|
-
|
10
|
+
# run any new migrations
|
11
|
+
Rake::Task["db:migrate"].invoke
|
11
12
|
|
12
|
-
|
13
|
-
|
14
|
-
destination_file = File.join(destination, filename)
|
13
|
+
# adjust the schema file (unfortunately rails mangles the schema file)
|
14
|
+
Rake::Task["apache_age:schema_config"].invoke
|
15
15
|
|
16
|
-
|
17
|
-
|
18
|
-
else
|
19
|
-
FileUtils.cp(file, destination_file)
|
20
|
-
puts "Copied #{filename} to #{destination}"
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
# Update the schema.rb file
|
25
|
-
schema_file = File.expand_path('../../../../db/schema.rb', __FILE__)
|
26
|
-
if File.exists?(schema_file)
|
27
|
-
content = File.read(schema_file)
|
28
|
-
|
29
|
-
# Add the necessary extensions and configurations at the top of the schema
|
30
|
-
insert_statements = <<-RUBY
|
31
|
-
|
32
|
-
# These are extensions that must be enabled in order to support this database
|
33
|
-
enable_extension "plpgsql"
|
34
|
-
|
35
|
-
# Allow age extension
|
36
|
-
execute('CREATE EXTENSION IF NOT EXISTS age;')
|
37
|
-
|
38
|
-
# Load the age code
|
39
|
-
execute("LOAD 'age';")
|
40
|
-
|
41
|
-
# Load the ag_catalog into the search path
|
42
|
-
execute('SET search_path = ag_catalog, "$user", public;')
|
43
|
-
|
44
|
-
# Create age_schema graph if it doesn't exist
|
45
|
-
execute("SELECT create_graph('age_schema');")
|
46
|
-
|
47
|
-
RUBY
|
48
|
-
|
49
|
-
unless content.include?(insert_statements.strip)
|
50
|
-
content.sub!(/^# These are extensions that must be enabled in order to support this database.*?\n\n/m, insert_statements)
|
51
|
-
end
|
52
|
-
|
53
|
-
# Remove unwanted schema statements
|
54
|
-
content.gsub!(/^.*?create_schema "ag_catalog".*?\n\n/m, '')
|
55
|
-
content.gsub!(/^.*?create_schema "age_schema".*?\n\n/m, '')
|
56
|
-
content.gsub!(/^.*?enable_extension "age".*?\n\n/m, '')
|
57
|
-
content.gsub!(/^.*?# Could not dump table "_ag_label_edge" because of following StandardError.*?\n\n/m, '')
|
58
|
-
content.gsub!(/^.*?# Could not dump table "_ag_label_vertex" because of following StandardError.*?\n\n/m, '')
|
59
|
-
content.gsub!(/^.*?# Could not dump table "ag_graph" because of following StandardError.*?\n\n/m, '')
|
60
|
-
content.gsub!(/^.*?# Could not dump table "ag_label" because of following StandardError.*?\n\n/m, '')
|
61
|
-
content.gsub!(/^.*?add_foreign_key "ag_label", "ag_graph".*?\n\n/m, '')
|
62
|
-
|
63
|
-
File.write(schema_file, content)
|
64
|
-
puts "Updated #{schema_file} with necessary extensions and configurations."
|
65
|
-
else
|
66
|
-
puts "schema.rb file not found. Please ensure migrations have been run."
|
67
|
-
end
|
16
|
+
# ensure the config/database.yml file has the proper configurations
|
17
|
+
Rake::Task["apache_age:database_config"].invoke
|
68
18
|
end
|
69
19
|
end
|
70
|
-
|
71
|
-
# namespace :rails_age do
|
72
|
-
# desc "Copy migrations from rails_age to application"
|
73
|
-
# task :install => :environment do
|
74
|
-
# source = File.expand_path('../../../db/migrate', __FILE__)
|
75
|
-
# destination = File.expand_path('../../../../db/migrate', __FILE__)
|
76
|
-
|
77
|
-
# FileUtils.mkdir_p(destination) unless File.exists?(destination)
|
78
|
-
|
79
|
-
# Dir.glob("#{source}/*.rb").each do |file|
|
80
|
-
# filename = File.basename(file)
|
81
|
-
# destination_file = File.join(destination, filename)
|
82
|
-
|
83
|
-
# if File.exists?(destination_file)
|
84
|
-
# puts "Skipping #{filename}, it already exists"
|
85
|
-
# else
|
86
|
-
# FileUtils.cp(file, destination_file)
|
87
|
-
# puts "Copied #{filename} to #{destination}"
|
88
|
-
# end
|
89
|
-
# end
|
90
|
-
# end
|
91
|
-
# end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
# lib/tasks/install.rake
|
2
|
+
# Usage: `rake apache_age:schema_config`
|
3
|
+
#
|
4
|
+
namespace :apache_age do
|
5
|
+
desc "Copy migrations from rails_age to application and update schema"
|
6
|
+
task :schema_config => :environment do
|
7
|
+
# source_schema = File.expand_path('../../../db/schema.rb', __FILE__)
|
8
|
+
destination_schema = File.expand_path("#{Rails.root}/db/schema.rb", __FILE__)
|
9
|
+
|
10
|
+
unless File.exist?(destination_schema)
|
11
|
+
puts "local db/schema.rb file not found. please run db:create and db:migrate first"
|
12
|
+
else
|
13
|
+
destination_content = File.read(destination_schema)
|
14
|
+
|
15
|
+
# Remove unwanted schema statements
|
16
|
+
destination_content.gsub!(%r{^.*?create_schema "ag_catalog".*?\n}, '')
|
17
|
+
destination_content.gsub!(%r{^.*?create_schema "age_schema".*?\n}, '')
|
18
|
+
destination_content.gsub!(%r{^.*?enable_extension "age".*?\n}, '')
|
19
|
+
destination_content.gsub!(%r{^.*?enable_extension "plpgsql".*?\n}, '')
|
20
|
+
destination_content.gsub!(%r{^.*?# Could not dump table "ag_graph" because of following StandardError.*?\n}, '')
|
21
|
+
destination_content.gsub!(%r{^.*?# Unknown type 'regnamespace' for column 'namespace'.*?\n}, '')
|
22
|
+
destination_content.gsub!(%r{^.*?# Could not dump table "ag_label" because of following StandardError.*?\n}, '')
|
23
|
+
destination_content.gsub!(%r{^.*?# Unknown type 'regclass' for column 'relation'.*?\n}, '')
|
24
|
+
destination_content.gsub!(%r{^.*?# Unknown type 'graphid' for column 'id'.*?\n}, '')
|
25
|
+
destination_content.gsub!(
|
26
|
+
%r{^.*?# Could not dump table "_ag_label_edge" because of following StandardError.*?\n}, ''
|
27
|
+
)
|
28
|
+
destination_content.gsub!(
|
29
|
+
%r{^.*?# Could not dump table "_ag_label_vertex" because of following StandardError.*?\n}, ''
|
30
|
+
)
|
31
|
+
destination_content.gsub!(%r{^.*?# Could not dump table "ag_graph" because of following StandardError.*?\n}, '')
|
32
|
+
destination_content.gsub!(%r{^.*?# Could not dump table "ag_label" because of following StandardError.*?\n}, '')
|
33
|
+
destination_content.gsub!(%r{^.*?add_foreign_key "ag_label", "ag_graph".*?\n}, '')
|
34
|
+
|
35
|
+
# add necessary contents (as needed)
|
36
|
+
if destination_content.include?(%{execute("LOAD 'age';")}) &&
|
37
|
+
destination_content.include?(%{enable_extension 'plpgsql'}) &&
|
38
|
+
destination_content.include?(%{execute("SELECT create_graph('age_schema');")}) &&
|
39
|
+
destination_content.include?(%{execute('CREATE EXTENSION IF NOT EXISTS age;')}) &&
|
40
|
+
destination_content.include?(%{execute('SET search_path = ag_catalog, "$user", public;')})
|
41
|
+
puts "schema.rb is properly configured, nothing to do"
|
42
|
+
else
|
43
|
+
# if not all are found then remove any found
|
44
|
+
destination_content.gsub!(%r{^.*?execute("LOAD 'age';")*?\n}, '')
|
45
|
+
destination_content.gsub!(%r{^.*?enable_extension 'plpgsql'*?\n}, '')
|
46
|
+
destination_content.gsub!(%r{^.*?execute("SELECT create_graph('age_schema');")*?\n}, '')
|
47
|
+
destination_content.gsub!(%r{^.*?execute('CREATE EXTENSION IF NOT EXISTS age;')*?\n}, '')
|
48
|
+
destination_content.gsub!(%r{^.*?execute('SET search_path = ag_catalog, "$user", public;')*?\n}, '')
|
49
|
+
destination_content.gsub!(%r{^.*?# Allow age extension*?\n}, '')
|
50
|
+
destination_content.gsub!(%r{^.*?# Load the ag_catalog into the search path*?\n}, '')
|
51
|
+
destination_content.gsub!(%r{^.*?# Create age_schema graph if it doesn't exist*?\n}, '')
|
52
|
+
destination_content.gsub!(%r{^.*?# These are extensions that must be enabled in order to support this database*?\n}, '')
|
53
|
+
|
54
|
+
# add all of the correct settings back in
|
55
|
+
# source_content = File.read(source_schema)
|
56
|
+
source_content =
|
57
|
+
<<~RUBY
|
58
|
+
ActiveRecord::Schema[7.1].define(version: 2024_05_21_062349) do
|
59
|
+
# These are extensions that must be enabled in order to support this database
|
60
|
+
enable_extension 'plpgsql'
|
61
|
+
|
62
|
+
# Allow age extension
|
63
|
+
execute('CREATE EXTENSION IF NOT EXISTS age;')
|
64
|
+
|
65
|
+
# Load the age code
|
66
|
+
execute("LOAD 'age';")
|
67
|
+
|
68
|
+
# Load the ag_catalog into the search path
|
69
|
+
execute('SET search_path = ag_catalog, "$user", public;')
|
70
|
+
|
71
|
+
# Create age_schema graph if it doesn't exist
|
72
|
+
execute("SELECT create_graph('age_schema');")
|
73
|
+
end
|
74
|
+
RUBY
|
75
|
+
|
76
|
+
age_config_contents =
|
77
|
+
source_content.gsub(
|
78
|
+
/.*ActiveRecord::Schema\[\d+\.\d+\]\.define\(version: \d{4}(?:_\d{2}){2}(?:_\d{6})?\) do\n|\nend$/,
|
79
|
+
''
|
80
|
+
)
|
81
|
+
|
82
|
+
destination_content.sub!(
|
83
|
+
%r{(ActiveRecord::Schema\[\d+\.\d+\]\.define\(version: \d{4}(?:_\d{2}){2}(?:_\d{6})?\) do\n)},
|
84
|
+
"\\1#{age_config_contents}\n"
|
85
|
+
)
|
86
|
+
end
|
87
|
+
|
88
|
+
# Remove multiple consecutive empty lines
|
89
|
+
destination_content.gsub!(/\n{2,}/, "\n\n")
|
90
|
+
|
91
|
+
File.write(destination_schema, destination_content)
|
92
|
+
puts "The schema '#{destination_schema}' is ready to work with Apache Age."
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rails_age
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Bill Tihen
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-06-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -44,8 +44,8 @@ dependencies:
|
|
44
44
|
- - "~>"
|
45
45
|
- !ruby/object:Gem::Version
|
46
46
|
version: '6.0'
|
47
|
-
description: This plugin integrates Apache AGE with Rails 7.x, providing
|
48
|
-
helpers for working with graph databases within a Rails application.
|
47
|
+
description: This plugin integrates Apache AGE for PostgreSQL with Rails 7.x, providing
|
48
|
+
tools and helpers for working with graph databases within a Rails application.
|
49
49
|
email:
|
50
50
|
- btihen@gmail.com
|
51
51
|
executables: []
|
@@ -65,7 +65,7 @@ files:
|
|
65
65
|
- app/models/rails_age/application_record.rb
|
66
66
|
- app/views/layouts/rails_age/application.html.erb
|
67
67
|
- config/routes.rb
|
68
|
-
- db/migrate/
|
68
|
+
- db/migrate/20240521062349_add_apache_age.rb
|
69
69
|
- db/schema.rb
|
70
70
|
- lib/apache_age/entities/class_methods.rb
|
71
71
|
- lib/apache_age/entities/common_methods.rb
|
@@ -79,8 +79,11 @@ files:
|
|
79
79
|
- lib/rails_age.rb
|
80
80
|
- lib/rails_age/engine.rb
|
81
81
|
- lib/rails_age/version.rb
|
82
|
+
- lib/tasks/copy_migrations.rake
|
83
|
+
- lib/tasks/database_config.rake
|
84
|
+
- lib/tasks/install.original.rake
|
82
85
|
- lib/tasks/install.rake
|
83
|
-
- lib/tasks/
|
86
|
+
- lib/tasks/schema_config.rake
|
84
87
|
homepage: https://github.com/marpori/rails_age
|
85
88
|
licenses:
|
86
89
|
- MIT
|