rails_age 0.5.2 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +30 -10
  3. data/README.md +108 -57
  4. data/config/initializers/types.rb +20 -0
  5. data/lib/apache_age/edge.rb +5 -0
  6. data/lib/apache_age/entities/common_methods.rb +6 -0
  7. data/lib/apache_age/entities/edge.rb +4 -3
  8. data/lib/apache_age/entities/node.rb +53 -0
  9. data/lib/apache_age/entities/vertex.rb +47 -47
  10. data/lib/apache_age/node.rb +36 -0
  11. data/lib/apache_age/types/{age_type_generator.rb → factory.rb} +3 -3
  12. data/lib/apache_age/validators/expected_node_type.rb +17 -0
  13. data/lib/apache_age/validators/node_type_validator.rb +15 -0
  14. data/lib/apache_age/validators/unique_node.rb +32 -0
  15. data/lib/apache_age/validators/unique_vertex.rb +27 -27
  16. data/lib/apache_age/validators/vertex_type_validator.rb +14 -13
  17. data/lib/generators/apache_age/edge/USAGE +7 -7
  18. data/lib/generators/apache_age/edge/edge_generator.rb +1 -0
  19. data/lib/generators/apache_age/edge/templates/edge.rb.tt +3 -3
  20. data/lib/generators/apache_age/generator_entity_helpers.rb +6 -11
  21. data/lib/generators/apache_age/node/USAGE +4 -4
  22. data/lib/generators/apache_age/node/node_generator.rb +1 -1
  23. data/lib/generators/apache_age/node/templates/node.rb.tt +2 -2
  24. data/lib/generators/apache_age/scaffold_edge/USAGE +16 -0
  25. data/lib/generators/apache_age/scaffold_edge/scaffold_edge_generator.rb +66 -0
  26. data/lib/generators/apache_age/scaffold_edge/templates/controller.rb.tt +50 -0
  27. data/lib/generators/apache_age/{scaffold_node/templates/views/_form.html.erb copy.tt → scaffold_edge/templates/views/_form.html.erb.tt} +10 -0
  28. data/lib/generators/apache_age/{scaffold_node/templates/views/index.html.erb copy.tt → scaffold_edge/templates/views/index.html.erb.tt} +1 -1
  29. data/lib/generators/apache_age/{scaffold_node/templates/views/partial.html.erb copy.tt → scaffold_edge/templates/views/partial.html.erb.tt} +11 -2
  30. data/lib/generators/apache_age/{scaffold_node/templates/views/show.html.erb copy.tt → scaffold_edge/templates/views/show.html.erb.tt} +1 -1
  31. data/lib/generators/apache_age/scaffold_node/scaffold_node_generator.rb +20 -22
  32. data/lib/rails_age/version.rb +1 -1
  33. data/lib/rails_age.rb +7 -4
  34. data/lib/tasks/config_types.rake +8 -8
  35. metadata +48 -10
  36. /data/lib/generators/apache_age/{scaffold_node/templates/views/edit.html.erb copy.tt → scaffold_edge/templates/views/edit.html.erb.tt} +0 -0
  37. /data/lib/generators/apache_age/{scaffold_node/templates/views/new.html.erb copy.tt → scaffold_edge/templates/views/new.html.erb.tt} +0 -0
  38. /data/lib/generators/apache_age/scaffold_node/templates/{node_controller.rb.tt → controller.rb.tt} +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d54712ca67a303ffa92d78165d30a500b1f695be6176b3198e7ff3f9f60ab4f9
4
- data.tar.gz: 8f8f999c42777dc432bf543b6256852d10a0fbcbcd4fe33ad06817277a47bb5d
3
+ metadata.gz: 6777870b945c8684ea2ef5d10b8e7411259e002001b074754840a9422b594cee
4
+ data.tar.gz: '008ab62bbe28d408d390acd5e00cecc764507e21ae3d38ca94d219e9d299435f'
5
5
  SHA512:
