rails_age 0.4.0 → 0.5.0

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
  SHA256:
3
- metadata.gz: 8228c60b43732a31b364b658e65b5d6a2e1b76018b573b1ec5b7fa25582f519c
4
- data.tar.gz: 60f5211cb37aa6605523f76cde81158b6495448e90d4fbeabecd020c38520c4e
3
+ metadata.gz: ccc1964e751ea0670eeab5cc406f1d73d0ac4a188c3f0c97ff410cf0a12aa664
4
+ data.tar.gz: 9de0e59661db92464c6505c9dc3d6a2eb314f35333eeb4f97d92d2f4b628275b
5
5
  SHA512:
6
- metadata.gz: eec16e9c7b4512793b4aac9a3ac1c9ee0ce6280a1033712cc6aed5466218ffa17a47c1fca4d1f4d0508ce38e4a12a9a0c392d927f72a62c5ebcf8d2ea7c343f5
7
- data.tar.gz: 3ccdeba21d32bc35de9162698c5c340efce5b0557b5930a3b1e0d9caaf5697f5afcd2b39ad65d0c976b5b3bc46df5edaf18a7bb96e023a2090642b08eb002786
6
+ metadata.gz: 15179f421eaa5fcf6180af0fd7865b1c69884064a2030b7f03300898eeda08d7bb2f871c43ec3b5ad1ae49db98b30cf274c582e3906fd66fe24b68812fb78b9a
7
+ data.tar.gz: 1a83d773effad75f2e90f89ee06f8585fa5a9fe40104df7913b0e0470d3026b7cc160ecb8e3ce3e2f2fcf7c1831a902fb61b1140974cc5f792d15e43ee3de4f1
data/CHANGELOG.md CHANGED
@@ -1,42 +1,59 @@
1
1
  # Change Log
2
2
 
3
- ## VERSION 0.5.0 - 2024-xx-xx
3
+ ## VERSION 0.6.2 - 2024-xx-xx
4
4
 
5
- - **AGE Schema override** (instance and class methods) assumes db and migrations up-to-date
6
-
7
- - **cypher**
5
+ - **cypher queries** (like active record queries)
8
6
  * schema override
9
7
  * query support
10
8
  * paths support
11
9
  * select attributes support
12
10
 
13
- - **Paths**
11
+ ## VERSION 0.6.1 - 2024-xx-xx
12
+
13
+ - **Age Path**
14
+
15
+ ## VERSION 0.6.0 - 2024-xx-xx
16
+
17
+ breaking change?: namespaces (by default) will use their own schema? (add to database.yml & schema.rb ?)
18
+
19
+ - **AGE Schema override**
20
+
21
+ - **multiple AGE Schema**
14
22
 
15
- ## VERSION 0.4.4 - 2024-xx-xx
23
+ ## VERSION 0.5.3 - 2024-xx-xx
16
24
 
17
25
  - **Edge Scaffold** (generates edge, type, view and controller)
18
26
  * add `rails generate apache_age:edge_scaffold HasJob employee_role start_node:person end_node:company`
19
27
 
20
- ## VERSION 0.4.3 - 2024-xx-xx
28
+ ## VERSION 0.5.2 - 2024-xx-xx
29
+
30
+ - **Edge Generator**
31
+ * add start-/end-nodes types to edge generator (would make scaffold easier), ie:
32
+ `rails generate apache_age:edge HasPet owner_role start_node:person end_node:pet`
33
+ with property and specified start-/end-nodes (person and pet nodes must have already been created)
34
+
35
+ ## VERSION 0.5.1 - 2024-xx-xx
21
36
 
22
37
  - **Node Scaffold** (generates node, type, view and controller)
23
38
  * add `rails generate apache_age:node_scaffold Person first_name last_name age:integer`
24
39
 
25
- ## VERSION 0.4.2 - 2024-xx-xx
40
+ ## VERSION 0.5.0 - 2024-06-15
41
+
42
+ **breaking change**: renamed the validators to UniqueVertex and UniqueEdge
26
43
 
27
44
  - **Edge Generator**
28
- * add `rails generate apache_age:edge HasPet owner_role` just a property
29
- * add `rails generate apache_age:edge HasPet owner_role start_node:person end_node:pet`
30
- with property and specified start-/end-nodes (person and pet nodes must have already been created)
45
+ * add `rails generate apache_age:edge HasPet owner_role`
46
+ caveate: start_node and end_node are of type `:vertex` in the generator but can be changed manually in the class file - having trouble with the generator loading the types (the generator rejects custom types - but rails still works with custom types)
31
47
 
