rails_age 0.1.0 → 0.3.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.
@@ -0,0 +1,69 @@
1
+ module ApacheAge
2
+ module Entities
3
+ class Entity
4
+ class << self
5
+ def find_by(attributes)
6
+ where_clause = attributes.map { |k, v| "find.#{k} = '#{v}'" }.join(' AND ')
7
+ handle_find(where_clause)
8
+ end
9
+
10
+ def find(id)
11
+ where_clause = "id(find) = #{id}"
12
+ handle_find(where_clause)
13
+ end
14
+
15
+ private
16
+
17
+ def age_graph = 'age_schema'
18
+
19
+ def handle_find(where_clause)
20
+ # try to find a vertex
21
+ match_node = '(find)'
22
+ cypher_sql = find_sql(match_node, where_clause)
23
+ age_response = execute_find(cypher_sql)
24
+
25
+ if age_response.nil?
26
+ # if not a vertex try to find an edge
27
+ match_edge = '()-[find]->()'
28
+ cypher_sql = find_sql(match_edge, where_clause)
29
+ age_response = execute_find(cypher_sql)
30
+ return nil if age_response.nil?
31
+ end
32
+
33
+ instantiate_result(age_response)
34
+ end
35
+
36
+ def execute_find(cypher_sql)
37
+ age_result = ActiveRecord::Base.connection.execute(cypher_sql)
38
+ return nil if age_result.values.first.nil?
39
+
40
+ age_result
41
+ end
42
+
43
+ def instantiate_result(age_response)
44
+ age_type = age_response.values.first.first.split('::').last
45
+ json_string = age_response.values.first.first.split('::').first
46
+ json_data = JSON.parse(json_string)
47
+
48
+ age_label = json_data['label']
49
+ attribs = json_data.except('label', 'properties')
50
+ .merge(json_data['properties'])
51
+ .symbolize_keys
52
+
53
+ "#{json_data['label'].gsub('__', '::')}".constantize.new(**attribs)
54
+ end
55
+
56
+ def find_sql(match_clause, where_clause)
57
+ <<-SQL
58
+ SELECT *
59
+ FROM cypher('#{age_graph}', $$
60
+ MATCH #{match_clause}
61
+ WHERE #{where_clause}
62
+ RETURN find
63
+ $$) as (found agtype);
64
+ SQL
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
@@ -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,46 @@
1
+ # lib/apache_age/types/age_type_generator.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 AgeTypeGenerator
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,53 @@
1
+ # Usage (within an Age Model)
2
+ # validates_with UniqueEdgeValidator, attributes: [:employee_role, :start_node, :end_node]
3
+ # validates_with UniqueEdgeValidator, attributes: [:start_id, :employee_role, :end_id]
4
+ #
5
+ module ApacheAge
6
+ module Validators
7
+ class UniqueEdgeValidator < ActiveModel::Validator
8
+ def validate(record)
9
+ allowed_keys = record.age_properties.keys
10
+ attributes = options[:attributes] || []
11
+
12
+ edge_attribs =
13
+ attributes
14
+ .map { |attr| [attr, record.send(attr)] }.to_h
15
+ .symbolize_keys
16
+ .slice(*allowed_keys)
17
+
18
+ possible_end_keys = [:end_id, 'end_id', :end_node, 'end_node']
19
+ end_query =
20
+ if possible_end_keys.any? { |key| attributes.include?(key) }
21
+ end_query = query_node(record.end_node)
22
+ edge_attribs[:end_id] = end_query&.id
23
+ end_query
24
+ end
25
+
26
+ possible_start_keys = [:start_id, 'start_id', :start_node, 'start_node']
27
+ start_query =
28
+ if possible_start_keys.any? { |key| attributes.include?(key) }
29
+ start_query = query_node(record.start_node)
30
+ edge_attribs[:start_id] = start_query&.id
31
+ start_query
32
+ end
33
+ return if attributes.blank? && (end_query.blank? || start_query.blank?)
34
+
35
+ query = record.class.find_by(edge_attribs.compact)
36
+ return if query.blank? || (query.id == record.id)
37
+
38
+ record.errors.add(:base, 'attribute combination not unique')
39
+ record.errors.add(:end_node, 'attribute combination not unique')
40
+ record.errors.add(:start_node, 'attribute combination not unique')
41
+ attributes.each { record.errors.add(_1, 'attribute combination not unique') }
42
+ end
43
+
44
+ private
45
+
46
+ def query_node(node)
47
+ return nil if node.blank?
48
+
49
+ node.persisted? ? node.class.find(node.id) : node.class.find_by(node.age_properties)
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,28 @@
1
+ # Usage (within an Age Model)
2
+ # validates_with UniqueVertexValidator, attributes: [:first_name, :last_name, :gender]
3
+
4
+ # lib/apache_age/validators/unique_vertex_validator.rb
5
+ module ApacheAge
6
+ module Validators
7
+ class UniqueVertexValidator < ActiveModel::Validator
8
+ def validate(record)
9
+ allowed_keys = record.age_properties.keys
10
+ attributes = options[:attributes]
11
+ return if attributes.blank?
12
+
13
+ record_attribs =
14
+ attributes
15
+ .map { |attr| [attr, record.send(attr)] }
16
+ .to_h.symbolize_keys
17
+ .slice(*allowed_keys)
18
+ query = record.class.find_by(record_attribs)
19
+
20
+ # if no match is found or if it finds itself, it's valid
21
+ return if query.blank? || (query.id == record.id)
22
+
23
+ record.errors.add(:base, 'attribute combination not unique')
24
+ attributes.each { record.errors.add(_1, 'attribute combination not unique') }
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,14 @@
1
+ module ApacheAge
2
+ module VertexTypeValidator
3
+ def vertex_attribute(attribute_name, type_symbol, klass)
4
+ attribute attribute_name, type_symbol
5
+
6
+ validate do
7
+ value = send(attribute_name)
8
+ unless value.is_a?(klass)
9
+ errors.add(attribute_name, "must be a #{klass.name}")
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -1,3 +1,3 @@
1
1
  module RailsAge
