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
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8228c60b43732a31b364b658e65b5d6a2e1b76018b573b1ec5b7fa25582f519c
|
4
|
+
data.tar.gz: 60f5211cb37aa6605523f76cde81158b6495448e90d4fbeabecd020c38520c4e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: eec16e9c7b4512793b4aac9a3ac1c9ee0ce6280a1033712cc6aed5466218ffa17a47c1fca4d1f4d0508ce38e4a12a9a0c392d927f72a62c5ebcf8d2ea7c343f5
|
7
|
+
data.tar.gz: 3ccdeba21d32bc35de9162698c5c340efce5b0557b5930a3b1e0d9caaf5697f5afcd2b39ad65d0c976b5b3bc46df5edaf18a7bb96e023a2090642b08eb002786
|
data/CHANGELOG.md
CHANGED
@@ -1,21 +1,64 @@
|
|
1
1
|
# Change Log
|
2
2
|
|
3
|
-
## VERSION 0.
|
3
|
+
## VERSION 0.5.0 - 2024-xx-xx
|
4
|
+
|
5
|
+
- **AGE Schema override** (instance and class methods) assumes db and migrations up-to-date
|
4
6
|
|
5
7
|
- **cypher**
|
8
|
+
* schema override
|
6
9
|
* query support
|
7
10
|
* paths support
|
8
11
|
* select attributes support
|
12
|
+
|
9
13
|
- **Paths**
|
10
|
-
* ?
|
11
14
|
|
12
|
-
## VERSION 0.
|
15
|
+
## VERSION 0.4.4 - 2024-xx-xx
|
16
|
+
|
17
|
+
- **Edge Scaffold** (generates edge, type, view and controller)
|
18
|
+
* add `rails generate apache_age:edge_scaffold HasJob employee_role start_node:person end_node:company`
|
19
|
+
|
20
|
+
## VERSION 0.4.3 - 2024-xx-xx
|
21
|
+
|
22
|
+
- **Node Scaffold** (generates node, type, view and controller)
|
23
|
+
* add `rails generate apache_age:node_scaffold Person first_name last_name age:integer`
|
24
|
+
|
25
|
+
## VERSION 0.4.2 - 2024-xx-xx
|
26
|
+
|
27
|
+
- **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)
|
31
|
+
|
32
|
+
## VERSION 0.4.1 - 2024-xx-xx
|
33
|
+
|
34
|
+
- **OPTIONAL Installer**
|
35
|
+
* add `config_migrate` to `rails generate apache_age:install` auto fix the schema after `rails db:migrate`
|
36
|
+
|
37
|
+
## VERSION 0.4.0 - 2024-06-14
|
38
|
+
|
39
|
+
Minor breaking change: type (:vertix) is now required in core for edges
|
40
|
+
|
41
|
+
- **Installer**
|
42
|
+
* AGE types added to installer (with tests)
|
43
|
+
|
44
|
+
- **Node Generator**
|
45
|
+
* add also creates node types (with tests)
|
46
|
+
|
47
|
+
- **Apache AGE Migrate**
|
48
|
+
* add `bin/rails apache_age:migrate` runs `bin/rails db:migrate` followed by `bin/rails apache_age:config_schema` to fix the schema file after `bin/rails db:migrate`
|
49
|
+
|
50
|
+
## VERSION 0.3.2 - 2024-06-08
|
51
|
+
|
52
|
+
- **Node Generator**
|
53
|
+
* add `rails generate apache_age:node Pets/Cat name age:integer` creates a node with a namespace and attributes at: `app/nodes/pets/cat.rb`
|
54
|
+
* add `rails generate apache_age:node Cat name age:integer` creates a node with attributes at: `app/nodes/cat.rb`
|
55
|
+
* add `rails destroy apache_age:node Cat` deletes an existing node at: `app/nodes/cat.rb`
|
56
|
+
|
57
|
+
## VERSION 0.3.1 - 2024-06-02
|
13
58
|
|
14
|
-
- **Generators**
|
15
|
-
* add `rails generate apache_age:node` to create a node model (with its type in initializer)
|
16
|
-
* add `rails generate apache_age:edge` to create an edge model (with its type in initializer)
|
17
59
|
- **Installer**
|
18
60
|
* refactor into multiple independent tasks with tests
|
61
|
+
|
19
62
|
- **Documentation**
|
20
63
|
* updated README with additional information
|
21
64
|
* added `db/structure.sql` config to README
|
@@ -24,6 +67,7 @@
|
|
24
67
|
|
25
68
|
- **Edges**
|
26
69
|
* `find_by(start_node:, :end_node:, properties:)` to find an edge with specific nodes & properties (deprecated `find_edge`)
|
70
|
+
|
27
71
|
- **Installer** (`rails generate apache_age:install`)
|
28
72
|
* copy Age PG Extenstion migration to `db/migrate`
|
29
73
|
* run the AGE PG Migration
|
@@ -39,6 +83,7 @@ NOTE: the `rails generate apache_age:install` can be run at any time to repair t
|
|
39
83
|
* add missing methods to use in rails controllers
|
40
84
|
* validate edge start- & end-nodes are valid
|
41
85
|
* add unique edge validations
|
86
|
+
|
42
87
|
- **Nodes**
|
43
88
|
* add missing methods to use in rails controllers
|
44
89
|
* add unique node validations
|
@@ -50,9 +95,11 @@ Initial release has the following features:
|
|
50
95
|
- **Nodes:**
|
51
96
|
* `.create`, `.read`, `.update`, `.delete`, `.all`, `.find(by id)`, `.find_by(age_properties)`
|
52
97
|
* verified with usage in a controller and views
|
98
|
+
|
53
99
|
- **Edges:**
|
54
100
|
*`.create`, `.read`, `.update`, `.delete`, `.all`, `.find(by id)`, `.find_by(age_properties)`
|
55
101
|
* verified with usage in a controller and views
|
102
|
+
|
56
103
|
- **Entities:**
|
57
104
|
* `.all`, `.find(id)`, `.find_by(age_property)` use these when class, label, edge, node
|
58
105
|
|
data/README.md
CHANGED
@@ -12,368 +12,109 @@ Add this line to your application's Gemfile:
|
|
12
12
|
gem "rails_age"
|
13
13
|
```
|
14
14
|
|
15
|
-
|
15
|
+
## Quick Start
|
16
16
|
|
17
17
|
using the installer, creates the migration to install age, runs the migration, and adjusts the schema file, and updates the `config/database.yml` file.
|
18
18
|
|
19
|
+
setup (& Test) postgresql with AGE (using the docker version of AGE DB may be the easiest way to get started)
|
20
|
+
using the docker version of AGE DB, you can confirm psql AGE with the following commands:
|
19
21
|
```bash
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
22
|
+
psql -h localhost -p 5455 -U docker_username
|
23
|
+
> CREATE EXTENSION IF NOT EXISTS age;
|
24
|
+
> LOAD 'age';
|
25
|
+
> SET search_path = ag_catalog, "$user", public;
|
26
|
+
> SELECT create_graph('age_schema');
|
27
|
+
> \q
|
24
28
|
```
|
25
29
|
|
26
|
-
|
27
|
-
|
28
|
-
For now, if you are using `db/structure.sql` you will need to manually configure Apache Age (RailsAge) as described below.
|
29
|
-
|
30
|
-
### Manual Install
|
31
|
-
|
32
|
-
create a migration to add the Apache Age extension to your database
|
30
|
+
create a new Rails app (WITH POSTGRESQL!)
|
33
31
|
```bash
|
34
|
-
|
32
|
+
rails new age_demo -d postgresql
|
33
|
+
cd age_demo
|
34
|
+
git add .
|
35
|
+
git commit -m "Initial Rails App"
|
35
36
|
```
|
36
|
-
|
37
|
-
```
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
# Load the age code
|
44
|
-
execute("LOAD 'age';")
|
45
|
-
|
46
|
-
# Load the ag_catalog into the search path
|
47
|
-
execute('SET search_path = ag_catalog, "$user", public;')
|
48
|
-
|
49
|
-
# Create age_schema graph if it doesn't exist
|
50
|
-
execute("SELECT create_graph('age_schema');")
|
51
|
-
end
|
52
|
-
|
53
|
-
def down
|
54
|
-
execute <<-SQL
|
55
|
-
DO $$
|
56
|
-
BEGIN
|
57
|
-
IF EXISTS (
|
58
|
-
SELECT 1
|
59
|
-
FROM pg_constraint
|
60
|
-
WHERE conname = 'fk_graph_oid'
|
61
|
-
) THEN
|
62
|
-
ALTER TABLE ag_catalog.ag_label
|
63
|
-
DROP CONSTRAINT fk_graph_oid;
|
64
|
-
END IF;
|
65
|
-
END $$;
|
66
|
-
SQL
|
67
|
-
|
68
|
-
execute("SELECT drop_graph('age_schema', true);")
|
69
|
-
execute('DROP SCHEMA IF EXISTS ag_catalog CASCADE;')
|
70
|
-
execute('DROP EXTENSION IF EXISTS age;')
|
71
|
-
end
|
72
|
-
end
|
37
|
+
configure `config/database.yml` when using the docker version of AGE DB my config looks like:
|
38
|
+
```yaml
|
39
|
+
port: 5455
|
40
|
+
host: localhost
|
41
|
+
username: docker_username
|
42
|
+
password: dockerized_password
|
73
43
|
```
|
74
|
-
into your new migration file
|
75
44
|
|
76
|
-
|
45
|
+
now you should be able to create the rails database:
|
77
46
|
```bash
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
```ruby
|
83
|
-
ActiveRecord::Schema[7.1].define(version: 2024_05_21_062349) do
|
84
|
-
create_schema "ag_catalog"
|
85
|
-
create_schema "age_schema"
|
86
|
-
|
87
|
-
# These are extensions that must be enabled in order to support this database
|
88
|
-
enable_extension "age"
|
89
|
-
enable_extension "plpgsql"
|
90
|
-
|
91
|
-
# Could not dump table "_ag_label_edge" because of following StandardError
|
92
|
-
# Unknown type 'graphid' for column 'id'
|
93
|
-
|
94
|
-
# Could not dump table "_ag_label_vertex" because of following StandardError
|
95
|
-
# Unknown type 'graphid' for column 'id'
|
96
|
-
|
97
|
-
# Could not dump table "ag_graph" because of following StandardError
|
98
|
-
# Unknown type 'regnamespace' for column 'namespace'
|
99
|
-
|
100
|
-
# Could not dump table "ag_label" because of following StandardError
|
101
|
-
# Unknown type 'regclass' for column 'relation'
|
102
|
-
|
103
|
-
add_foreign_key "ag_label", "ag_graph", column: "graph", primary_key: "graphid", name: "fk_graph_oid"
|
104
|
-
|
105
|
-
# other migrations
|
106
|
-
# ...
|
107
|
-
end
|
108
|
-
```
|
109
|
-
|
110
|
-
and replace them with the following lines:
|
111
|
-
```ruby
|
112
|
-
ActiveRecord::Schema[7.1].define(version: 2024_05_21_062349) do
|
113
|
-
# These are extensions that must be enabled in order to support this database
|
114
|
-
enable_extension 'plpgsql'
|
115
|
-
|
116
|
-
# Allow age extension
|
117
|
-
execute('CREATE EXTENSION IF NOT EXISTS age;')
|
118
|
-
|
119
|
-
# Load the age code
|
120
|
-
execute("LOAD 'age';")
|
121
|
-
|
122
|
-
# Load the ag_catalog into the search path
|
123
|
-
execute('SET search_path = ag_catalog, "$user", public;')
|
124
|
-
|
125
|
-
# Create age_schema graph if it doesn't exist
|
126
|
-
execute("SELECT create_graph('age_schema');")
|
127
|
-
|
128
|
-
# other migrations
|
129
|
-
# ...
|
130
|
-
end
|
47
|
+
rails db:create
|
48
|
+
rails db:migrate
|
49
|
+
git add .
|
50
|
+
git commit -m "Add Apache Age Postgres DB configured with Rails App"
|
131
51
|
```
|
132
52
|
|
133
|
-
|
134
|
-
```
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
-- Load the age module
|
142
|
-
LOAD 'age';
|
143
|
-
|
144
|
-
-- Load the ag_catalog into the search path
|
145
|
-
SET search_path = ag_catalog, "$user", public;
|
146
|
-
|
147
|
-
-- Create age_schema graph if it doesn't exist
|
148
|
-
SELECT create_graph('age_schema');
|
149
|
-
|
150
|
-
# other migrations
|
151
|
-
# ...
|
152
|
-
|
153
|
-
INSERT INTO "schema_migrations" (version) VALUES
|
154
|
-
('20110315075839'),
|
155
|
-
--- ...
|
156
|
-
('20240521062349');
|
53
|
+
install Apache Age (you can ignore the `unknown OID` warnings)
|
54
|
+
```bash
|
55
|
+
bundle add rails_age
|
56
|
+
bundle install
|
57
|
+
bin/rails apache_age:install
|
58
|
+
git add .
|
59
|
+
git commit -m "Add Apache Age to Rails"
|
157
60
|
```
|
158
61
|
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
166
|
-
|
167
|
-
## Usage
|
168
|
-
|
169
|
-
I suggest you creat a folder create a folder called `app/nodes` and `app/edges` to keep the code organized.
|
170
|
-
I frequently use the `app/graphs` folder to keep all the graph related code together in a Module (as is done in the [rails age demo app](https://github.com/marpori/rails_age_demo_app))
|
171
|
-
|
172
|
-
I suggest you creat a folder within app called `graphs` and under that create a folder called `nodes` and `edges`. This will help you keep your code organized.
|
173
|
-
|
174
|
-
A trival, but fully functional [rails age demo app](https://github.com/marpori/rails_age_demo_app), based on the Flintstones Commic, is available for reference.
|
175
|
-
|
176
|
-
### Nodes
|
177
|
-
|
178
|
-
```ruby
|
179
|
-
# app/graphs/nodes/company.rb
|
180
|
-
module Nodes
|
181
|
-
class Company
|
182
|
-
include ApacheAge::Entities::Vertex
|
183
|
-
|
184
|
-
attribute :company_name, :string
|
185
|
-
|
186
|
-
validates :company_name, presence: true
|
187
|
-
validates_with(
|
188
|
-
ApacheAge::Validators::UniqueVertexValidator,
|
189
|
-
attributes: [:company_name]
|
190
|
-
)
|
191
|
-
end
|
192
|
-
end
|
62
|
+
make some nodes :string is the default type
|
63
|
+
```bash
|
64
|
+
rails generate apache_age:node Company company_name
|
65
|
+
rails generate apache_age:node Person first_name last_name
|
66
|
+
rails generate apache_age:node Pet pet_name:string age:integer
|
193
67
|
```
|
68
|
+
make some edges (`:vertex` is the default type) for start_node and end_node
|
69
|
+
```bash
|
70
|
+
# when start node and end node are not specified they are of type `:vertex`
|
71
|
+
# this is generally not recommended - exept when very generic relationships are needed
|
72
|
+
rails generate apache_age:edge HasJob employee_role
|
194
73
|
|
195
|
-
|
196
|
-
|
197
|
-
module Nodes
|
198
|
-
class Person
|
199
|
-
include ApacheAge::Entities::Vertex
|
200
|
-
|
201
|
-
attribute :first_name, :string, default: nil
|
202
|
-
attribute :last_name, :string, default: nil
|
203
|
-
attribute :given_name, :string, default: nil
|
204
|
-
attribute :nick_name, :string, default: nil
|
205
|
-
attribute :gender, :string, default: nil
|
206
|
-
|
207
|
-
validates :gender, :first_name, :last_name, :given_name, :nick_name,
|
208
|
-
presence: true
|
209
|
-
|
210
|
-
def initialize(**attributes)
|
211
|
-
super
|
212
|
-
# use unless present? since attributes when empty sets to "" by default
|
213
|
-
self.nick_name = first_name unless nick_name.present?
|
214
|
-
self.given_name = last_name unless given_name.present?
|
215
|
-
end
|
216
|
-
end
|
217
|
-
end
|
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
|
218
76
|
```
|
219
77
|
|
220
|
-
|
221
|
-
|
222
|
-
```ruby
|
223
|
-
# app/graphs/edges/has_job.rb
|
224
|
-
module Edges
|
225
|
-
class HasJob
|
226
|
-
include ApacheAge::Entities::Edge
|
227
|
-
|
228
|
-
attribute :employee_role, :string
|
229
|
-
attribute :start_node, :person
|
230
|
-
attribute :end_node, :company
|
231
|
-
|
232
|
-
validates :employee_role, presence: true
|
233
|
-
validate :validate_unique
|
234
|
-
# or with a one-liner
|
235
|
-
# validates_with(
|
236
|
-
# ApacheAge::Validators::UniqueEdgeValidator,
|
237
|
-
# attributes: %i[employee_role start_node end_node]
|
238
|
-
# )
|
78
|
+
**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.**
|
239
79
|
|
240
|
-
|
241
|
-
|
242
|
-
def validate_unique
|
243
|
-
ApacheAge::Validators::UniqueEdgeValidator
|
244
|
-
.new(attributes: %i[employee_role start_node end_node])
|
245
|
-
.validate(self)
|
246
|
-
end
|
247
|
-
end
|
248
|
-
end
|
249
|
-
```
|
80
|
+
For now, if you are using `db/structure.sql` you will need to manually configure Apache Age (RailsAge) as described below.
|
250
81
|
|
251
82
|
### Rails Console Usage
|
252
83
|
|
253
84
|
```ruby
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
```
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
# Ensure the files are loaded
|
291
|
-
require_dependency 'nodes/company'
|
292
|
-
require_dependency 'nodes/person'
|
293
|
-
|
294
|
-
# Register the custom types
|
295
|
-
ActiveModel::Type.register(:company, ApacheAge::Types::AgeTypeGenerator.create_type_for(Nodes::Company))
|
296
|
-
ActiveModel::Type.register(:person, ApacheAge::Types::AgeTypeGenerator.create_type_for(Nodes::Person))
|
297
|
-
end
|
298
|
-
```
|
299
|
-
|
300
|
-
### Controller Usage
|
301
|
-
|
302
|
-
```ruby
|
303
|
-
# app/controllers/people_controller.rb
|
304
|
-
class PeopleController < ApplicationController
|
305
|
-
before_action :set_person, only: %i[show edit update destroy]
|
306
|
-
|
307
|
-
# GET /people or /people.json
|
308
|
-
def index
|
309
|
-
@people = Nodes::Person.all
|
310
|
-
end
|
311
|
-
|
312
|
-
# GET /people/1 or /people/1.json
|
313
|
-
def show; end
|
314
|
-
|
315
|
-
# GET /people/new
|
316
|
-
def new
|
317
|
-
@person = Nodes::Person.new
|
318
|
-
end
|
319
|
-
|
320
|
-
# GET /people/1/edit
|
321
|
-
def edit; end
|
322
|
-
|
323
|
-
# POST /people or /people.json
|
324
|
-
def create
|
325
|
-
@person = Nodes::Person.new(**person_params)
|
326
|
-
respond_to do |format|
|
327
|
-
if @person.save
|
328
|
-
format.html { redirect_to person_url(@person), notice: 'Person was successfully created.' }
|
329
|
-
format.json { render :show, status: :created, location: @person }
|
330
|
-
else
|
331
|
-
format.html { render :new, status: :unprocessable_entity }
|
332
|
-
format.json { render json: @person.errors, status: :unprocessable_entity }
|
333
|
-
end
|
334
|
-
end
|
335
|
-
end
|
336
|
-
|
337
|
-
# PATCH/PUT /people/1 or /people/1.json
|
338
|
-
def update
|
339
|
-
respond_to do |format|
|
340
|
-
if @person.update(**person_params)
|
341
|
-
format.html { redirect_to person_url(@person), notice: 'Person was successfully updated.' }
|
342
|
-
format.json { render :show, status: :ok, location: @person }
|
343
|
-
else
|
344
|
-
format.html { render :edit, status: :unprocessable_entity }
|
345
|
-
format.json { render json: @person.errors, status: :unprocessable_entity }
|
346
|
-
end
|
347
|
-
end
|
348
|
-
end
|
349
|
-
|
350
|
-
# DELETE /people/1 or /people/1.json
|
351
|
-
def destroy
|
352
|
-
@person.destroy!
|
353
|
-
|
354
|
-
respond_to do |format|
|
355
|
-
format.html { redirect_to people_url, notice: 'Person was successfully destroyed.' }
|
356
|
-
format.json { head :no_content }
|
357
|
-
end
|
358
|
-
end
|
359
|
-
|
360
|
-
private
|
361
|
-
|
362
|
-
# Use callbacks to share common setup or constraints between actions.
|
363
|
-
def set_person
|
364
|
-
@person = Nodes::Person.find(params[:id])
|
365
|
-
end
|
366
|
-
|
367
|
-
# Only allow a list of trusted parameters through.
|
368
|
-
def person_params
|
369
|
-
# params.fetch(:person, {})
|
370
|
-
params.require(:nodes_person).permit(:first_name, :last_name, :nick_name, :given_name, :gender)
|
371
|
-
end
|
372
|
-
end
|
373
|
-
```
|
374
|
-
|
375
|
-
### Views
|
376
|
-
|
377
|
-
```erb
|
378
|
-
|
379
|
-
```
|
85
|
+
bin/rails c
|
86
|
+
|
87
|
+
fred = Person.new(first_name: 'Fredrick Jay', last_name: 'Flintstone')
|
88
|
+
fred.valid?
|
89
|
+
fred.save
|
90
|
+
fred.to_h # should have an ID
|
91
|
+
|
92
|
+
# fails because of a missing required field (Property)
|
93
|
+
incomplete = Person.new(first_name: 'Fredrick Jay')
|
94
|
+
incomplete.valid?
|
95
|
+
incomplete.errors
|
96
|
+
incomplete.to_h
|
97
|
+
|
98
|
+
# fails because of uniqueness constraints
|
99
|
+
jay = Person.create(first_name: 'Fredrick Jay', last_name: 'Flintstone')
|
100
|
+
jay.to_h
|
101
|
+
=> {:id=>nil, :first_name=>"Fredrick Jay", :last_name=>"Flintstone"}
|
102
|
+
jay.valid?
|
103
|
+
=> false
|
104
|
+
jay.errors
|
105
|
+
=> #<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={}>]>
|
106
|
+
irb(main):008> jav.to_h
|
107
|
+
=> {:id=>nil, :first_name=>"Fredrick Jay", :last_name=>"Flintstone"}
|
108
|
+
|
109
|
+
# .create is a shortcut for .new and .save
|
110
|
+
quarry = Company.create(company_name: 'Bedrock Quarry')
|
111
|
+
quarry.to_h # should have an ID
|
112
|
+
|
113
|
+
# create an edge (no generator yet)
|
114
|
+
job = HasJob.create(start_node: fred, end_node: quarry, employee_role: 'Crane Operator')
|
115
|
+
job.to_h # should have an ID
|
116
|
+
```
|
117
|
+
|
118
|
+
## Manual Install, Config and Usage
|
119
|
+
|
120
|
+
see [Manuel Installation, Configuration and Usage](MANUAL_INSTALL.md)
|
@@ -11,9 +11,9 @@ module ApacheAge
|
|
11
11
|
attribute :id, :integer
|
12
12
|
attribute :end_id, :integer
|
13
13
|
attribute :start_id, :integer
|
14
|
-
#
|
15
|
-
attribute :end_node
|
16
|
-
attribute :start_node
|
14
|
+
# type: `:vertex` can be overriden with a specific node type
|
15
|
+
attribute :end_node, :vertex
|
16
|
+
attribute :start_node, :vertex
|
17
17
|
|
18
18
|
validates :end_node, :start_node, presence: true
|
19
19
|
validate :validate_nodes
|
@@ -35,10 +35,10 @@ module ApacheAge
|
|
35
35
|
query = record.class.find_by(edge_attribs.compact)
|
36
36
|
return if query.blank? || (query.id == record.id)
|
37
37
|
|
38
|
-
record.errors.add(:base, '
|
39
|
-
record.errors.add(:end_node, '
|
40
|
-
record.errors.add(:start_node, '
|
41
|
-
attributes.each { record.errors.add(_1, '
|
38
|
+
record.errors.add(:base, 'record not unique')
|
39
|
+
record.errors.add(:end_node, 'node combination not unique')
|
40
|
+
record.errors.add(:start_node, 'node combination not unique')
|
41
|
+
attributes.each { record.errors.add(_1, 'prpoerty combination not unique') }
|
42
42
|
end
|
43
43
|
|
44
44
|
private
|
@@ -20,8 +20,8 @@ module ApacheAge
|
|
20
20
|
# if no match is found or if it finds itself, it's valid
|
21
21
|
return if query.blank? || (query.id == record.id)
|
22
22
|
|
23
|
-
record.errors.add(:base, '
|
24
|
-
attributes.each { record.errors.add(_1, '
|
23
|
+
record.errors.add(:base, 'record not unique')
|
24
|
+
attributes.each { record.errors.add(_1, 'property combination not unique') }
|
25
25
|
end
|
26
26
|
end
|
27
27
|
end
|
@@ -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
|
+
```
|