32
- ## VERSION 0.4.1 - 2024-xx-xx
48
+ ## VERSION 0.4.1 - 2024-06-15
33
49
 
34
- - **OPTIONAL Installer**
35
- * add `config_migrate` to `rails generate apache_age:install` auto fix the schema after `rails db:migrate`
50
+ - **Installer**
51
+ * add optional: safe migrations with `bin/rails db:migrate` instead of using `bin/rails apache_age:migrate`
52
+ (install using `bin/rails apache_age:override_db_migrate`)
36
53
 
37
54
  ## VERSION 0.4.0 - 2024-06-14
38
55
 
39
- Minor breaking change: type (:vertix) is now required in core for edges
56
+ **breaking change**: type (:vertix) is now required in core for edges
40
57
 
41
58
  - **Installer**
42
59
  * AGE types added to installer (with tests)
data/README.md CHANGED
@@ -55,8 +55,11 @@ install Apache Age (you can ignore the `unknown OID` warnings)
55
55
  bundle add rails_age
56
56
  bundle install
57
57
  bin/rails apache_age:install
58
+ # optional: prevents `bin/rails db:migrate` from modifying the schema file,
59
+ # alternatively you can use: `bin/rails apache_age:migrate` to run safe migrations
60
+ bin/rails apache_age:override_db_migrate
58
61
  git add .
59
- git commit -m "Add Apache Age to Rails"
62
+ git commit -m "Add & configure Apache Age within Rails"
60
63
  ```
61
64
 
62
65
  make some nodes :string is the default type
@@ -69,10 +72,10 @@ make some edges (`:vertex` is the default type) for start_node and end_node
69
72
  ```bash
70
73
  # when start node and end node are not specified they are of type `:vertex`
71
74
  # this is generally not recommended - exept when very generic relationships are needed
72
- rails generate apache_age:edge HasJob employee_role
75
+ rails generate apache_age:edge HasJob employee_role begin_date:date
73
76
 
74
- # this is recommended - use explicit start_node and end_node types
75
- rails generate apache_age:node HasPet start_node:person end_node:pet caretaker_role
77
+ # # this is recommended - (but not yet working) add explicit start_node and end_node types manually
78
+ # rails generate apache_age:node HasPet start_node:person end_node:pet caretaker_role
76
79
  ```
77
80
 
78
81
  **NOTE:** the default `rails db:migrate` inappropriately modifies the schema file. This installer patches the migration to prevent this (however this might break on rails updates, etc). **You can run `bin/rails apache_age:install` at any time to repair the schema file as needed.**
@@ -1,10 +1,14 @@
1
+ # lib/apache_age/validators/unique_edge.rb
2
+
1
3
  # Usage (within an Age Model)
2
- # validates_with UniqueEdgeValidator, attributes: [:employee_role, :start_node, :end_node]
3
- # validates_with UniqueEdgeValidator, attributes: [:start_id, :employee_role, :end_id]
4
- #
4
+ # validates_with(
5
+ # ApacheAge::Validators::UniqueEdge,
6
+ # attributes: [:start_id, :employee_role, :end_id]
7
+ # )
8
+
5
9
  module ApacheAge
6
10
  module Validators
7
- class UniqueEdgeValidator < ActiveModel::Validator
11
+ class UniqueEdge < ActiveModel::Validator
8
12
  def validate(record)
9
13
  allowed_keys = record.age_properties.keys
10
14
  attributes = options[:attributes] || []
@@ -1,10 +1,14 @@
1
+ # lib/apache_age/validators/unique_vertex.rb
2
+
1
3
  # Usage (within an Age Model)
2
- # validates_with UniqueVertexValidator, attributes: [:first_name, :last_name, :gender]
4
+ # validates_with(
5
+ # ApacheAge::Validators::UniqueVertex,
6
+ # attributes: [:first_name, :last_name, :gender]
7
+ # )
3
8
 
4
- # lib/apache_age/validators/unique_vertex_validator.rb
5
9
  module ApacheAge
6
10
  module Validators
7
- class UniqueVertexValidator < ActiveModel::Validator
11
+ class UniqueVertex < ActiveModel::Validator
8
12
  def validate(record)
9
13
  allowed_keys = record.age_properties.keys
10
14
  attributes = options[:attributes]
@@ -1,35 +1,38 @@
1
1
  Description:
2
- This creates Apache AGE nodes that work seamlessly with Rails.
3
- A node can be created with or without a namespace.
2
+ This creates Apache AGE edges that work seamlessly with Rails.
3
+ An edge can be created with or without a namespace.
4
4
  See the below examples.
5
5
 
6
6
  Example:
7
- `bin/rails g apache_age:node Cat name age:integer`
7
+ `bin/rails g apache_age:edge HasJob employee_role start_date:date`
8
+
9
+ (hopefully comming soon - but not yet ready is the ability to add start_node and end_node types)
10
+ `bin/rails g apache_age:edge HasJob employee_role start_date:date start_node:person end_node:company`
8
11
 
9
12
  This creates:
10
- `app/nodes/cat.rb`
13
+ `app/edges/has_job.rb`
11
14
 
12
15
  with the contents:
13
16
  ```