2
- VERSION = "0.1.0"
2
+ VERSION = '0.3.0'
3
3
  end
data/lib/rails_age.rb CHANGED
@@ -1,14 +1,17 @@
1
- require "rails_age/version"
2
- require "rails_age/engine"
1
+ require 'rails_age/version'
2
+ require 'rails_age/engine'
3
3
 
4
4
  module RailsAge
5
5
  # Your code goes here...
6
6
  end
7
7
 
8
8
  module ApacheAge
9
- require "apache_age/class_methods"
10
- require "apache_age/common_methods"
11
- require "apache_age/edge"
12
- require "apache_age/entity"
13
- require "apache_age/vertex"
9
+ require 'apache_age/entities/class_methods'
10
+ require 'apache_age/entities/common_methods'
11
+ require 'apache_age/entities/edge'
12
+ require 'apache_age/entities/entity'
13
+ require 'apache_age/entities/vertex'
14
+ require 'apache_age/types/age_type_generator'
15
+ require 'apache_age/validators/unique_edge_validator'
16
+ require 'apache_age/validators/unique_vertex_validator'
14
17
  end
@@ -0,0 +1,210 @@
1
+ # lib/tasks/install.rake
2
+ # Usage: `rake apache_age:install`
3
+ #
4
+ namespace :apache_age do
5
+ desc "Copy migrations from rails_age to application and update schema"
6
+ task :install => :environment do
7
+ source_schema = File.expand_path('../../../db/schema.rb', __FILE__)
8
+ destination_schema = File.expand_path("#{Rails.root}/db/schema.rb", __FILE__)
9
+
10
+ # ensure we have a schema file
11
+ run_migrations
12
+
13
+ # copy our migrations to the application
14
+ last_migration_version = copy_migrations
15
+
16
+ # check if the schema is blank (NEW) before running migrations!
17
+ is_schema_blank = blank_schema?(destination_schema)
18
+ puts "Schema is blank: #{is_schema_blank}"
19
+
20
+ # run our new migrations
21
+ run_migrations
22
+
23
+ # adjust the schema file (unfortunately rails mangles the schema file)
24
+ if is_schema_blank
25
+ puts "creating new schema..."
26
+ create_new_schema(last_migration_version, destination_schema, source_schema)
27
+ else
28
+ puts "updating existing schema..."
29
+ update_existing_schema(last_migration_version, destination_schema, source_schema)
30
+ end
31
+
32
+ update_database_yml
33
+ end
34
+
35
+ def copy_migrations
36
+ migration_version = nil
37
+
38
+ source = File.expand_path('../../../db/migrate', __FILE__)
39
+ destination = File.expand_path("#{Rails.root}/db/migrate", __FILE__)
40
+
41
+ FileUtils.mkdir_p(destination) unless File.exist?(destination)
42
+ existing_migrations =
43
+ Dir.glob("#{destination}/*.rb").map { |file| File.basename(file).sub(/^\d+/, '') }
44
+
45
+ Dir.glob("#{source}/*.rb").each do |file|
46
+ filename = File.basename(file)
47
+ test_name = filename.sub(/^\d+/, '')
48
+
49
+ if existing_migrations.include?(test_name)
50
+ puts "Skipping #{filename}, it already exists"
51
+ else
52
+ migration_version = Time.now.utc.strftime("%Y_%m_%d_%H%M%S")
53
+ file_version = migration_version.delete('_')
54
+ new_filename = filename.sub(/^\d+/, file_version)
55
+ destination_file = File.join(destination, new_filename)
56
+ FileUtils.cp(file, destination_file)
57
+ puts "Copied #{filename} to #{destination} as #{new_filename}"
58
+ end
59
+ end
60
+ migration_version
61
+ end
62
+
63
+ def blank_schema?(destination_schema)
64
+ return false unless File.exist?(destination_schema)
65
+
66
+ content = File.read(destination_schema)
67
+ content.include?('define(version: 0)') &&
68
+ content.include?('enable_extension "plpgsql"') &&
69
+ content.scan(/enable_extension/).size == 1
70
+ end
71
+
72
+ def run_migrations
73
+ puts "Running migrations..."
74
+ Rake::Task["db:migrate"].invoke
75
+ end
76
+
77
+ def extract_rails_version(destination_schema)
78
+ if File.exist?(destination_schema)
79
+ content = File.read(destination_schema)
80
+ version_match = content.match(/ActiveRecord::Schema\[(.*?)\]/)
81
+ return version_match[1] if version_match
82
+ else
83
+ full_version = Rails.version
84
+ primary_secondary_version = full_version.split('.')[0..1].join('.')
85
+ primary_secondary_version
86
+ end
87
+ end
88
+
89
+ def create_new_schema(last_migration_version, destination_schema, source_schema)
90
+ if File.exist?(source_schema) && File.exist?(destination_schema)
91
+ rails_version = extract_rails_version(destination_schema)
92
+ source_content = File.read(source_schema)
93
+
94
+ # ensure we use the Rails version from the destination schema
95
+ source_content.gsub!(
96
+ /ActiveRecord::Schema\[\d+\.\d+\]/,
97
+ "ActiveRecord::Schema[#{rails_version}]"
98
+ )
99
+ # ensure we use the last migration version (not the source schema version)
100
+ source_content.gsub!(
101
+ /define\(version: \d{4}(?:_\d{2}){2}(?:_\d{6})?\) do/,
102
+ "define(version: #{last_migration_version}) do"
103
+ )
104
+
105
+ File.write(destination_schema, source_content)
106
+ puts "Created new schema in #{destination_schema} with necessary extensions and configurations."
107
+ else
108
+ puts "local db/schema.rb file not found."
109
+ end
110
+ end
111
+
112
+ def update_existing_schema(last_migration_version, destination_schema, source_schema)
113
+ if File.exist?(source_schema) && File.exist?(destination_schema)
114
+ rails_version = extract_rails_version(destination_schema)
115
+ source_content = File.read(source_schema)
116
+ new_content =
117
+ source_content.gsub(
118
+ /.*ActiveRecord::Schema\[\d+\.\d+\]\.define\(version: \d{4}(?:_\d{2}){2}(?:_\d{6})?\) do\n|\nend$/,
119
+ ''
120
+ )
121
+
122
+ destination_content = File.read(destination_schema)
123
+
124
+ # Remove unwanted schema statements
125
+ destination_content.gsub!(%r{^.*?# These are extensions that must be enabled in order to support this database.*?\n}, '')
126
+
127
+ destination_content.gsub!(%r{^.*?create_schema "ag_catalog".*?\n}, '')
128
+ destination_content.gsub!(%r{^.*?create_schema "age_schema".*?\n}, '')
129
+ destination_content.gsub!(%r{^.*?enable_extension "age".*?\n}, '')
130
+ destination_content.gsub!(%r{^.*?enable_extension "plpgsql".*?\n}, '')
131
+ destination_content.gsub!(%r{^.*?# Could not dump table "ag_graph" because of following StandardError.*?\n}, '')
132
+ destination_content.gsub!(%r{^.*?# Unknown type 'regnamespace' for column 'namespace'.*?\n}, '')
133
+ destination_content.gsub!(%r{^.*?# Could not dump table "ag_label" because of following StandardError.*?\n}, '')
134
+ destination_content.gsub!(%r{^.*?# Unknown type 'regclass' for column 'relation'.*?\n}, '')
135
+ destination_content.gsub!(%r{^.*?# Unknown type 'graphid' for column 'id'.*?\n}, '')
136
+ destination_content.gsub!(
137
+ %r{^.*?# Could not dump table "_ag_label_edge" because of following StandardError.*?\n}, ''
138
+ )
139
+ destination_content.gsub!(
140
+ %r{^.*?# Could not dump table "_ag_label_vertex" because of following StandardError.*?\n}, ''
141
+ )
142
+ destination_content.gsub!(%r{^.*?# Could not dump table "ag_graph" because of following StandardError.*?\n}, '')
143
+ destination_content.gsub!(%r{^.*?# Could not dump table "ag_label" because of following StandardError.*?\n}, '')
144
+ destination_content.gsub!(%r{^.*?add_foreign_key "ag_label", "ag_graph".*?\n}, '')
145
+
146
+ # add new wanted schema statements (at the top of the schema)
147
+ destination_content.sub!(
148
+ %r{(ActiveRecord::Schema\[\d+\.\d+\]\.define\(version: \d{4}(?:_\d{2}){2}(?:_\d{6})?\) do\n)},
149
+ "\\1#{new_content}\n"
150
+ )
151
+
152
+ existing_version = destination_content.match(/define\(version: (\d{4}(?:_\d{2}){2}(?:_\d{6})?)\)/)[1].gsub('_', '')
153
+ current_version = last_migration_version ? last_migration_version.gsub('_', '') : existing_version
154
+
155
+ # ensure we use the last migration version (not the source schema version)
156
+ if current_version.to_i > existing_version.to_i
157
+ destination_content.gsub!(
158
+ /define\(version: \d{4}(?:_\d{2}){2}(?:_\d{6})?\) do/,
159
+ "define(version: #{last_migration_version}) do"
160
+ )
161
+ end
162
+
163
+ File.write(destination_schema, destination_content)
164
+ puts "Updated #{destination_schema} with necessary extensions and configurations."
165
+ else
166
+ puts "local db/schema.rb file not found."
167
+ end
168
+ end
169
+
170
+ def update_database_yml
171
+ db_config_file = File.expand_path("#{Rails.root}/config/database.yml", __FILE__)
172
+
173
+ # Read the file
174
+ lines = File.readlines(db_config_file)
175
+
176
+ # any uncommented "schema_search_path:" lines?
177
+ path_index = lines.find_index { |line| !line.include?('#') && line.include?('schema_search_path:') }
178
+ default_start_index = lines.index { |line| line.strip.start_with?('default:') }
179
+
180
+ # when it finds an existing schema_search_path, it updates it
181
+ if path_index && lines[path_index].include?('ag_catalog,age_schema')
182
+ puts "schema_search_path already set to ag_catalog,age_schema nothing to do."
183
+ return
184
+ elsif path_index
185
+ key, val = lines[path_index].split(': ')
186
+ # remove any unwanted characters
187
+ val = val.gsub(/[ "\s\"\"'\n]/, '')
188
+ lines[path_index] = "#{key}: ag_catalog,age_schema,#{val}\n"
189
+ puts "add ag_catalog,age_schema to schema_search_path"
190
+ elsif default_start_index
191
+ puts "add ag_catalog,age_schema,public to schema_search_path in the default section of database.yml"
192
+ sections_index = lines.map.with_index { |line, index| index if !line.start_with?(' ') }.compact.sort
193
+
194
+ # find the start of the default section
195
+ next_section_in_list = sections_index.index(default_start_index) + 1
196
+
197
+ # find the end of the default section (before the next section starts)
198
+ path_insert_index = sections_index[next_section_in_list]
199
+
200
+ lines.insert(path_insert_index, " schema_search_path: ag_catalog,age_schema,public\n")
201
+ else
202
+ puts "didn't find a default section in database.yml, please add the following line:"
203
+ puts " schema_search_path: ag_catalog,age_schema,public"
204
+ puts "to the apprpriate section of your database.yml"
205
+ end
206
+
207
+ # Write the modified lines back to the file
208
+ File.open(db_config_file, 'w') { |file| file.write(lines.join) }
209
+ end
210
+ 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.1.0
4
+ version: 0.3.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-05-21 00:00:00.000000000 Z
11
+ date: 2024-05-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -16,29 +16,36 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: 7.1.3.2
19
+ version: '7.0'
20
+ - - "<"
21
+ - !ruby/object:Gem::Version
22
+ version: '9.0'
20
23
  type: :runtime
21
24
  prerelease: false
22
25
  version_requirements: !ruby/object:Gem::Requirement
23
26
  requirements:
24
27
  - - ">="
25
28
  - !ruby/object:Gem::Version
26
- version: 7.1.3.2
29
+ version: '7.0'
30
+ - - "<"
31
+ - !ruby/object:Gem::Version
32
+ version: '9.0'
27
33
  - !ruby/object:Gem::Dependency
28
34
  name: rspec-rails
29
35
  requirement: !ruby/object:Gem::Requirement
30
36
  requirements:
31
- - - ">="
37
+ - - "~>"
32
38
  - !ruby/object:Gem::Version
33
- version: '0'
39
+ version: '6.0'
34
40
  type: :development
35
41
  prerelease: false
36
42
  version_requirements: !ruby/object:Gem::Requirement
37
43
  requirements:
38
- - - ">="
44
+ - - "~>"
39
45
  - !ruby/object:Gem::Version
40
- version: '0'
41
- description: Apache AGE plugin for Rails 7.1
46
+ version: '6.0'
47
+ description: This plugin integrates Apache AGE for PostgreSQL with Rails 7.x, providing
48
+ tools and helpers for working with graph databases within a Rails application.
42
49
  email:
43
50
  - btihen@gmail.com
44
51
  executables: []
@@ -58,23 +65,28 @@ files:
58
65
  - app/models/rails_age/application_record.rb
59
66
  - app/views/layouts/rails_age/application.html.erb
60
67
  - config/routes.rb
61
- - db/migrate/20240521062349_configure_apache_age.rb
68
+ - db/migrate/20240521062349_add_apache_age.rb
62
69
  - db/schema.rb
63
- - lib/apache_age/class_methods.rb
64
- - lib/apache_age/common_methods.rb
65
- - lib/apache_age/edge.rb
66
- - lib/apache_age/entity.rb
67
- - lib/apache_age/vertex.rb
70
+ - lib/apache_age/entities/class_methods.rb
71
+ - lib/apache_age/entities/common_methods.rb
72
+ - lib/apache_age/entities/edge.rb
73
+ - lib/apache_age/entities/entity.rb
74
+ - lib/apache_age/entities/vertex.rb
75
+ - lib/apache_age/types/age_type_generator.rb
76
+ - lib/apache_age/validators/unique_edge_validator.rb
77
+ - lib/apache_age/validators/unique_vertex_validator.rb
78
+ - lib/apache_age/validators/vertex_type_validator.rb
68
79
  - lib/rails_age.rb
69
80
  - lib/rails_age/engine.rb
70
81
  - lib/rails_age/version.rb
82
+ - lib/tasks/install.rake
71
83
  - lib/tasks/rails_age_tasks.rake
72
84
  homepage: https://github.com/marpori/rails_age
73
85
  licenses:
74
86
  - MIT
75
87
  metadata:
76
88
  homepage_uri: https://github.com/marpori/rails_age
77
- source_code_uri: https://github.com/marpori/rails_age
89
+ source_code_uri: https://github.com/marpori/rails_age/blob/main
78
90
  changelog_uri: https://github.com/marpori/rails_age/blob/main/CHANGELOG.md
79
91
  post_install_message:
80
92
  rdoc_options: []
@@ -84,15 +96,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
84
96
  requirements:
85
97
  - - ">="
86
98
  - !ruby/object:Gem::Version
87
- version: '0'
99
+ version: '3.0'
88
100
  required_rubygems_version: !ruby/object:Gem::Requirement
89
101
  requirements:
90
102
  - - ">="
91
103
  - !ruby/object:Gem::Version
92
104
  version: '0'
93
105
  requirements: []
94
- rubygems_version: 3.5.9
106
+ rubygems_version: 3.5.10
95
107
  signing_key:
96
108
  specification_version: 4
97
- summary: Apache AGE plugin for Rails 7.1
109
+ summary: Apache AGE plugin for Rails 7.x
98
110
  test_files: []