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