14
- class Cat
15
- include ApacheAge::Entities::Vertex
17
+ class HasJob
18
+ include ApacheAge::Entities::Edge
16
19
 
17
- attribute :name, :string
18
- attribute :age, :integer
20
+ attribute :employee_role, :string
21
+ attribute :start_date, :date
19
22
 
20
- validates :name, presence: true
21
- validates :age, presence: true
23
+ validates :employee_role, presence: true
24
+ validates :begin_date, presence: true
22
25
 
23
26
  # unique node validator (remove any attributes that are not important to uniqueness)
24
27
  validates_with(
25
- ApacheAge::Validators::UniqueVertexValidator,
26
- attributes: [:name, :age]
28
+ ApacheAge::Validators::UniqueVertex,
29
+ attributes: [:employee_role, :begin_date :start_node, :end_node]
27
30
  )
28
31
  end
29
32
  ```
30
33
 
31
34
  A namespace can also be used:
32
- `bin/rails g apache_age:node Animals/Cat name age:integer`
35
+ `bin/rails g apache_age:edge Animals/Cat name age:integer`
33
36
 
34
37
  This creates:
35
38
  `app/nodes/animals/cat.rb`
@@ -47,8 +50,8 @@ Example:
47
50
 
48
51
  # unique node validator (remove any attributes that are not important to uniqueness)
49
52
  validates_with(
50
- ApacheAge::Validators::UniqueVertexValidator,
51
- attributes: [:name, :age]
53
+ ApacheAge::Validators::UniqueVertex,
54
+ attributes: [:name, :age, :start_node, :end_node]
52
55
  )
53
56
  end
54
57
  ```
@@ -1,56 +1,27 @@
1
+ # lib/generators/apache_age/edge/edge_generator.rb
1
2
  require 'rails/generators'
2
3
  require 'rails/generators/named_base'
3
4
 
5
+ require_relative '../generate_entity_methods'
6
+ # TODO: get generators to work with custom types!
7
+ # require_relative "#{Rails.root}/config/initializers/types"
8
+
4
9
  module ApacheAge
5
10
  class EdgeGenerator < Rails::Generators::NamedBase
11
+ include ApacheAge::GenerateEntityMethods
12
+
6
13
  source_root File.expand_path('templates', __dir__)
7
14
  argument :attributes, type: :array, default: [], banner: "field:type field:type"
8
15
 
9
16
  def perform_task
10
- behavior == :invoke ? create_edge_file : destroy_edge_file
17
+ age_type = 'edge'
18
+ Rails.application.eager_load! # Ensure all initializers and dependencies are loaded
19
+ behavior == :invoke ? generate_age_entity(age_type) : destroy_age_entity(age_type)
11
20
  end
12
21
 
13
22
  private
14
23
 
15
- def create_edge_file
16
- template "edge.rb.tt", File.join("app/edge", class_path, "#{file_name}.rb")
17
- end
18
-
19
- def destroy_edge_file
20
- file_path = File.join("app/edge", class_path, "#{file_name}.rb")
21
- File.delete(file_path) if File.exist?(file_path)
22
- end
23
-
24
- def attributes_list
25
- attributes.map { |attr| { name: attr.name, type: attr.type } }
26
- end
27
-
28
- def unique_attributes
29
- attributes_list.map { |attr| attr[:name].to_sym }
30
- end
31
-
32
- def parent_module
33
- class_path.map(&:camelize).join('::')
34
- end
35
-
36
- def full_class_name
37
- parent_module.empty? ? class_name : "#{parent_module}::#{class_name}"
38
- end
39
-
40
- def indented_namespace
41
- return '' if parent_module.empty?
42
-
43
- parent_module.split('::').map.with_index do |namespace, index|
44
- "#{' ' * index}module #{namespace}"
45
- end.join("\n") + "\n"
46
- end
47
-
48
- def indented_end_namespace
49
- return '' if parent_module.empty?
50
-
51
- parent_module.split('::').map.with_index do |_, index|
52
- "#{' ' * (parent_module.split('::').length - 1 - index)}end"
53
- end.join("\n") + "\n"
54
- end
24
+ # different than node_generator.rb
25
+ def unique_edge_attributes = unique_attributes + [:start_node, :end_node]
55
26
  end
