tarantool-rails 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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 824fd7e958e18a4576a55a510ae6713557bb7d0e6a36ce202f18d72785eb18ef
4
- data.tar.gz: e33adb1ed56190a0d11f32c1994a71f2e247c2548e30bb594344b581794f03ba
3
+ metadata.gz: 30e777d2be6062e333e01fa5ab1e50fc2ccab417e19ae4513dddf8a5b8f2bd8f
4
+ data.tar.gz: 72c8fdfd334c5d3da9b62313e90e92650e7b045596d57409aff7a02e2dff8c85
5
5
  SHA512:
6
- metadata.gz: cd86cbe614985b58a745dfa2807df813e8b1eb95f7ea0a044f26f0a539577c15dac3cbeffa33bcec5774c31583a9a324a7706fbbb72ba16392a4c7e46088bd2f
7
- data.tar.gz: 5afa1af094609b0ed6f6637b7a1aa08dbe6afcf8b9ae3cda57031ad0f4a3565bb23ff204e37c5e9f779366cc916fdcb83025f8dec3de43501eda8b9ae5f90451
6
+ metadata.gz: 10cd6a546bb3298f10b39a44a3fec2f79189c300e9152e0a1eaad79f9350e868b0d84caae77e77e222f2f03cc989070674cbde5b61c45a023b70cf1679636f72
7
+ data.tar.gz: 38f7446ec5667c71a8717e84bf4b77b2763288d8f011be423867796a982e85844651ad10296961644e8a0df7716c443b09aba827da452aa652110a1b77bed59e
data/CHANGELOG.md CHANGED
@@ -1,5 +1,20 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.3.0 (2026-10-18)
4
+
5
+ - `config.tarantool.instrumenter` replaces `ActiveSupport::Notifications` as the pool instrumenter, for chaining
6
+ tarantool-opentelemetry.
7
+ - Depends on `tarantool-client ~> 0.3` and `tarantool-schema ~> 0.3`; `tarantool:schema:apply` grants what the schema declares.
8
+ - The schema rake tasks no longer define helper methods and a constant on `Object`.
9
+
10
+ ## 0.2.0 (2026-10-17)
11
+
12
+ - `tarantool:schema:dump` writes the live schema to `config/tarantool_schema.rb` (`FORCE=1` to overwrite,
13
+ `dump[users,codes]` to limit the spaces).
14
+ - `plan`, `apply` and `check` take `alter` names as a task argument: `"tarantool:schema:apply[add_field]"`.
15
+ - `LogSubscriber` logs `shutdown.tarantool` (tarantool-client 0.3.0) at info.
16
+ - Depends on `tarantool-schema ~> 0.2`.
17
+
3
18
  ## 0.1.0 (2026-10-17)
4
19
 
5
20
  First release.
data/README.md CHANGED
@@ -67,6 +67,14 @@ can run; the first model operation then raises `Tarantool::Rails::Error`.
67
67
  password).
68
68
  - `Tarantool::Rails.pool` is one `Tarantool::Client::Pool` per process, built on first use with
69
69
  `instrumenter: ActiveSupport::Notifications`. After `fork` the pool reopens its connections on its own.
70
+ `config.tarantool.instrumenter` replaces the instrumenter; the log subscriber still needs the events on
71
+ `ActiveSupport::Notifications`, so chain it:
72
+
73
+ ```ruby
74
+ # config/initializers/tarantool.rb
75
+ Rails.application.config.tarantool.instrumenter =
76
+ Tarantool::OpenTelemetry::Instrumenter.new(instrumenter: ActiveSupport::Notifications)
77
+ ```
70
78
  - `Tarantool::Rails.storage` is a `Storage::Box` (or `Storage::Crud`) over the pool, and
71
79
  `Tarantool::Model.default_storage` is set so every model without a storage of its own uses it. Nothing
72
80
  connects until a model is used.
@@ -101,7 +109,7 @@ Completed 200 OK in 4ms (Views: 1.9ms | Tarantool: 0.5ms)
101
109
  Request lines are debug, colored like ActiveRecord's when `colorize_logging` is on. Keys, tuples and