6
- metadata.gz: 75c804a5b29705a18344ea70d9a799ea3578d90055b056ba48b70984132f2fbd04bdd906ac744e2a587fe095e9a8e112d773ecc84cf6325be192c212d769090d
7
- data.tar.gz: 932a6c5843243a23ef48b69e87f0fef940030c9e47ce652be141bc6efb45a8cbde04c191853b3d15e754e3ee71d7033b301ab6432982c1fc357250fceb892f02
6
+ metadata.gz: 8a9c122ce1a8b5b173fc9ffd6ba45a4bf97965eb0681ec0d44e9779dcc1ef647da8e7e5110a5564a9f28c329ebe9e83ba832d920f4504bb5026650f41cc102d2
7
+ data.tar.gz: f9217b16e0177746a438ab5fed8a4e5c7f4bb6ff02b3e31a2a4d1db7a8bd246550e33be6494e0533c70dcbcde3a54078ed041b2c3a08718cb9b020c73353eee5
data/CHANGELOG.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Change Log
2
2
 
3
- ## VERSION 0.6.2 - 2024-xx-xx
3
+ ## VERSION 0.6.4 - 2024-xx-xx
4
4
 
5
5
  - **cypher queries** (like active record queries)
6
6
  * schema override
@@ -8,11 +8,11 @@
8
8
  * paths support
9
9
  * select attributes support
10
10
 
11
- ## VERSION 0.6.1 - 2024-xx-xx
11
+ ## VERSION 0.6.3 - 2024-xx-xx
12
12
 
13
13
  - **Age Path**
14
14
 
15
- ## VERSION 0.6.0 - 2024-xx-xx
15
+ ## VERSION 0.6.2 - 2024-xx-xx
16
16
 
17
17
  breaking change?: namespaces (by default) will use their own schema? (add to database.yml & schema.rb ?)
18
18
 
@@ -20,24 +20,44 @@ 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.3 - 2024-xx-xx
24
-
25
- - **Edge Scaffold** (generates edge, type, view and controller)
26
- * add `rails generate apache_age:edge_scaffold HasJob employee_role start_node:person end_node:company`
23
+ ## VERSION 0.6.1 - 2024-xx-xx
27
24
 
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
 
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.6.0 - 2024-06-xx
37
+
38
+ **breaking changes**: update naming
39
+ * renamed `Entities::Vertex` module to `Entities::Node`
40
+ * renamed `UniqueVertex` to `UniqueNode`
41
+ * rebamed `AgeTypeGenerator` to `Type::Factory`
42
+ * move `lib/generators/*` intp `lib/apache_age/generators`
43
+
44
+ here is the [commit](https://github.com/marpori/rails_age_demo_app/commit/a6f0708f2bbc165eddbafe63896068a72d803b17) to see the changes te demo app to make it work for release 0.6.0
45
+
46
+ ## VERSION 0.5.3 - 2024-06-23
47
+
48
+ - **Edge Scaffold** (generates edge, type, view and controller) - without start-/end-nodes types!?
49
+ * add `rails generate apache_age:edge_scaffold HasJob employee_role`
50
+ * add system test (to dummy app after scaffold_node is run)
51
+
52
+ - **Node Scaffold** (generates node, type, view and controller)
53
+ * add system test (to dummy app after scaffold_node is run)
54
+
35
55
  ## VERSION 0.5.2 - 2024-06-16
36
56
 
37
57
  - **Node Scaffold** (generates node, type, view and controller)
38
58
  * add `rails generate apache_age:node_scaffold Person first_name last_name age:integer`
39
59
 
40
- ## VERSION 0.5.1 - 2024-06-16
60
+ ## VERSION 0.5.1 - 2024-06-16 (yanked)
41
61
 
42
62
  **yanked** (2024-06-16) - had an issue with the generator
43
63
 
@@ -50,7 +70,7 @@ breaking change?: namespaces (by default) will use their own schema? (add to dat
50
70
 
51
71
  - **Edge Generator**
52
72
  * add `rails generate apache_age:edge HasPet owner_role`
53
- 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)
73
+ 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)
54
74
 
55
75
  ## VERSION 0.4.1 - 2024-06-15
56
76
 
data/README.md CHANGED
@@ -1,25 +1,76 @@
1
1
  # RailsAge
2
2
 
3
- Simplify Apache Age usage within a Rails application.
3
+ Apache Age integration within a Rails application.
4
4
 
5
- ## Installation
5
+ ## Quick Start - Essentials
6
6
 
7
7
  **NOTE:** you must be using Postgres as your database! Apache Age requires it.