56
27
  end
@@ -1,20 +1,28 @@
1
- <%= indented_namespace %>
2
- <%= ' ' * class_path.length %>class <%= class_name %>
3
- <%= ' ' * class_path.length %> include ApacheAge::Entities::Edge
1
+ class <%= class_name %>
2
+ include ApacheAge::Entities::Edge
4
3
 
5
4
  <%- attributes_list.each do |attribute| -%>
6
- <%= ' ' * class_path.length %> attribute :<%= attribute[:name] %>, :<%= attribute[:reference] || attribute[:type] %>
5
+ attribute :<%= attribute[:name] %>, :<%= attribute[:reference] || attribute[:type] %>
7
6
  <%- end -%>
7
+ # recommendation for (start_node and end_node): change `:vertex` with the 'node' type
8
+ # see `config/initializers/apache_age.rb` for the list of available node types
9
+ attribute :start_node, :vertex
10
+ attribute :end_node, :vertex
8
11
 
9
- <%= ' ' * class_path.length %> validates :<%= unique_attributes.first %>, presence: true
10
- <%= ' ' * class_path.length %> validate :validate_unique
12
+ <%- attributes_list.each do |attribute| -%>
13
+ validates :<%= attribute[:name] %>, presence: true
14
+ <%- end -%>
15
+ validates :start_node, presence: true
16
+ validates :end_node, presence: true
17
+
18
+ validate :validate_unique_edge
11
19
 
12
- <%= ' ' * class_path.length %> private
20
+ private
13
21
 
14
- <%= ' ' * class_path.length %> def validate_unique
15
- <%= ' ' * class_path.length %> ApacheAge::Validators::UniqueEdgeValidator
16
- <%= ' ' * class_path.length %> .new(attributes: <%= unique_attributes.inspect %>)
17
- <%= ' ' * class_path.length %> .validate(self)
18
- <%= ' ' * class_path.length %> end
19
- <%= ' ' * class_path.length %>end
20
- <%= indented_end_namespace %>
22
+ # custom unique edge validator (remove any attributes that are NOT important to uniqueness)
23
+ def validate_unique_edge
24
+ ApacheAge::Validators::UniqueEdge
25
+ .new(attributes: <%= unique_edge_attributes.inspect %>)
26
+ .validate(self)
27
+ end
28
+ end
@@ -0,0 +1,84 @@
1
+ # lib/generators/apache_age/generate_entity_methods.rb
2
+
3
+ module ApacheAge
4
+ module GenerateEntityMethods
5
+ def generate_age_entity(age_type)
6
+ template "#{age_type}.rb.tt", File.join(destination_root, "app/#{age_type}s", class_path, "#{file_name}.rb")
7
+ add_type_config
8
+ end
9
+
10
+ def destroy_age_entity(age_type)
11
+ file_path = File.join(destination_root, "app/#{age_type}s", class_path, "#{file_name}.rb")
12
+ File.delete(file_path) if File.exist?(file_path)
13
+ remove_type_config
14
+ end
15
+
16
+ def attributes_list
17
+ attributes.map { |attr| { name: attr.name, type: attr.type } }
18
+ end
19
+
20
+ def parent_module
21
+ class_path.map(&:camelize).join('::')
22
+ end
23
+
24
+ def unique_attributes
25
+ attributes_list.map { |attr| attr[:name].to_sym }
26
+ end
27
+
28
+ def full_class_name
29
+ parent_module.empty? ? class_name : "#{parent_module}::#{class_name}"
30
+ end
31
+
32
+ def indented_namespace
33
+ return '' if parent_module.empty?
34
+
35
+ parent_module.split('::').map.with_index do |namespace, index|
36
+ "#{' ' * index}module #{namespace}"
37
+ end.join("\n") + "\n"
38
+ end
39
+
40
+ # def indented_end_namespace
41
+ # return '' if parent_module.empty?
42
+
43
+ # parent_module.split('::').map.with_index do |_, index|
44
+ # "#{' ' * (parent_module.split('::').length - 1 - index)}end"
45
+ # end.join("\n") + "\n"
46
+ # end
47
+
48
+ def add_type_config
49
+ return unless File.exist?(types_config_file)
50
+
51
+ types_content = File.read(types_config_file)
52
+ types_content.sub!(/^end\s*$/, "#{new_type_content}end")
53
+ File.open(types_config_file, 'w') { |file| file.write(types_content) }
54
+ puts " modified: config/initializers/types.rb"
55
+ end
56
+
57
+ def remove_type_config
58
+ return unless File.exist?(types_config_file)
59
+
60
+ type_to_remove = new_type_content
61
+
62
+ types_content = File.read(types_config_file)
63
+ types_content.gsub!(type_to_remove, '')
64
+ File.open(types_config_file, 'w') { |file| file.write(types_content) }
65
+ end
66
+
67
+ def types_config_file = File.join(Rails.root, 'config/initializers/types.rb')
68
+
69
+ def new_type_content
70
+ file_path = [class_path, file_name].reject(&:blank?).join('/').downcase
71
+ entity_namespace = class_path.map(&:capitalize).join('::')
72
+ entity_class_name = file_name.split('_').map(&:capitalize).join
73
+ entity_namespaced_class = [entity_namespace, entity_class_name].reject(&:blank?).join('::')
74
+ type_name = [class_path.join('_'), file_name].reject(&:blank?).join('_')
75
+ content =
76
+ <<-RUBY
77
+ require_dependency '#{file_path}'
78
+ ActiveModel::Type.register(
79
+ :#{type_name}, ApacheAge::Types::AgeTypeGenerator.create_type_for(#{entity_namespaced_class})
80
+ )
81
+ RUBY
82
+ end
83
+ end
84
+ end
@@ -14,16 +14,16 @@ Example:
14
14
  class Cat