102
110
  arguments never appear: the client does not put them in the event payload. A failed request shows the error
103
111
  class and code, not the message. Lifecycle events are logged too: `disconnect` at warn, `reconnect_failed` at
104
- error, `reconnect`, `fork` and `role_change` at info.
112
+ error, `reconnect`, `fork`, `role_change` and `shutdown` at info.
105
113
 
106
114
  ## Schema
107
115
 
@@ -118,10 +126,14 @@ end
118
126
  | Task | |
119
127
  |---|---|
120
128
  | `rails tarantool:schema:plan` | prints what `apply` would create; drift raises `MismatchError` with the diff |
121
- | `rails tarantool:schema:apply` | creates the missing spaces and indexes and prints them; never alters or drops |
129
+ | `rails tarantool:schema:apply` | creates the missing spaces, indexes and grants and prints them; never drops |
122
130
  | `rails tarantool:schema:check` | for CI: exits 1 and prints `pending: ...` when anything would be created, 0 when live equals declared |
131
+ | `rails tarantool:schema:dump` | writes the live schema to `config/tarantool_schema.rb` for bootstrapping; refuses to overwrite unless `FORCE=1`; `dump[users,codes]` limits the spaces |
123
132
 
124
- The tasks connect with `admin_uri`; see the tarantool-schema README for the privileges that user needs.
133
+ `plan`, `apply` and `check` take the tarantool-schema `alter:` names as an argument: `rails
134
+ "tarantool:schema:apply[add_field]"` appends nullable fields declared past the end of a live format. Without it
135
+ such a format is drift. The tasks connect with `admin_uri`; see the tarantool-schema README for the privileges
136
+ that user needs.
125
137
 
126
138
  ## Generators
127
139
 
@@ -197,6 +209,8 @@ gem must be in the Gemfile.
197
209
  `Tarantool::Rails::Configuration`, `Tarantool::Rails::LogSubscriber`, `Tarantool::Rails::Error`
198
210
  (descends from `Tarantool::Error`), the rake tasks and the generators.
199
211
 
212
+ RBS signatures for this API ship with the gem in `sig/`.
213
+
200
214
  Everything else, including anything tagged `@api private` in its source comment, is an implementation detail
201
215
  and may change in any release.
202
216
 
@@ -5,4 +5,5 @@ Tarantool::Rails.schema = Tarantool::Schema::Definition.new do
5
5
  # ensure_space :users,
6
6
  # format: [{ name: :id, type: :unsigned }, { name: :email, type: :string }],
7
7
  # indexes: [{ name: :primary, parts: [:id], sequence: true }, { name: :email, parts: [:email] }]
8
+ # grant :app, %i[read write], on: :users
8
9
  end
@@ -22,6 +22,8 @@ module Tarantool
22
22
  }.freeze
23
23
  ID = Field.new(name: 'id', attribute_type: ':integer', field_type: 'unsigned', nullable: false)
24
24
  SCHEMA_PATH = 'config/tarantool_schema.rb'
25
+ SCHEMA_END = /^end\s*\z/
26
+ LINE_START = /^/
25
27
 
26
28
  source_root File.expand_path('templates', __dir__)
27
29
 
@@ -40,7 +42,7 @@ module Tarantool
40
42
  def add_space_to_schema
41
43
  return if options[:skip_schema] || !File.exist?(File.join(destination_root, SCHEMA_PATH))
42
44
 
43
- inject_into_file SCHEMA_PATH, space_declaration, before: /^end\s*\z/
45
+ inject_into_file SCHEMA_PATH, space_declaration, before: SCHEMA_END
44
46
  end
45
47
 
46
48
  private
@@ -70,7 +72,7 @@ module Tarantool
70
72
  ' ],', ' indexes: [',
71
73
  " { name: :primary, parts: [:#{model_fields.first.name}]#{sequence} }", ' ]']
72
74
 
73
- "\n#{lines.join("\n").gsub(/^/, ' ')}\n"
75
+ "\n#{lines.join("\n").gsub(LINE_START, ' ')}\n"
74
76
  end