8
8
 
9
- Add this line to your application's Gemfile:
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` - this is not yet supported by the generator, but easy to do manually as shown below. (The problem is that I havent been able to figure out how load all the rails types in the testing environment).
36
+
37
+ ie:
11
38
  ```ruby
12
- gem "rails_age"
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
45
+ attribute :end_node, :company
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
- ## Quick Start
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
- 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:
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
- now you should be able to create the rails database:
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 "Add Apache Age Postgres DB configured with Rails App"
108
+ git commit -m "Basic Rails Configuration"
51
109
  ```
52
110
 
53
- install Apache Age (you can ignore the `unknown OID` warnings)
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
- make some nodes :string is the default type
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:node Company company_name
68
- rails generate apache_age:node Person first_name last_name
69
- rails generate apache_age:node Pet pet_name:string age:integer
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
- make some edges (`:vertex` is the default type) for start_node and end_node
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
- **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.**
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
- ### Rails Console Usage
160
+ ```bash
161
+ # without a namespace
162
+ rails generate apache_age:scaffold_edge HasPet caretaker_role
86
163
 
87
- ```ruby
88
- bin/rails c
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/factory'
4
+ # USAGE (with edges or nodes) - ie:
5
+ # require_dependency 'company'
6
+ # ActiveModel::Type.register(
7
+ # :company, ApacheAge::Types::Factory.type_for(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::Factory.type_for(ApacheAge::Node)
15
+ )
16
+ require_dependency 'apache_age/edge'
17
+ ActiveModel::Type.register(
18
+ :edge, ApacheAge::Types::Factory.type_for(ApacheAge::Edge)
19
+ )
20
+ end
@@ -0,0 +1,5 @@
1
+ module ApacheAge
2
+ class Edge
3
+ include ApacheAge::Entities::Edge
4
+ end
5
+ 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
- # type: `:vertex` can be overriden with a specific node type
15
- attribute :end_node, :vertex
16
- attribute :start_node, :vertex
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 Node
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
@@ -1,53 +1,53 @@
1
- module ApacheAge
2
- module Entities
3
- module Vertex
4
- extend ActiveSupport::Concern
1
+ # module ApacheAge
2
+ # module Entities
3
+ # module Vertex
4
+ # extend ActiveSupport::Concern
5
5
 
6
- included do
7
- include ActiveModel::Model
8
- include ActiveModel::Dirty
9
- include ActiveModel::Attributes
6
+ # included do
7
+ # include ActiveModel::Model
8
+ # include ActiveModel::Dirty
9
+ # include ActiveModel::Attributes
10
10
 
11
- attribute :id, :integer
11
+ # attribute :id, :integer
12
12
 
13
- extend ApacheAge::Entities::ClassMethods
14
- include ApacheAge::Entities::CommonMethods
15
- end
13
+ # extend ApacheAge::Entities::ClassMethods
14
+ # include ApacheAge::Entities::CommonMethods
15
+ # end
16
16
 
17
- def age_type = 'vertex'
17
+ # def age_type = 'vertex'
18
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
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
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
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
@@ -1,4 +1,4 @@
1
- # lib/apache_age/types/age_type_generator.rb
1
+ # lib/apache_age/types/factory.rb
2
2
  # Automatically generates ActiveModel::Type classes
3
3
  # Dynamically builds this (as a concrete example):
4
4
  # module ApacheAge
@@ -22,8 +22,8 @@
22
22
  # end
23
23
  module ApacheAge
24
24
  module Types
25
- class AgeTypeGenerator
26
- def self.create_type_for(klass)
25
+ class Factory
26
+ def self.type_for(klass)
27
27
  Class.new(ActiveModel::Type::Value) do
28
28
  define_method(:cast) do |value|
29
29
  case value
@@ -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,15 @@
1
+ module ApacheAge
2
+ module NodeTypeValidator
3
+ def
4
+ # Register the AGE typesvertex_attribute(attribute_name, type_symbol, klass)
5
+ attribute attribute_name, type_symbol
6
+
7
+ validate do
8
+ value = send(attribute_name)
9
+ unless value.is_a?(klass)
10
+ errors.add(attribute_name, "must be a #{klass.name}")
11
+ end
12
+ end
13
+ end
14
+ end
15
+ 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