15
15
  include ApacheAge::Entities::Vertex
16
16
 
17
- attribute :name, :string
18
- attribute :age, :integer
17
+ attribute :full_name, :string
18
+ attribute :birthdate, :date
19
19
 
20
- validates :name, presence: true
21
- validates :age, presence: true
20
+ validates :full_name, presence: true
21
+ validates :birthdate, presence: true
22
22
 
23
23
  # unique node validator (remove any attributes that are not important to uniqueness)
24
24
  validates_with(
25
- ApacheAge::Validators::UniqueVertexValidator,
26
- attributes: [:name, :age]
25
+ ApacheAge::Validators::UniqueVertex,
26
+ attributes: [:full_name, :birthdate]
27
27
  )
28
28
  end
29
29
  ```
@@ -47,7 +47,7 @@ Example:
47
47
 
48
48
  # unique node validator (remove any attributes that are not important to uniqueness)
49
49
  validates_with(
50
- ApacheAge::Validators::UniqueVertexValidator,
50
+ ApacheAge::Validators::UniqueVertex,
51
51
  attributes: [:name, :age]
52
52
  )
53
53
  end
@@ -1,94 +1,19 @@
1
+ # lib/generators/apache_age/node/entity_generator.rb
1
2
  require 'rails/generators'
2
3
  require 'rails/generators/named_base'
3
4
 
5
+ require_relative '../generate_entity_methods'
6
+
4
7
  module ApacheAge
5
8
  class NodeGenerator < Rails::Generators::NamedBase
9
+ include ApacheAge::GenerateEntityMethods
10
+
6
11
  source_root File.expand_path('templates', __dir__)
7
12
  argument :attributes, type: :array, default: [], banner: "field:type field:type"
8
13
 
9
14
  def perform_task
10
- behavior == :invoke ? create_node_file : destroy_node_file
11
- end
12
-
13
- private
14
-
15
- def create_node_file
16
- template "node.rb.tt", File.join(destination_root, "app/nodes", class_path, "#{file_name}.rb")
17
- add_type_config
18
- end
19
-
20
- def destroy_node_file
21
- file_path = File.join(destination_root, "app/nodes", class_path, "#{file_name}.rb")
22
- File.delete(file_path) if File.exist?(file_path)
23
- remove_type_config
24
- end
25
-
26
- def attributes_list
27
- attributes.map { |attr| { name: attr.name, type: attr.type } }
28
- end
29
-
30
- def unique_attributes
31
- attributes_list.map { |attr| attr[:name].to_sym }
32
- end
33
-
34
- def parent_module
35
- class_path.map(&:camelize).join('::')
36
- end
37
-
38
- def full_class_name
39
- parent_module.empty? ? class_name : "#{parent_module}::#{class_name}"
40
- end
41
-
42
- def indented_namespace
43
- return '' if parent_module.empty?
44
-
45
- parent_module.split('::').map.with_index do |namespace, index|
46
- "#{' ' * index}module #{namespace}"
47
- end.join("\n") + "\n"
48
- end
49
-
50
- def indented_end_namespace
51
- return '' if parent_module.empty?
52
-
53
- parent_module.split('::').map.with_index do |_, index|
54
- "#{' ' * (parent_module.split('::').length - 1 - index)}end"
55
- end.join("\n") + "\n"
56
- end
57
-
58
- def add_type_config
59
- return unless File.exist?(types_config_file)
60
-
61
- types_content = File.read(types_config_file)
62
- types_content.sub!(/^end\s*$/, "#{new_type_content}end")
63
- File.open(types_config_file, 'w') { |file| file.write(types_content) }
64
- puts " modified: config/initializers/types.rb"
65
- end
66
-
67
- def remove_type_config
68
- return unless File.exist?(types_config_file)
69
-
70
- type_to_remove = new_type_content
71
-
72
- types_content = File.read(types_config_file)
73
- types_content.gsub!(type_to_remove, '')
74
- File.open(types_config_file, 'w') { |file| file.write(types_content) }
75
- end
76
-
77
- def types_config_file = File.join(Rails.root, 'config/initializers/types.rb')
78
-
79
- def new_type_content
80
- file_path = [class_path, file_name].reject(&:blank?).join('/').downcase
81
- node_namespace = class_path.map(&:capitalize).join('::')
82
- node_class_name = file_name.split('_').map(&:capitalize).join
83
- node_namespaced_class = [node_namespace, node_class_name].reject(&:blank?).join('::')
84
- type_name = [class_path.join('_'), file_name].reject(&:blank?).join('_')
85
- content =
86
- <<-RUBY
87
- require_dependency '#{file_path}'
88
- ActiveModel::Type.register(
89
- :#{type_name}, ApacheAge::Types::AgeTypeGenerator.create_type_for(#{node_namespaced_class})
90
- )
91
- RUBY
15
+ age_type = 'node'
16
+ behavior == :invoke ? generate_age_entity(age_type) : destroy_age_entity(age_type)
92
17
  end
93
18
  end
94
19
  end
@@ -9,9 +9,9 @@ class <%= class_name %>
9
9
  validates :<%= attribute[:name] %>, presence: true
10
10
  <%- end -%>
11
11
 
12
- # unique node validator (remove any attributes that are not important to uniqueness)
12
+ # custom unique node validator (remove any attributes that are NOT important to uniqueness)
13
13
  validates_with(
14
- ApacheAge::Validators::UniqueVertexValidator,
14
+ ApacheAge::Validators::UniqueVertex,
15
15
  attributes: <%= unique_attributes.inspect %>
16
16
  )
17
17
  end
@@ -1,3 +1,3 @@
1
1
  module RailsAge
2
- VERSION = '0.4.0'
2
+ VERSION = '0.5.0'
3
3
  end
data/lib/rails_age.rb CHANGED
@@ -11,7 +11,7 @@ module ApacheAge
11
11
  require 'apache_age/entities/edge'
12
12
  require 'apache_age/entities/entity'
13
13
  require 'apache_age/entities/vertex'
14
+ require 'apache_age/validators/unique_edge'
15
+ require 'apache_age/validators/unique_vertex'
14
16
  require 'apache_age/types/age_type_generator'
15
- require 'apache_age/validators/unique_edge_validator'
16
- require 'apache_age/validators/unique_vertex_validator'
17
17
  end
@@ -0,0 +1,69 @@
1
+ # lib/tasks/install.rake
2
+ # Usage:
3
+ # * `bin/rails apache_age:add_age_migration`
4
+ # * `bin/rails apache_age:add_age_migration[destination_path.to_s]`
5
+ # * `bundle exec rails apache_age:add_age_migration.invoke(destination_path.to_s)`
6
+ namespace :apache_age do
7
+ desc "Copy migrations from rails_age to application and update schema"
8
+ task :add_age_migration, [:destination_path] => :environment do |t, args|
9
+
10
+ base_name = 'add_apache_age'
11
+ destination_path =
12
+ File.expand_path(args[:destination_path].presence || "#{Rails.root}/db/migrate", __FILE__)
13
+
14
+ FileUtils.mkdir_p(destination_path) unless File.exist?(destination_path)
15
+ existing_migrations =
16
+ Dir.glob("#{destination_path}/*.rb").map { |file| File.basename(file).sub(/^\d+/, '') }
17
+
18
+ if existing_migrations.any? { |migration| migration.include?(base_name) }
19
+ puts "Skipping migration AddApacheAge, it already exists"
20
+ else
21
+ age_migration_contents =
22
+ <<~RUBY
23
+ class AddApacheAge < ActiveRecord::Migration[7.1]
24
+ def up
25
+ # Allow age extension
26
+ execute('CREATE EXTENSION IF NOT EXISTS age;')
27
+
28
+ # Load the age code
29
+ execute("LOAD 'age';")
30
+
31
+ # Load the ag_catalog into the search path
32
+ execute('SET search_path = ag_catalog, "$user", public;')
33
+
34
+ # Create age_schema graph if it doesn't exist
35
+ execute("SELECT create_graph('age_schema');")
36
+ end
37
+
38
+ def down
39
+ execute <<-SQL
40
+ DO $$
41
+ BEGIN
42
+ IF EXISTS (
43
+ SELECT 1
44
+ FROM pg_constraint
45
+ WHERE conname = 'fk_graph_oid'
46
+ ) THEN
47
+ ALTER TABLE ag_catalog.ag_label
48
+ DROP CONSTRAINT fk_graph_oid;
49
+ END IF;
50
+ END $$;
51
+ SQL
52
+
53
+ execute("SELECT drop_graph('age_schema', true);")
54
+ execute('DROP SCHEMA IF EXISTS ag_catalog CASCADE;')
55
+ execute('DROP EXTENSION IF EXISTS age;')
56
+ end
57
+ end
58
+ RUBY
59
+
60
+ migration_version = Time.now.utc.strftime("%Y%m%d%H%M%S")
61
+ file_version = migration_version.delete('_')
62
+ new_filename = "#{file_version}_#{base_name}.rb"
63
+ destination_file = File.join(destination_path, new_filename)
64
+
65
+ File.write(destination_file, age_migration_contents)
66
+ puts "Created migration AddApacheAge"
67
+ end
68
+ end
69
+ end
@@ -4,8 +4,8 @@
4
4
  namespace :apache_age do
5
5
  desc "Install & configure Apache Age within Rails (updates migrations, schema & database.yml)"
6
6
  task :install => :environment do
7
- # copy our migrations to the application (if needed)
8
- Rake::Task["apache_age:copy_migrations"].invoke
7
+ # Ensure the AGE migration is in place
8
+ Rake::Task["apache_age:add_age_migration"].invoke
9
9
 
10
10
  # run any new migrations
11
11
  Rake::Task["db:migrate"].invoke
@@ -18,9 +18,5 @@ namespace :apache_age do
18
18
 
19
19
  # ensure the config/initializers/types.rb file has the base AGE Types
20
20
  Rake::Task["apache_age:config_types"].invoke
21
-
22
- # # ensure bin/rails db:migrate is always followed with
23
- # # `Rake::Task["apache_age:config_schema"].invoke` to ensure the schema isn't mangled
24
- # Rake::Task["apache_age:config_migrate"].invoke
25
21
  end
26
22
  end
@@ -0,0 +1,50 @@
1
+ namespace :apache_age do
2
+ desc "Ensure 'db:migrate' is followed by 'apache_age:config_schema' to repair the schema.rb file after migration mangles it."
3
+ task :override_db_migrate, [:destination_path] => :environment do |t, args|
4
+ destination_path = (args[:destination_path].presence || "#{Rails.root}") + "/bin"
5
+ FileUtils.mkdir_p(destination_path) unless File.exist?(destination_path)
6
+ bin_rails_path = File.expand_path("#{destination_path}/rails", __FILE__)
7
+
8
+ original_content = File.read(bin_rails_path)
9
+ destination_content = original_content.dup
10
+
11
+ unless destination_content.include?("#!/usr/bin/env ruby\nrails_cmd = ARGV.first")
12
+ capture_rails_cmd =
13
+ <<~RUBY
14
+ rails_cmd = ARGV.first # must be first (otherwise consumed by rails:commands)
15
+ RUBY
16
+
17
+ # add to the top of the file (with gsub)
18
+ destination_content.sub!(
19
+ %r{#!/usr/bin/env ruby\n},
20
+ "#!/usr/bin/env ruby\n#{capture_rails_cmd}\n"
21
+ )
22
+ end
23
+
24
+ # Check if the migration hook is already present
25
+ unless destination_content.include?('Rake::Task["apache_age:config_schema"].invoke')
26
+ override_migrate =
27
+ <<~RUBY
28
+
29
+ # ensure db:migrate is followed with: `Rake::Task["apache_age:config_schema"].invoke`
30
+ # to the schema.rb file after the migration mangles it
31
+ if rails_cmd == 'db:migrate'
32
+ require 'rake'
33
+ Rails.application.load_tasks
34
+ Rake::Task['db:migrate'].invoke
35
+ Rake::Task["apache_age:config_schema"].invoke
36
+ end
37
+ RUBY
38
+
39
+ # append to the end of the file
40
+ destination_content << override_migrate
41
+ end
42
+
43
+ if destination_content == original_content
44
+ puts "AGE Safe Migration is already present in bin/rails. (If its not working inspect the bin/rails file)"
45
+ else
46
+ File.write(bin_rails_path, destination_content)
47
+ puts "AGE Safe Migration added to bin/rails. Now run `bin/rails db:migrate`, then run your tests (or inspect the schema.rb file)."
48
+ end
49
+ end
50
+ 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.0
4
+ version: 0.5.0
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-06-14 00:00:00.000000000 Z
11
+ date: 2024-06-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -73,25 +73,26 @@ files:
73
73
  - lib/apache_age/entities/entity.rb
74
74
  - lib/apache_age/entities/vertex.rb
75
75
  - lib/apache_age/types/age_type_generator.rb
76
- - lib/apache_age/validators/unique_edge_validator.rb
77
- - lib/apache_age/validators/unique_vertex_validator.rb
76
+ - lib/apache_age/validators/unique_edge.rb
77
+ - lib/apache_age/validators/unique_vertex.rb
78
78
  - lib/apache_age/validators/vertex_type_validator.rb
79
79
  - lib/generators/apache_age/edge/USAGE
80
80
  - lib/generators/apache_age/edge/edge_generator.rb
81
81
  - lib/generators/apache_age/edge/templates/edge.rb.tt
82
+ - lib/generators/apache_age/generate_entity_methods.rb
82
83
  - lib/generators/apache_age/node/USAGE
83
84
  - lib/generators/apache_age/node/node_generator.rb
84
85
  - lib/generators/apache_age/node/templates/node.rb.tt
85
86
  - lib/rails_age.rb
86
87
  - lib/rails_age/engine.rb
87
88
  - lib/rails_age/version.rb
89
+ - lib/tasks/add_age_migration.rake
88
90
  - lib/tasks/config_database.rake
89
- - lib/tasks/config_migrate.rake
90
91
  - lib/tasks/config_schema.rake
91
92
  - lib/tasks/config_types.rake
92
- - lib/tasks/copy_migrations.rake
93
93
  - lib/tasks/install.rake
94
94
  - lib/tasks/migrate.rake
95
+ - lib/tasks/override_db_migrate.rake
95
96
  homepage: https://github.com/marpori/rails_age
96
97
  licenses:
97
98
  - MIT
@@ -1,35 +0,0 @@
1
- # namespace :apache_age do
2
- # desc "Ensure db:migrate is followed by apache_age:config_schema"
3
- # task :config_migrate do
4
- # bin_rails_path = File.expand_path('../../../bin/rails', __FILE__)
5
- # migration_hook =
6
- # <<-RUBY
7
- # # ensure db:migrate is followed with: `Rake::Task["apache_age:config_schema"].invoke`
8
- # # which repairs the schema.rb file after the migration mangles it
9
- # require_relative '../config/boot'
10
- # require 'rails/commands'
11
-
12
- # if ARGV.first == 'db:migrate'
13
- # require 'rake'
14
- # Rails.application.load_tasks
15
- # Rake::Task['db:migrate'].invoke
16
- # Rake::Task["apache_age:config_schema"].invoke
17
- # else
18
- # Rake::Task['rails:commands'].invoke(*ARGV)
19
- # end
20
- # RUBY
21
-
22
- # # Read the current content of the bin/rails file
23
- # bin_rails_content = File.read(bin_rails_path)
24
-
25
- # # Check if the migration hook is already present
26
- # unless bin_rails_content.include?('Rake::Task["apache_age:config_schema"].invoke')
27
- # # Append the migration hook to the end of the bin/rails file
28
- # File.open(bin_rails_path, 'a') do |file|
29
- # file.write(migration_hook)
30
- # end
31
- # puts "Migration hook added to bin/rails."
32
- # else
33
- # puts "Migration hook already present in bin/rails."
34
- # end
35
- # end
@@ -1,33 +0,0 @@
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