75
77
  end
76
78
  end
@@ -17,7 +17,7 @@ module Tarantool
17
17
  def filtered(key, value)
18
18
  case key
19
19
  when :client_options then value.key?(:password) ? value.merge(password: '[FILTERED]') : value
20
- when :admin_uri then value&.sub(%r{:[^:@/]*@}, ':[FILTERED]@')
20
+ when :admin_uri then value&.sub(Configuration::URI_PASSWORD, ':[FILTERED]@')
21
21
  else value
22
22
  end
23
23
  end
@@ -30,6 +30,7 @@ module Tarantool
30
30
  KEYS = (CLIENT_KEYS + %i[addresses address read write storage crud admin_uri]).freeze
31
31
  CRUD_KEYS = %i[primary_index read write timeout_margin].freeze
32
32
  STORAGES = %i[box crud].freeze
33
+ URI_PASSWORD = %r{:[^:@/]*@}
33
34
 
34
35
  class << self
35
36
  def from_hash(hash)
@@ -57,6 +57,10 @@ module Tarantool
57
57
  info "Tarantool #{event.payload[:address]} is now #{event.payload[:read_only] ? 'read-only' : 'writable'}"
58
58
  end
59
59
 
60
+ def shutdown(event)
61
+ info "Tarantool #{event.payload[:address]} is shutting down, #{event.payload[:pending]} requests in flight"
62
+ end
63
+
60
64
  private
61
65
 
62
66
  def request_name(payload)
@@ -10,6 +10,7 @@ module Tarantool
10
10
  # A missing config/tarantool.yml is not an error at boot: the install generator has to be able to run.
11
11
  initializer 'tarantool.configuration' do |app|
12
12
  Tarantool::Rails.schema_path = app.config.tarantool.schema_path || app.root.join('config/tarantool_schema.rb')
13
+ Tarantool::Rails.instrumenter = app.config.tarantool.instrumenter
13
14
  next unless app.root.join('config/tarantool.yml').exist?
14
15
 
15
16
  section = app.config_for(:tarantool)
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tarantool
4
+ module Rails
5
+ # Helpers behind the tarantool:schema rake tasks.
6
+ # @api private
7
+ module SchemaTasks
8
+ # Task arguments are one String: rails "tarantool:schema:apply[add_field]", "tarantool:schema:dump[users,codes]".
9
+ NAME_SEPARATOR = /[\s,]+/
10
+
11
+ module_function
12
+
13
+ def names(argument) = argument.to_s.split(NAME_SEPARATOR).reject(&:empty?)
14
+
15
+ def dump(path, only)
16
+ abort "#{path} exists; set FORCE=1 to overwrite" if File.exist?(path) && ENV['FORCE'].to_s.empty?
17
+
18
+ definition = Tarantool::Rails.admin_client do |client|
19
+ Tarantool::Schema::Definition.from_live(client, only: only.empty? ? nil : only)
20
+ end
21
+ File.write(path, "# frozen_string_literal: true\n\nTarantool::Rails.schema = #{definition.to_ruby}")
22
+ puts path
23
+ end
24
+ end
25
+ end
26
+ end
@@ -3,22 +3,31 @@
3
3
  namespace :tarantool do
4
4
  namespace :schema do
5
5
  desc 'Print what tarantool:schema:apply would create; fail on drift'
6
- task plan: :environment do
7
- Tarantool::Rails.admin_client { |client| puts Tarantool::Rails.schema.plan(client) }
6
+ task :plan, [:alter] => :environment do |_task, args|
7
+ alter = Tarantool::Rails::SchemaTasks.names(args[:alter]).map(&:to_sym)
8
+ Tarantool::Rails.admin_client { |client| puts Tarantool::Rails.schema.plan(client, alter:) }
8
9
  end
9
10
 
10
11
  desc 'Create missing Tarantool spaces and indexes; fail on drift'
