rails_age 0.5.0 → 0.5.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +24 -7
- data/README.md +108 -57
- data/config/initializers/types.rb +20 -0
- data/lib/apache_age/edge.rb +5 -0
- data/lib/apache_age/entities/common_methods.rb +6 -0
- data/lib/apache_age/entities/edge.rb +4 -3
- data/lib/apache_age/entities/node.rb +53 -0
- data/lib/apache_age/entities/vertex.rb +1 -1
- data/lib/apache_age/node.rb +36 -0
- data/lib/apache_age/types/age_type_factory.rb +46 -0
- data/lib/apache_age/validators/expected_node_type.rb +17 -0
- data/lib/apache_age/validators/unique_node.rb +32 -0
- data/lib/apache_age/validators/{vertex_type_validator.rb → vertex_type_validator copy.rb } +2 -1
- data/lib/generators/apache_age/edge/edge_generator.rb +4 -2
- data/lib/generators/apache_age/edge/templates/edge.rb.tt +3 -3
- data/lib/generators/apache_age/{generate_entity_methods.rb → generator_entity_helpers.rb} +7 -4
- data/lib/generators/apache_age/generator_resource_helpers.rb +6 -0
- data/lib/generators/apache_age/node/node_generator.rb +4 -2
- data/lib/generators/apache_age/scaffold_edge/USAGE +16 -0
- data/lib/generators/apache_age/scaffold_edge/scaffold_edge_generator.rb +67 -0
- data/lib/generators/apache_age/scaffold_edge/scaffold_node_generator.rb +67 -0
- data/lib/generators/apache_age/scaffold_edge/templates/controller.rb.tt +50 -0
- data/lib/generators/apache_age/scaffold_edge/templates/views/_form.html.erb.tt +47 -0
- data/lib/generators/apache_age/scaffold_edge/templates/views/edit.html.erb.tt +12 -0
- data/lib/generators/apache_age/scaffold_edge/templates/views/index.html.erb.tt +16 -0
- data/lib/generators/apache_age/scaffold_edge/templates/views/new.html.erb.tt +11 -0
- data/lib/generators/apache_age/scaffold_edge/templates/views/partial.html.erb.tt +26 -0
- data/lib/generators/apache_age/scaffold_edge/templates/views/show.html.erb.tt +10 -0
- data/lib/generators/apache_age/scaffold_node/USAGE +16 -0
- data/lib/generators/apache_age/scaffold_node/scaffold_node_generator.rb +67 -0
- data/lib/generators/apache_age/scaffold_node/templates/controller.rb.tt +50 -0
- data/lib/generators/apache_age/scaffold_node/templates/views/_form.html.erb.tt +37 -0
- data/lib/generators/apache_age/scaffold_node/templates/views/edit.html.erb.tt +12 -0
- data/lib/generators/apache_age/scaffold_node/templates/views/index.html.erb.tt +16 -0
- data/lib/generators/apache_age/scaffold_node/templates/views/new.html.erb.tt +11 -0
- data/lib/generators/apache_age/scaffold_node/templates/views/partial.html.erb.tt +17 -0
- data/lib/generators/apache_age/scaffold_node/templates/views/show.html.erb.tt +10 -0
- data/lib/rails_age/version.rb +1 -1
- data/lib/rails_age.rb +7 -1
- data/lib/tasks/config_types.rake +4 -4
- metadata +59 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cd10883497fc245765a9e8313265e4d06c7a602a2f83cad592ecb5a951ab488d
|
4
|
+
data.tar.gz: ce03c6dc4535e2a54dddc9f2b9df8e4b413c6aeb690ee00138c4120fe1d72ce1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dcb00b35abd3a931c95ffce304c079eee2d1ba1b09ee3aa17f9c9150f4bc3b55276e7670cf3925e4ae484a117bef66bddc0242efc0cbc8295da03538ae628805
|
7
|
+
data.tar.gz: d6ac9aaa1ba0b04c9a48ed320e53a4de65a74e6cb6574f42c407b0c1124fafc650def68df4c41beaad27607b6f1fa22a7962518108c2fc39cd7c0e95f76d8c09
|
data/CHANGELOG.md
CHANGED
@@ -20,19 +20,36 @@ breaking change?: namespaces (by default) will use their own schema? (add to dat
|
|
20
20
|
|
21
21
|
- **multiple AGE Schema**
|
22
22
|
|
23
|
-
## VERSION 0.5.
|
23
|
+
## VERSION 0.5.4 - 2024-xx-xx
|
24
24
|
|
25
|
-
- **
|
26
|
-
*
|
27
|
-
|
28
|
-
## VERSION 0.5.2 - 2024-xx-xx
|
25
|
+
- **Fix**
|
26
|
+
* show validation errors in scaffold views
|
29
27
|
|
30
28
|
- **Edge Generator**
|
31
29
|
* add start-/end-nodes types to edge generator (would make scaffold easier), ie:
|
32
30
|
`rails generate apache_age:edge HasPet owner_role start_node:person end_node:pet`
|
33
31
|
with property and specified start-/end-nodes (person and pet nodes must have already been created)
|
34
32
|
|
35
|
-
|
33
|
+
- **Edge Scaffold** (generates edge, type, view and controller)
|
34
|
+
* add `rails generate apache_age:edge_scaffold HasJob employee_role start_node:person end_node:company`
|
35
|
+
|
36
|
+
## VERSION 0.5.3 - 2024-06-23
|
37
|
+
|
38
|
+
- **Edge Scaffold** (generates edge, type, view and controller) - without start-/end-nodes types!?
|
39
|
+
* add `rails generate apache_age:edge_scaffold HasJob employee_role`
|
40
|
+
* add system test (to dummy app after scaffold_node is run)
|
41
|
+
|
42
|
+
- **Node Scaffold** (generates node, type, view and controller)
|
43
|
+
* add system test (to dummy app after scaffold_node is run)
|
44
|
+
|
45
|
+
## VERSION 0.5.2 - 2024-06-16
|
46
|
+
|
47
|
+
- **Node Scaffold** (generates node, type, view and controller)
|
48
|
+
* add `rails generate apache_age:node_scaffold Person first_name last_name age:integer`
|
49
|
+
|
50
|
+
## VERSION 0.5.1 - 2024-06-16 (yanked)
|
51
|
+
|
52
|
+
**yanked** (2024-06-16) - had an issue with the generator
|
36
53
|
|
37
54
|
- **Node Scaffold** (generates node, type, view and controller)
|
38
55
|
* add `rails generate apache_age:node_scaffold Person first_name last_name age:integer`
|
@@ -43,7 +60,7 @@ breaking change?: namespaces (by default) will use their own schema? (add to dat
|
|
43
60
|
|
44
61
|
- **Edge Generator**
|
45
62
|
* add `rails generate apache_age:edge HasPet owner_role`
|
46
|
-
caveate: start_node and end_node are of type `:
|
63
|
+
caveate: start_node and end_node are of type `:node` 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)
|
47
64
|
|
48
65
|
## VERSION 0.4.1 - 2024-06-15
|
49
66
|
|
data/README.md
CHANGED
@@ -1,25 +1,76 @@
|
|
1
1
|
# RailsAge
|
2
2
|
|
3
|
-
|
3
|
+
Apache Age integration within a Rails application.
|
4
4
|
|
5
|
-
##
|
5
|
+
## Quick Start - Essentials
|
6
6
|
|
7
7
|
**NOTE:** you must be using Postgres as your database! Apache Age requires it.
|
8
8
|
|
9
|
-
|
9
|
+
```bash
|
10
|
+
bundle add rails_age
|
11
|
+
bundle install
|
12
|
+
bin/rails apache_age:install
|
13
|
+
# optional: prevents `bin/rails db:migrate` from modifying the schema file,
|
14
|
+
# bin/rails apache_age:override_db_migrate
|
15
|
+
git add .
|
16
|
+
git commit -m "Add & configure Apache Age within Rails"
|
17
|
+
```
|
18
|
+
|
19
|
+
## Generators
|
20
|
+
|
21
|
+
**NODES**
|
22
|
+
|
23
|
+
```bash
|
24
|
+
rails generate apache_age:scaffold_node Company company_name
|
25
|
+
|
26
|
+
rails generate apache_age:scaffold_node Person first_name last_name
|
27
|
+
```
|
28
|
+
|
29
|
+
**EDGES**
|
10
30
|
|
31
|
+
```bash
|
32
|
+
rails generate apache_age:scaffold_edge HasJob employee_role start_date:date
|
33
|
+
```
|
34
|
+
|
35
|
+
Ideally, edit the HasJob class so that `start_node` would use a type `:person` and the `end_node` uses at type `:company`
|
36
|
+
|
37
|
+
ie:
|
11
38
|
```ruby
|
12
|
-
|
39
|
+
# app/edges/has_job.rb
|
40
|
+
class HasJob
|
41
|
+
include ApacheAge::Entities::Edge
|
42
|
+
|
43
|
+
attribute :employee_role, :string
|
44
|
+
attribute :start_node, :person # instead of `:node`
|
45
|
+
attribute :end_node, :company # instead of `:node`
|
46
|
+
|
47
|
+
validates :employee_role, presence: true
|
48
|
+
validate :validate_unique_edge
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def validate_unique_edge
|
53
|
+
ApacheAge::Validators::UniqueEdge
|
54
|
+
.new(attributes: %i[employee_role start_node end_node])
|
55
|
+
.validate(self)
|
56
|
+
end
|
57
|
+
end
|
13
58
|
```
|
14
59
|
|
15
|
-
##
|
60
|
+
## Installation in Detail
|
16
61
|
|
17
62
|
using the installer, creates the migration to install age, runs the migration, and adjusts the schema file, and updates the `config/database.yml` file.
|
18
63
|
|
19
|
-
|
20
|
-
|
64
|
+
### Install Apache Age
|
65
|
+
|
66
|
+
see: [Apache AGE Installation](https://age.apache.org/age-manual/master/intro/setup.html#installation)
|
67
|
+
(The docker install is probably the easiest way to get started with a new application - for existing applications you may need to compile the extension from source.)
|
68
|
+
|
69
|
+
Verify your PostgreSQL AGE with the following commands:
|
70
|
+
|
21
71
|
```bash
|
22
|
-
psql -h localhost -p 5455 -U docker_username
|
72
|
+
$ psql -h localhost -p 5455 -U docker_username
|
73
|
+
|
23
74
|
> CREATE EXTENSION IF NOT EXISTS age;
|
24
75
|
> LOAD 'age';
|
25
76
|
> SET search_path = ag_catalog, "$user", public;
|
@@ -27,7 +78,12 @@ psql -h localhost -p 5455 -U docker_username
|
|
27
78
|
> \q
|
28
79
|
```
|
29
80
|
|
81
|
+
### Install and Configure Rails (if not done already)
|
82
|
+
|
83
|
+
AGE REQUIRES POSTGRESQL!
|
84
|
+
|
30
85
|
create a new Rails app (WITH POSTGRESQL!)
|
86
|
+
|
31
87
|
```bash
|
32
88
|
rails new age_demo -d postgresql
|
33
89
|
cd age_demo
|
@@ -35,6 +91,7 @@ git add .
|
|
35
91
|
git commit -m "Initial Rails App"
|
36
92
|
```
|
37
93
|
configure `config/database.yml` when using the docker version of AGE DB my config looks like:
|
94
|
+
|
38
95
|
```yaml
|
39
96
|
port: 5455
|
40
97
|
host: localhost
|
@@ -42,15 +99,19 @@ username: docker_username
|
|
42
99
|
password: dockerized_password
|
43
100
|
```
|
44
101
|
|
45
|
-
|
102
|
+
If both the Rails DB config and AGE DB are correctly configured, you should be able to run the following command without error:
|
103
|
+
|
46
104
|
```bash
|
47
105
|
rails db:create
|
48
106
|
rails db:migrate
|
49
107
|
git add .
|
50
|
-
git commit -m "
|
108
|
+
git commit -m "Basic Rails Configuration"
|
51
109
|
```
|
52
110
|
|
53
|
-
install Apache Age
|
111
|
+
### install Apache Age Plugin
|
112
|
+
|
113
|
+
NOTE: _ignore the `unknown OID` warnings_
|
114
|
+
|
54
115
|
```bash
|
55
116
|
bundle add rails_age
|
56
117
|
bundle install
|
@@ -62,62 +123,52 @@ git add .
|
|
62
123
|
git commit -m "Add & configure Apache Age within Rails"
|
63
124
|
```
|
64
125
|
|
65
|
-
|
126
|
+
### Optional Migration override (OPTIONAL)
|
127
|
+
|
128
|
+
run `bin/rails apache_age:override_db_migrate` to ensure that running `rails db:migrate` does not inappropriately modify the schema file.
|
129
|
+
|
130
|
+
However, if you are familiar with the schema file and git then you can safely ignore this step and manage the changes after a migration manually - only submitting changes directly related to the newest migration and not those related AGE.
|
131
|
+
|
132
|
+
**NOTE:**
|
133
|
+
* **You can run `bin/rails apache_age:config_schema` at any time to repair the schema file as needed.**
|
134
|
+
( **You can run `bin/rails apache_age:install` at any time to repair any AGE related config file**
|
135
|
+
|
136
|
+
If you are using `db/structure.sql` you will need to manually configure Apache Age (RailsAge).
|
137
|
+
|
138
|
+
### NODE Scaffold Generation
|
139
|
+
|
66
140
|
```bash
|
67
|
-
rails generate apache_age:
|
68
|
-
|
69
|
-
|
141
|
+
rails generate apache_age:scaffold_node Company company_name:string
|
142
|
+
|
143
|
+
# string is the default type (so it can be omitted)
|
144
|
+
rails generate apache_age:scaffold_node Person first_name last_name
|
145
|
+
|
146
|
+
# with a namespace
|
147
|
+
rails generate apache_age:scaffold_node Animals/Pet pet_name birthdate:date
|
70
148
|
```
|
71
|
-
|
149
|
+
|
150
|
+
### EDGE Scaffold Generation**
|
151
|
+
|
152
|
+
NOTE: the generator will only allow `:node` (default type) for start_node and end_node, however, it is strongly recommended to specify the start_node and end_node types manually. _Hopefully, I can find a way to get the generators to recognize and allow the usage of custom node types. Thus eventually, I hope: `rails generate apache_age:node HasPet start_node:person end_node:pet caretaker_role` will work._
|
153
|
+
|
72
154
|
```bash
|
73
|
-
# when start node and end node are not specified they are of type `:vertex`
|
74
|
-
# this is generally not recommended - exept when very generic relationships are needed
|
75
155
|
rails generate apache_age:edge HasJob employee_role begin_date:date
|
76
|
-
|
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
|
79
156
|
```
|
80
157
|
|
81
|
-
|
82
|
-
|
83
|
-
For now, if you are using `db/structure.sql` you will need to manually configure Apache Age (RailsAge) as described below.
|
158
|
+
_edge scaffold is coming soon._
|
84
159
|
|
85
|
-
|
160
|
+
```bash
|
161
|
+
# without a namespace
|
162
|
+
rails generate apache_age:scaffold_edge HasPet caretaker_role
|
86
163
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
fred = Person.new(first_name: 'Fredrick Jay', last_name: 'Flintstone')
|
91
|
-
fred.valid?
|
92
|
-
fred.save
|
93
|
-
fred.to_h # should have an ID
|
94
|
-
|
95
|
-
# fails because of a missing required field (Property)
|
96
|
-
incomplete = Person.new(first_name: 'Fredrick Jay')
|
97
|
-
incomplete.valid?
|
98
|
-
incomplete.errors
|
99
|
-
incomplete.to_h
|
100
|
-
|
101
|
-
# fails because of uniqueness constraints
|
102
|
-
jay = Person.create(first_name: 'Fredrick Jay', last_name: 'Flintstone')
|
103
|
-
jay.to_h
|
104
|
-
=> {:id=>nil, :first_name=>"Fredrick Jay", :last_name=>"Flintstone"}
|
105
|
-
jay.valid?
|
106
|
-
=> false
|
107
|
-
jay.errors
|
108
|
-
=> #<ActiveModel::Errors [#<ActiveModel::Error attribute=base, type=record not unique, options={}>, #<ActiveModel::Error attribute=first_name, type=property combination not unique, options={}>, #<ActiveModel::Error attribute=last_name, type=property combination not unique, options={}>]>
|
109
|
-
irb(main):008> jav.to_h
|
110
|
-
=> {:id=>nil, :first_name=>"Fredrick Jay", :last_name=>"Flintstone"}
|
111
|
-
|
112
|
-
# .create is a shortcut for .new and .save
|
113
|
-
quarry = Company.create(company_name: 'Bedrock Quarry')
|
114
|
-
quarry.to_h # should have an ID
|
115
|
-
|
116
|
-
# create an edge (no generator yet)
|
117
|
-
job = HasJob.create(start_node: fred, end_node: quarry, employee_role: 'Crane Operator')
|
118
|
-
job.to_h # should have an ID
|
164
|
+
# with a namespace
|
165
|
+
rails generate apache_age:scaffold_edge People/HasSpouse spousal_role
|
119
166
|
```
|
120
167
|
|
168
|
+
### AGE Usage within Rails Console
|
169
|
+
|
170
|
+
see [AGE Usage within Rails Console](AGE_CONSOLE_USAGE.md)
|
171
|
+
|
121
172
|
## Manual Install, Config and Usage
|
122
173
|
|
123
174
|
see [Manuel Installation, Configuration and Usage](MANUAL_INSTALL.md)
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# config/initializers/types.rb
|
2
|
+
|
3
|
+
require 'apache_age/types/age_type_factory'
|
4
|
+
# USAGE (with edges or nodes) - ie:
|
5
|
+
# require_dependency 'nodes/company'
|
6
|
+
# ActiveModel::Type.register(
|
7
|
+
# :company, ApacheAge::Types::AgeTypeFactory.create_type_for(Nodes::Company)
|
8
|
+
# )
|
9
|
+
|
10
|
+
Rails.application.config.to_prepare do
|
11
|
+
# Register AGE types
|
12
|
+
require_dependency 'apache_age/node'
|
13
|
+
ActiveModel::Type.register(
|
14
|
+
:node, ApacheAge::Types::AgeTypeFactory.create_type_for(ApacheAge::Node)
|
15
|
+
)
|
16
|
+
require_dependency 'apache_age/edge'
|
17
|
+
ActiveModel::Type.register(
|
18
|
+
:edge, ApacheAge::Types::AgeTypeFactory.create_type_for(ApacheAge::Edge)
|
19
|
+
)
|
20
|
+
end
|
@@ -7,6 +7,12 @@ module ApacheAge
|
|
7
7
|
def persisted? = id.present?
|
8
8
|
def to_s = ":#{age_label} #{properties_to_s}"
|
9
9
|
|
10
|
+
# default display value
|
11
|
+
def display
|
12
|
+
info = age_properties&.values&.first
|
13
|
+
info.blank? ? "#{age_label} (#{id})" : "#{info} (#{age_label})"
|
14
|
+
end
|
15
|
+
|
10
16
|
def to_h
|
11
17
|
base_h = attributes.to_hash
|
12
18
|
if age_type == 'edge'
|
@@ -9,11 +9,12 @@ module ApacheAge
|
|
9
9
|
include ActiveModel::Attributes
|
10
10
|
|
11
11
|
attribute :id, :integer
|
12
|
+
# attribute :label, :string
|
12
13
|
attribute :end_id, :integer
|
13
14
|
attribute :start_id, :integer
|
14
|
-
#
|
15
|
-
attribute :end_node
|
16
|
-
attribute :start_node
|
15
|
+
# override with a specific node type in the defining class
|
16
|
+
attribute :end_node
|
17
|
+
attribute :start_node
|
17
18
|
|
18
19
|
validates :end_node, :start_node, presence: true
|
19
20
|
validate :validate_nodes
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module ApacheAge
|
2
|
+
module Entities
|
3
|
+
module Vertex
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
included do
|
7
|
+
include ActiveModel::Model
|
8
|
+
include ActiveModel::Dirty
|
9
|
+
include ActiveModel::Attributes
|
10
|
+
|
11
|
+
attribute :id, :integer
|
12
|
+
|
13
|
+
extend ApacheAge::Entities::ClassMethods
|
14
|
+
include ApacheAge::Entities::CommonMethods
|
15
|
+
end
|
16
|
+
|
17
|
+
def age_type = 'vertex'
|
18
|
+
|
19
|
+
# AgeSchema::Nodes::Company.create(company_name: 'Bedrock Quarry')
|
20
|
+
# SELECT *
|
21
|
+
# FROM cypher('age_schema', $$
|
22
|
+
# CREATE (company:Company {company_name: 'Bedrock Quarry'})
|
23
|
+
# RETURN company
|
24
|
+
# $$) as (Company agtype);
|
25
|
+
def create_sql
|
26
|
+
alias_name = age_alias || age_label.downcase
|
27
|
+
<<-SQL
|
28
|
+
SELECT *
|
29
|
+
FROM cypher('#{age_graph}', $$
|
30
|
+
CREATE (#{alias_name}#{self})
|
31
|
+
RETURN #{alias_name}
|
32
|
+
$$) as (#{age_label} agtype);
|
33
|
+
SQL
|
34
|
+
end
|
35
|
+
|
36
|
+
# So far just properties of string type with '' around them
|
37
|
+
def update_sql
|
38
|
+
alias_name = age_alias || age_label.downcase
|
39
|
+
set_caluse =
|
40
|
+
age_properties.map { |k, v| v ? "#{alias_name}.#{k} = '#{v}'" : "#{alias_name}.#{k} = NULL" }.join(', ')
|
41
|
+
<<-SQL
|
42
|
+
SELECT *
|
43
|
+
FROM cypher('#{age_graph}', $$
|
44
|
+
MATCH (#{alias_name}:#{age_label})
|
45
|
+
WHERE id(#{alias_name}) = #{id}
|
46
|
+
SET #{set_caluse}
|
47
|
+
RETURN #{alias_name}
|
48
|
+
$$) as (#{age_label} agtype);
|
49
|
+
SQL
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module ApacheAge
|
2
|
+
class Node
|
3
|
+
include ApacheAge::Entities::Node
|
4
|
+
|
5
|
+
attribute :label, :string
|
6
|
+
attribute :properties
|
7
|
+
# attribute :properties, :hash
|
8
|
+
|
9
|
+
# def display = [label, properties&.values&.first].compact.join(' - ')
|
10
|
+
def display
|
11
|
+
info = properties&.values&.first
|
12
|
+
info.blank? ? "#{label} (#{id})" : "#{info} (#{label})"
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.all
|
16
|
+
all_nodes_sql = <<~SQL
|
17
|
+
SELECT *
|
18
|
+
FROM cypher('age_schema', $$
|
19
|
+
MATCH (node)
|
20
|
+
RETURN node
|
21
|
+
$$) as (node agtype);
|
22
|
+
SQL
|
23
|
+
age_results = ActiveRecord::Base.connection.execute(all_nodes_sql)
|
24
|
+
return [] if age_results.values.count.zero?
|
25
|
+
|
26
|
+
age_results.values.map do |result|
|
27
|
+
json_string = result.first.split('::').first
|
28
|
+
hash = JSON.parse(json_string)
|
29
|
+
attribs = hash.slice('id', 'label').symbolize_keys
|
30
|
+
attribs[:properties] = hash['properties'].symbolize_keys
|
31
|
+
|
32
|
+
new(**attribs)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# lib/apache_age/types/age_type_factory.rb
|
2
|
+
# Automatically generates ActiveModel::Type classes
|
3
|
+
# Dynamically builds this (as a concrete example):
|
4
|
+
# module ApacheAge
|
5
|
+
# module Types
|
6
|
+
# class CompanyType < ActiveModel::Type::Value
|
7
|
+
# def cast(value)
|
8
|
+
# case value
|
9
|
+
# when Nodes::Company
|
10
|
+
# value
|
11
|
+
# when Hash
|
12
|
+
# Nodes::Company.new(value)
|
13
|
+
# else
|
14
|
+
# nil
|
15
|
+
# end
|
16
|
+
# end
|
17
|
+
# def serialize(value)
|
18
|
+
# value.is_a?(Nodes::Company) ? value.attributes : nil
|
19
|
+
# end
|
20
|
+
# end
|
21
|
+
# end
|
22
|
+
# end
|
23
|
+
module ApacheAge
|
24
|
+
module Types
|
25
|
+
class AgeTypeFactory
|
26
|
+
def self.create_type_for(klass)
|
27
|
+
Class.new(ActiveModel::Type::Value) do
|
28
|
+
define_method(:cast) do |value|
|
29
|
+
case value
|
30
|
+
when klass
|
31
|
+
value
|
32
|
+
when Hash
|
33
|
+
klass.new(value)
|
34
|
+
else
|
35
|
+
nil
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
define_method(:serialize) do |value|
|
40
|
+
value.is_a?(klass) ? value.attributes : nil
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module ApacheAge
|
2
|
+
module Validators
|
3
|
+
module ExpectedNodeType
|
4
|
+
def
|
5
|
+
# Register the AGE types vertex_attribute(attribute_name, type_symbol, klass)
|
6
|
+
attribute attribute_name, type_symbol
|
7
|
+
|
8
|
+
validate do
|
9
|
+
value = send(attribute_name)
|
10
|
+
unless value.is_a?(klass)
|
11
|
+
errors.add(attribute_name, "must be a #{klass.name}")
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# lib/apache_age/validators/unique_node.rb
|
2
|
+
|
3
|
+
# Usage:
|
4
|
+
# validates_with(
|
5
|
+
# ApacheAge::Validators::UniqueNode,
|
6
|
+
# attributes: [:first_name, :last_name, :gender]
|
7
|
+
# )
|
8
|
+
|
9
|
+
module ApacheAge
|
10
|
+
module Validators
|
11
|
+
class UniqueNode < ActiveModel::Validator
|
12
|
+
def validate(record)
|
13
|
+
allowed_keys = record.age_properties.keys
|
14
|
+
attributes = options[:attributes]
|
15
|
+
return if attributes.blank?
|
16
|
+
|
17
|
+
record_attribs =
|
18
|
+
attributes
|
19
|
+
.map { |attr| [attr, record.send(attr)] }
|
20
|
+
.to_h.symbolize_keys
|
21
|
+
.slice(*allowed_keys)
|
22
|
+
query = record.class.find_by(record_attribs)
|
23
|
+
|
24
|
+
# if no match is found or if it finds itself, it's valid
|
25
|
+
return if query.blank? || (query.id == record.id)
|
26
|
+
|
27
|
+
record.errors.add(:base, 'record not unique')
|
28
|
+
attributes.each { record.errors.add(_1, 'property combination not unique') }
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -2,13 +2,15 @@
|
|
2
2
|
require 'rails/generators'
|
3
3
|
require 'rails/generators/named_base'
|
4
4
|
|
5
|
-
require_relative '../
|
5
|
+
require_relative '../generator_entity_helpers'
|
6
6
|
# TODO: get generators to work with custom types!
|
7
7
|
# require_relative "#{Rails.root}/config/initializers/types"
|
8
8
|
|
9
9
|
module ApacheAge
|
10
10
|
class EdgeGenerator < Rails::Generators::NamedBase
|
11
|
-
include ApacheAge::
|
11
|
+
include ApacheAge::GeneratorEntityHelpers
|
12
|
+
|
13
|
+
desc "Generates edge (model) with attributes."
|
12
14
|
|
13
15
|
source_root File.expand_path('templates', __dir__)
|
14
16
|
argument :attributes, type: :array, default: [], banner: "field:type field:type"
|
@@ -4,10 +4,10 @@ class <%= class_name %>
|
|
4
4
|
<%- attributes_list.each do |attribute| -%>
|
5
5
|
attribute :<%= attribute[:name] %>, :<%= attribute[:reference] || attribute[:type] %>
|
6
6
|
<%- end -%>
|
7
|
-
# recommendation for (start_node and end_node): change `:
|
7
|
+
# recommendation for (start_node and end_node): change `:node` with the actual 'node' type
|
8
8
|
# see `config/initializers/apache_age.rb` for the list of available node types
|
9
|
-
attribute :start_node
|
10
|
-
attribute :end_node
|
9
|
+
attribute :start_node
|
10
|
+
attribute :end_node
|
11
11
|
|
12
12
|
<%- attributes_list.each do |attribute| -%>
|
13
13
|
validates :<%= attribute[:name] %>, presence: true
|
@@ -1,14 +1,17 @@
|
|
1
|
-
# lib/generators/apache_age/
|
1
|
+
# lib/generators/apache_age/generator_entity_helpers.rb
|
2
2
|
|
3
3
|
module ApacheAge
|
4
|
-
module
|
4
|
+
module GeneratorEntityHelpers
|
5
5
|
def generate_age_entity(age_type)
|
6
|
-
template
|
6
|
+
template(
|
7
|
+
"#{age_type}.rb.tt",
|
8
|
+
File.join(Rails.root, "app/#{age_type}s", class_path, "#{file_name}.rb")
|
9
|
+
)
|
7
10
|
add_type_config
|
8
11
|
end
|
9
12
|
|
10
13
|
def destroy_age_entity(age_type)
|
11
|
-
file_path = File.join(
|
14
|
+
file_path = File.join(Rails.root, "app/#{age_type}s", class_path, "#{file_name}.rb")
|
12
15
|
File.delete(file_path) if File.exist?(file_path)
|
13
16
|
remove_type_config
|
14
17
|
end
|
@@ -2,11 +2,13 @@
|
|
2
2
|
require 'rails/generators'
|
3
3
|
require 'rails/generators/named_base'
|
4
4
|
|
5
|
-
require_relative '../
|
5
|
+
require_relative '../generator_entity_helpers'
|
6
6
|
|
7
7
|
module ApacheAge
|
8
8
|
class NodeGenerator < Rails::Generators::NamedBase
|
9
|
-
include ApacheAge::
|
9
|
+
include ApacheAge::GeneratorEntityHelpers
|
10
|
+
|
11
|
+
desc "Generates node (model) with attributes."
|
10
12
|
|
11
13
|
source_root File.expand_path('templates', __dir__)
|
12
14
|
argument :attributes, type: :array, default: [], banner: "field:type field:type"
|
@@ -0,0 +1,16 @@
|
|
1
|
+
Description:
|
2
|
+
Explain the generator
|
3
|
+
|
4
|
+
Example:
|
5
|
+
bin/rails generate apache_age:scaffold_edge HasPet careciver_role
|
6
|
+
|
7
|
+
This will create:
|
8
|
+
create app/edge/has_pet.rb
|
9
|
+
modified: config/initializers/types.rb
|
10
|
+
create app/controllers/has_pets_controller.rb
|
11
|
+
route resources :has_pets
|
12
|
+
create app/views/pets/index.html.erb
|
13
|
+
create app/views/pets/edit.html.erb
|
14
|
+
create app/views/pets/show.html.erb
|
15
|
+
create app/views/pets/new.html.erb
|
16
|
+
create app/views/pets/partial.html.erb
|