rails_age 0.3.1 → 0.4.0
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 +53 -6
- data/README.md +82 -341
- data/lib/apache_age/entities/edge.rb +3 -3
- data/lib/apache_age/validators/unique_edge_validator.rb +4 -4
- data/lib/apache_age/validators/unique_vertex_validator.rb +2 -2
- data/lib/generators/apache_age/edge/USAGE +54 -0
- data/lib/generators/apache_age/edge/edge_generator.rb +56 -0
- data/lib/generators/apache_age/edge/templates/edge.rb.tt +20 -0
- data/lib/generators/apache_age/node/USAGE +54 -0
- data/lib/generators/apache_age/node/node_generator.rb +94 -0
- data/lib/generators/apache_age/node/templates/node.rb.tt +17 -0
- data/lib/rails_age/version.rb +1 -1
- data/lib/tasks/{database_config.rake → config_database.rake} +1 -1
- data/lib/tasks/config_migrate.rake +35 -0
- data/lib/tasks/{schema_config.rake → config_schema.rake} +1 -2
- data/lib/tasks/config_types.rake +85 -0
- data/lib/tasks/install.rake +10 -3
- data/lib/tasks/migrate.rake +7 -0
- metadata +13 -5
- data/lib/tasks/install.original.rake +0 -257
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'rails/generators'
|
2
|
+
require 'rails/generators/named_base'
|
3
|
+
|
4
|
+
module ApacheAge
|
5
|
+
class EdgeGenerator < Rails::Generators::NamedBase
|
6
|
+
source_root File.expand_path('templates', __dir__)
|
7
|
+
argument :attributes, type: :array, default: [], banner: "field:type field:type"
|
8
|
+
|
9
|
+
def perform_task
|
10
|
+
behavior == :invoke ? create_edge_file : destroy_edge_file
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
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
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
<%= indented_namespace %>
|
2
|
+
<%= ' ' * class_path.length %>class <%= class_name %>
|
3
|
+
<%= ' ' * class_path.length %> include ApacheAge::Entities::Edge
|
4
|
+
|
5
|
+
<%- attributes_list.each do |attribute| -%>
|
6
|
+
<%= ' ' * class_path.length %> attribute :<%= attribute[:name] %>, :<%= attribute[:reference] || attribute[:type] %>
|
7
|
+
<%- end -%>
|
8
|
+
|
9
|
+
<%= ' ' * class_path.length %> validates :<%= unique_attributes.first %>, presence: true
|
10
|
+
<%= ' ' * class_path.length %> validate :validate_unique
|
11
|
+
|
12
|
+
<%= ' ' * class_path.length %> private
|
13
|
+
|
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 %>
|
@@ -0,0 +1,54 @@
|
|
1
|
+
Description:
|
2
|
+
This creates Apache AGE nodes that work seamlessly with Rails.
|
3
|
+
A node can be created with or without a namespace.
|
4
|
+
See the below examples.
|
5
|
+
|
6
|
+
Example:
|
7
|
+
`bin/rails g apache_age:node Cat name age:integer`
|
8
|
+
|
9
|
+
This creates:
|
10
|
+
`app/nodes/cat.rb`
|
11
|
+
|
12
|
+
with the contents:
|
13
|
+
```
|
14
|
+
class Cat
|
15
|
+
include ApacheAge::Entities::Vertex
|
16
|
+
|
17
|
+
attribute :name, :string
|
18
|
+
attribute :age, :integer
|
19
|
+
|
20
|
+
validates :name, presence: true
|
21
|
+
validates :age, presence: true
|
22
|
+
|
23
|
+
# unique node validator (remove any attributes that are not important to uniqueness)
|
24
|
+
validates_with(
|
25
|
+
ApacheAge::Validators::UniqueVertexValidator,
|
26
|
+
attributes: [:name, :age]
|
27
|
+
)
|
28
|
+
end
|
29
|
+
```
|
30
|
+
|
31
|
+
A namespace can also be used:
|
32
|
+
`bin/rails g apache_age:node Animals/Cat name age:integer`
|
33
|
+
|
34
|
+
This creates:
|
35
|
+
`app/nodes/animals/cat.rb`
|
36
|
+
|
37
|
+
with the contents
|
38
|
+
```
|
39
|
+
class Animals::Cat
|
40
|
+
include ApacheAge::Entities::Vertex
|
41
|
+
|
42
|
+
attribute :name, :string
|
43
|
+
attribute :age, :integer
|
44
|
+
|
45
|
+
validates :name, presence: true
|
46
|
+
validates :age, presence: true
|
47
|
+
|
48
|
+
# unique node validator (remove any attributes that are not important to uniqueness)
|
49
|
+
validates_with(
|
50
|
+
ApacheAge::Validators::UniqueVertexValidator,
|
51
|
+
attributes: [:name, :age]
|
52
|
+
)
|
53
|
+
end
|
54
|
+
```
|
@@ -0,0 +1,94 @@
|
|
1
|
+
require 'rails/generators'
|
2
|
+
require 'rails/generators/named_base'
|
3
|
+
|
4
|
+
module ApacheAge
|
5
|
+
class NodeGenerator < Rails::Generators::NamedBase
|
6
|
+
source_root File.expand_path('templates', __dir__)
|
7
|
+
argument :attributes, type: :array, default: [], banner: "field:type field:type"
|
8
|
+
|
9
|
+
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
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
class <%= class_name %>
|
2
|
+
include ApacheAge::Entities::Vertex
|
3
|
+
|
4
|
+
<%- attributes_list.each do |attribute| -%>
|
5
|
+
attribute :<%= attribute[:name] %>, :<%= attribute[:type] %>
|
6
|
+
<%- end -%>
|
7
|
+
|
8
|
+
<%- attributes_list.each do |attribute| -%>
|
9
|
+
validates :<%= attribute[:name] %>, presence: true
|
10
|
+
<%- end -%>
|
11
|
+
|
12
|
+
# unique node validator (remove any attributes that are not important to uniqueness)
|
13
|
+
validates_with(
|
14
|
+
ApacheAge::Validators::UniqueVertexValidator,
|
15
|
+
attributes: <%= unique_attributes.inspect %>
|
16
|
+
)
|
17
|
+
end
|
data/lib/rails_age/version.rb
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
#
|
4
4
|
namespace :apache_age do
|
5
5
|
desc "Ensure the database.yml file is properly configured for Apache Age"
|
6
|
-
task :
|
6
|
+
task :config_database => :environment do
|
7
7
|
|
8
8
|
db_config_file = File.expand_path("#{Rails.root}/config/database.yml", __FILE__)
|
9
9
|
|
@@ -0,0 +1,35 @@
|
|
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
|
@@ -3,8 +3,7 @@
|
|
3
3
|
#
|
4
4
|
namespace :apache_age do
|
5
5
|
desc "Copy migrations from rails_age to application and update schema"
|
6
|
-
task :
|
7
|
-
# source_schema = File.expand_path('../../../db/schema.rb', __FILE__)
|
6
|
+
task :config_schema => :environment do
|
8
7
|
destination_schema = File.expand_path("#{Rails.root}/db/schema.rb", __FILE__)
|
9
8
|
|
10
9
|
unless File.exist?(destination_schema)
|
@@ -0,0 +1,85 @@
|
|
1
|
+
# lib/tasks/install.rake
|
2
|
+
# Usage: `rake apache_age:config_types`
|
3
|
+
#
|
4
|
+
namespace :apache_age do
|
5
|
+
desc "Install AGE types from rails_age into the rails initializers"
|
6
|
+
task :config_types => :environment do
|
7
|
+
types_file_path = File.expand_path("#{Rails.root}/config/initializers/types.rb", __FILE__)
|
8
|
+
required_file_path = "require 'apache_age/types/age_type_generator'"
|
9
|
+
required_file_content =
|
10
|
+
<<~RUBY
|
11
|
+
require 'apache_age/types/age_type_generator'
|
12
|
+
# AGE Type Definition Usage (edges/nodes):
|
13
|
+
# require_dependency 'nodes/company'
|
14
|
+
# ActiveModel::Type.register(
|
15
|
+
# :company, ApacheAge::Types::AgeTypeGenerator.create_type_for(Nodes::Company)
|
16
|
+
# )
|
17
|
+
RUBY
|
18
|
+
node_type_content =
|
19
|
+
<<-RUBY
|
20
|
+
require_dependency 'apache_age/entities/vertex'
|
21
|
+
ActiveModel::Type.register(
|
22
|
+
:vertex, ApacheAge::Types::AgeTypeGenerator.create_type_for(ApacheAge::Entities::Vertex)
|
23
|
+
)
|
24
|
+
RUBY
|
25
|
+
edge_type_content =
|
26
|
+
<<-RUBY
|
27
|
+
require_dependency 'apache_age/entities/edge'
|
28
|
+
ActiveModel::Type.register(
|
29
|
+
:edge, ApacheAge::Types::AgeTypeGenerator.create_type_for(ApacheAge::Entities::Edge)
|
30
|
+
)
|
31
|
+
RUBY
|
32
|
+
|
33
|
+
unless File.exist?(types_file_path)
|
34
|
+
source_content =
|
35
|
+
<<~RUBY
|
36
|
+
# config/initializers/types.rb
|
37
|
+
|
38
|
+
#{required_file_content}
|
39
|
+
Rails.application.config.to_prepare do
|
40
|
+
# Register AGE types
|
41
|
+
#{node_type_content}
|
42
|
+
#{edge_type_content}
|
43
|
+
end
|
44
|
+
RUBY
|
45
|
+
File.open(types_file_path, 'w') { |file| file.write(source_content) }
|
46
|
+
puts "config/initializers/types.rb file created with AGE base types"
|
47
|
+
else
|
48
|
+
destination_content = File.read(types_file_path)
|
49
|
+
original_content = destination_content.dup
|
50
|
+
|
51
|
+
unless destination_content.include?(required_file_path)
|
52
|
+
destination_content.sub!(
|
53
|
+
/^(\s*Rails\.application\.config\.to_prepare do\n)/,
|
54
|
+
"#{required_file_content}\n\\1"
|
55
|
+
)
|
56
|
+
end
|
57
|
+
|
58
|
+
unless destination_content.include?('# Register AGE types')
|
59
|
+
destination_content.sub!(
|
60
|
+
/^(\s*Rails\.application\.config\.to_prepare do\n)/,
|
61
|
+
"\\1 # Register AGE types\n"
|
62
|
+
)
|
63
|
+
end
|
64
|
+
|
65
|
+
unless destination_content.include?(edge_type_content)
|
66
|
+
destination_content.sub!(
|
67
|
+
/^(\s*Rails\.application\.config\.to_prepare do\n # Register AGE types\n)/,
|
68
|
+
"\\1#{edge_type_content}"
|
69
|
+
)
|
70
|
+
end
|
71
|
+
|
72
|
+
unless destination_content.include?(node_type_content)
|
73
|
+
destination_content.sub!(
|
74
|
+
/^(\s*Rails\.application\.config\.to_prepare do\n # Register AGE types\n)/,
|
75
|
+
"\\1#{node_type_content}"
|
76
|
+
)
|
77
|
+
end
|
78
|
+
|
79
|
+
if destination_content != original_content
|
80
|
+
File.open(types_file_path, 'w') { |file| file.write(destination_content) }
|
81
|
+
puts "modified: config/initializers/types.rb"
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
data/lib/tasks/install.rake
CHANGED
@@ -10,10 +10,17 @@ namespace :apache_age do
|
|
10
10
|
# run any new migrations
|
11
11
|
Rake::Task["db:migrate"].invoke
|
12
12
|
|
13
|
+
# ensure the config/database.yml file has the proper configurations
|
14
|
+
Rake::Task["apache_age:config_database"].invoke
|
15
|
+
|
13
16
|
# adjust the schema file (unfortunately rails mangles the schema file)
|
14
|
-
Rake::Task["apache_age:
|
17
|
+
Rake::Task["apache_age:config_schema"].invoke
|
15
18
|
|
16
|
-
# ensure the config/
|
17
|
-
Rake::Task["apache_age:
|
19
|
+
# ensure the config/initializers/types.rb file has the base AGE Types
|
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
|
18
25
|
end
|
19
26
|
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.4.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-
|
11
|
+
date: 2024-06-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -76,14 +76,22 @@ files:
|
|
76
76
|
- lib/apache_age/validators/unique_edge_validator.rb
|
77
77
|
- lib/apache_age/validators/unique_vertex_validator.rb
|
78
78
|
- lib/apache_age/validators/vertex_type_validator.rb
|
79
|
+
- lib/generators/apache_age/edge/USAGE
|
80
|
+
- lib/generators/apache_age/edge/edge_generator.rb
|
81
|
+
- lib/generators/apache_age/edge/templates/edge.rb.tt
|
82
|
+
- lib/generators/apache_age/node/USAGE
|
83
|
+
- lib/generators/apache_age/node/node_generator.rb
|
84
|
+
- lib/generators/apache_age/node/templates/node.rb.tt
|
79
85
|
- lib/rails_age.rb
|
80
86
|
- lib/rails_age/engine.rb
|
81
87
|
- lib/rails_age/version.rb
|
88
|
+
- lib/tasks/config_database.rake
|
89
|
+
- lib/tasks/config_migrate.rake
|
90
|
+
- lib/tasks/config_schema.rake
|
91
|
+
- lib/tasks/config_types.rake
|
82
92
|
- lib/tasks/copy_migrations.rake
|
83
|
-
- lib/tasks/database_config.rake
|
84
|
-
- lib/tasks/install.original.rake
|
85
93
|
- lib/tasks/install.rake
|
86
|
-
- lib/tasks/
|
94
|
+
- lib/tasks/migrate.rake
|
87
95
|
homepage: https://github.com/marpori/rails_age
|
88
96
|
licenses:
|
89
97
|
- MIT
|