11
- task apply: :environment do
12
- Tarantool::Rails.admin_client { |client| puts Tarantool::Rails.schema.apply(client) }
12
+ task :apply, [:alter] => :environment do |_task, args|
13
+ alter = Tarantool::Rails::SchemaTasks.names(args[:alter]).map(&:to_sym)
14
+ Tarantool::Rails.admin_client { |client| puts Tarantool::Rails.schema.apply(client, alter:) }
13
15
  end
14
16
 
15
17
  desc 'Fail unless the live schema equals the declaration, for CI'
16
- task check: :environment do
17
- pending = Tarantool::Rails.admin_client { |client| Tarantool::Rails.schema.plan(client) }
18
+ task :check, [:alter] => :environment do |_task, args|
19
+ alter = Tarantool::Rails::SchemaTasks.names(args[:alter]).map(&:to_sym)
20
+ pending = Tarantool::Rails.admin_client { |client| Tarantool::Rails.schema.plan(client, alter:) }
18
21
  next if pending.empty?
19
22
 
20
23
  pending.each { |line| puts "pending: #{line}" }
21
24
  exit 1
22
25
  end
26
+
27
+ desc 'Write the live schema to config/tarantool_schema.rb (FORCE=1 to overwrite; spaces as [users,codes])'
28
+ task :dump, [:only] => :environment do |_task, args|
29
+ Tarantool::Rails::SchemaTasks.dump(Tarantool::Rails.schema_path.to_s,
30
+ Tarantool::Rails::SchemaTasks.names(args[:only]))
31
+ end
23
32
  end
24
33
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Tarantool
4
4
  module Rails
5
- VERSION = '0.1.0'
5
+ VERSION = '0.3.0'
6
6
  end
7
7
  end
@@ -10,6 +10,7 @@ require_relative 'rails/errors'
10
10
  require_relative 'rails/configuration'
11
11
  require_relative 'rails/log_subscriber'
12
12
  require_relative 'rails/controller_runtime'
13
+ require_relative 'rails/schema_tasks'
13
14
  require_relative 'rails/railtie' if defined?(Rails::Railtie)
14
15
 
15
16
  module Tarantool
@@ -21,7 +22,13 @@ module Tarantool
21
22
 
22
23
  class << self
23
24
  attr_accessor :config, :schema_path
24
- attr_writer :schema
25
+ attr_writer :schema, :instrumenter
26
+
27
+ # What the pool reports requests to; the log subscriber listens to ActiveSupport::Notifications, so an
28
+ # instrumenter of your own should chain it (tarantool-opentelemetry's does with `instrumenter:`).
29
+ def instrumenter
30
+ @instrumenter || ActiveSupport::Notifications
31
+ end
25
32
 
26
33
  def pool
27
34
  LOCK.synchronize { @pool ||= build_pool }
@@ -69,8 +76,7 @@ module Tarantool
69
76
  end
70
77
 
71
78
  def build_pool
72
- Tarantool::Client::Pool.new(configured.addresses, **configured.client_options,
73
- instrumenter: ActiveSupport::Notifications)
79
+ Tarantool::Client::Pool.new(configured.addresses, **configured.client_options, instrumenter:)
74
80
  end
75
81
 
76
82
  def build_storage
@@ -0,0 +1,59 @@
1
+ module Tarantool
2
+ # Rails integration: configuration from config/tarantool.yml, one Pool per process, models on it, log lines,
3
+ # schema rake tasks and generators.
4
+ module Rails
5
+ VERSION: String
6
+
7
+ def self.config: () -> Configuration?
8
+ def self.config=: (Configuration? config) -> void
9
+ def self.schema_path: () -> (String | Pathname)?
10
+ def self.schema_path=: ((String | Pathname)? path) -> void
11
+ # Loaded from schema_path on first use; the file assigns it.
12
+ def self.schema: () -> Tarantool::Schema::Definition
13
+ def self.schema=: (Tarantool::Schema::Definition? schema) -> void
14
+ # ActiveSupport::Notifications unless configured.
15
+ def self.instrumenter: () -> Tarantool::Client::_Instrumenter
16
+ def self.instrumenter=: (Tarantool::Client::_Instrumenter? instrumenter) -> void
17
+ def self.pool: () -> Tarantool::Client::Pool
18
+ def self.storage: () -> (Tarantool::Model::Storage::Box | Tarantool::Model::Storage::Crud)
19
+ def self.admin_client: [T] () { (Tarantool::Client client) -> T } -> T
20
+ def self.use_default_storage!: () -> void
21
+ def self.disconnect!: () -> void
22
+
23
+ class Error < Tarantool::Error
24
+ end
25
+
26
+ # One environment section of config/tarantool.yml; inspect and to_s hide the password.
27
+ class Configuration < Data
28
+ attr_reader addresses: Array[String]
29
+ attr_reader client_options: Hash[Symbol, untyped]
30
+ attr_reader storage: :box | :crud
31
+ attr_reader read: Hash[Symbol, untyped]
32
+ attr_reader write: Hash[Symbol, untyped]
33
+ attr_reader crud: Hash[Symbol, untyped]
34
+ attr_reader admin_uri: String?
35
+
36
+ # The Hash config_for returns (an ActiveSupport::OrderedOptions is a Hash); unknown keys raise ArgumentError.
37
+ def self.from_hash: (Hash[Symbol | String, untyped] hash) -> Configuration
38
+ def inspect: () -> String
39
+ def to_s: () -> String
40
+ end
41
+
42
+ # ActiveRecord-style log lines for the client's events (an ActiveSupport::LogSubscriber); the time spent in
43
+ # requests is accumulated per thread for the controller log.
44
+ class LogSubscriber
45
+ def self.runtime: () -> Float
46
+ def self.runtime=: (Float value) -> void
47
+ def self.reset_runtime: () -> Float
48
+
49
+ # Each takes an ActiveSupport::Notifications::Event.
50
+ def request: (untyped event) -> void
51
+ def disconnect: (untyped event) -> void
52
+ def reconnect: (untyped event) -> void
53
+ def reconnect_failed: (untyped event) -> void
54
+ def fork: (untyped event) -> void
55
+ def role_change: (untyped event) -> void
56
+ def shutdown: (untyped event) -> void
57
+ end
58
+ end
59
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tarantool-rails
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
  - Starlexxx
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-10-17 00:00:00.000000000 Z
11
+ date: 2026-10-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: railties
@@ -36,14 +36,14 @@ dependencies:
36
36
  requirements:
37
37
  - - "~>"
38
38
  - !ruby/object:Gem::Version
39
- version: '0.2'
39
+ version: '0.3'
40
40
  type: :runtime
41
41
  prerelease: false
42
42
  version_requirements: !ruby/object:Gem::Requirement
43
43
  requirements:
44
44
  - - "~>"
45
45
  - !ruby/object:Gem::Version
46
- version: '0.2'
46
+ version: '0.3'
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: tarantool-model
49
49
  requirement: !ruby/object:Gem::Requirement
@@ -64,14 +64,14 @@ dependencies:
64
64
  requirements:
65
65
  - - "~>"
66
66
  - !ruby/object:Gem::Version
67
- version: '0.1'
67
+ version: '0.3'
68
68
  type: :runtime
69
69
  prerelease: false
70
70
  version_requirements: !ruby/object:Gem::Requirement
71
71
  requirements:
72
72
  - - "~>"
73
73
  - !ruby/object:Gem::Version
74
- version: '0.1'
74
+ version: '0.3'
75
75
  description: config/tarantool.yml per environment, one connection pool per process,
76
76
  models on it without an initializer, ActiveRecord-style log lines, schema rake tasks
77
77
  and generators.
@@ -95,8 +95,10 @@ files:
95
95
  - lib/tarantool/rails/errors.rb
96
96
  - lib/tarantool/rails/log_subscriber.rb
97
97
  - lib/tarantool/rails/railtie.rb
98
+ - lib/tarantool/rails/schema_tasks.rb
98
99
  - lib/tarantool/rails/tasks/schema.rake
99
100
  - lib/tarantool/rails/version.rb
101
+ - sig/tarantool/rails.rbs
100
102
  homepage: https://github.com/Starlexxx/tarantool-ruby
101
103
  licenses:
102
104
  - MIT