ruby_llm-agents 1.1.0 → 1.2.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.
@@ -2,262 +2,21 @@
2
2
 
3
3
  module RubyLLM
4
4
  module Agents
5
- # Database-backed budget configuration for multi-tenant environments
5
+ # @deprecated Use {Tenant} instead. This class will be removed in a future major version.
6
6
  #
7
- # Stores per-tenant budget limits that override the global configuration.
8
- # Supports runtime updates without application restarts.
9
- # Supports cost-based (USD), token-based, and execution-based limits.
7
+ # TenantBudget is now an alias to Tenant for backward compatibility.
8
+ # All functionality has been moved to the Tenant model with organized concerns.
10
9
  #
11
- # @!attribute [rw] tenant_id
12
- # @return [String] Unique identifier for the tenant
13
- # @!attribute [rw] name
14
- # @return [String, nil] Human-readable name for the tenant
15
- # @!attribute [rw] daily_limit
16
- # @return [BigDecimal, nil] Daily budget limit in USD
17
- # @!attribute [rw] monthly_limit
18
- # @return [BigDecimal, nil] Monthly budget limit in USD
19
- # @!attribute [rw] daily_token_limit
20
- # @return [Integer, nil] Daily token limit (across all models)
21
- # @!attribute [rw] monthly_token_limit
22
- # @return [Integer, nil] Monthly token limit (across all models)
23
- # @!attribute [rw] daily_execution_limit
24
- # @return [Integer, nil] Daily execution/call limit
25
- # @!attribute [rw] monthly_execution_limit
26
- # @return [Integer, nil] Monthly execution/call limit
27
- # @!attribute [rw] per_agent_daily
28
- # @return [Hash] Per-agent daily cost limits: { "AgentName" => limit }
29
- # @!attribute [rw] per_agent_monthly
30
- # @return [Hash] Per-agent monthly cost limits: { "AgentName" => limit }
31
- # @!attribute [rw] enforcement
32
- # @return [String] Enforcement mode: "none", "soft", or "hard"
33
- # @!attribute [rw] inherit_global_defaults
34
- # @return [Boolean] Whether to fall back to global config for unset limits
35
- # @!attribute [rw] tenant_record
36
- # @return [ActiveRecord::Base, nil] Polymorphic association to tenant model
10
+ # @example Migration path
11
+ # # Old usage (still works)
12
+ # TenantBudget.for_tenant("acme_corp")
13
+ # TenantBudget.create!(tenant_id: "acme", daily_limit: 100)
37
14
  #
38
- # @example Creating a tenant budget with cost, token, and execution limits
39
- # TenantBudget.create!(
40
- # tenant_id: "acme_corp",
41
- # name: "Acme Corporation",
42
- # daily_limit: 50.0, # USD
43
- # monthly_limit: 500.0, # USD
44
- # daily_token_limit: 1_000_000,
45
- # monthly_token_limit: 10_000_000,
46
- # daily_execution_limit: 500,
47
- # monthly_execution_limit: 10_000,
48
- # enforcement: "hard"
49
- # )
15
+ # # New usage (preferred)
16
+ # Tenant.for("acme_corp")
17
+ # Tenant.create!(tenant_id: "acme", daily_limit: 100)
50
18
  #
51
- # @example Fetching budget for a tenant object
52
- # budget = TenantBudget.for_tenant(organization)
53
- # budget.effective_daily_limit # => 50.0 (cost)
54
- # budget.effective_daily_token_limit # => 1_000_000 (tokens)
55
- # budget.effective_daily_execution_limit # => 500 (executions)
56
- #
57
- # @see RubyLLM::Agents::BudgetTracker
58
- # @see RubyLLM::Agents::LLMTenant
59
- # @api public
60
- class TenantBudget < ::ActiveRecord::Base
61
- self.table_name = "ruby_llm_agents_tenant_budgets"
62
-
63
- # Valid enforcement modes
64
- ENFORCEMENT_MODES = %w[none soft hard].freeze
65
-
66
- # Polymorphic association to the tenant model (e.g., Organization, Account)
67
- belongs_to :tenant_record, polymorphic: true, optional: true
68
-
69
- # Validations
70
- validates :tenant_id, presence: true, uniqueness: true
71
- validates :enforcement, inclusion: { in: ENFORCEMENT_MODES }, allow_nil: true
72
- validates :daily_limit, :monthly_limit,
73
- numericality: { greater_than_or_equal_to: 0 }, allow_nil: true
74
- validates :daily_token_limit, :monthly_token_limit,
75
- numericality: { greater_than_or_equal_to: 0, only_integer: true }, allow_nil: true
76
- validates :daily_execution_limit, :monthly_execution_limit,
77
- numericality: { greater_than_or_equal_to: 0, only_integer: true }, allow_nil: true
78
-
79
- # Finds a budget for the given tenant
80
- #
81
- # @param tenant [String, Object] The tenant identifier string or object with llm_tenant_id
82
- # @return [TenantBudget, nil] The budget record or nil if not found
83
- def self.for_tenant(tenant)
84
- return nil if tenant.blank?
85
-
86
- if tenant.respond_to?(:llm_tenant_id)
87
- # Object with llm_tenant DSL - try polymorphic first, then tenant_id
88
- find_by(tenant_record: tenant) || find_by(tenant_id: tenant.llm_tenant_id)
89
- else
90
- # String tenant_id
91
- find_by(tenant_id: tenant.to_s)
92
- end
93
- end
94
-
95
- # Finds or creates a budget for the given tenant
96
- #
97
- # @param tenant_id [String] The tenant identifier
98
- # @param name [String, nil] Optional human-readable name
99
- # @return [TenantBudget] The budget record
100
- def self.for_tenant!(tenant_id, name: nil)
101
- find_or_create_by!(tenant_id: tenant_id) do |budget|
102
- budget.name = name
103
- end
104
- end
105
-
106
- # Returns the display name (name or tenant_id fallback)
107
- #
108
- # @return [String] The name to display
109
- def display_name
110
- name.presence || tenant_id
111
- end
112
-
113
- # Returns the effective daily limit, considering inheritance
114
- #
115
- # @return [Float, nil] The daily limit or nil if not set
116
- def effective_daily_limit
117
- return daily_limit if daily_limit.present?
118
- return nil unless inherit_global_defaults
119
-
120
- global_config&.dig(:global_daily)
121
- end
122
-
123
- # Returns the effective monthly limit, considering inheritance
124
- #
125
- # @return [Float, nil] The monthly limit or nil if not set
126
- def effective_monthly_limit
127
- return monthly_limit if monthly_limit.present?
128
- return nil unless inherit_global_defaults
129
-
130
- global_config&.dig(:global_monthly)
131
- end
132
-
133
- # Returns the effective per-agent daily limit
134
- #
135
- # @param agent_type [String] The agent class name
136
- # @return [Float, nil] The limit or nil if not set
137
- def effective_per_agent_daily(agent_type)
138
- limit = per_agent_daily&.dig(agent_type)
139
- return limit if limit.present?
140
- return nil unless inherit_global_defaults
141
-
142
- global_config&.dig(:per_agent_daily, agent_type)
143
- end
144
-
145
- # Returns the effective per-agent monthly limit
146
- #
147
- # @param agent_type [String] The agent class name
148
- # @return [Float, nil] The limit or nil if not set
149
- def effective_per_agent_monthly(agent_type)
150
- limit = per_agent_monthly&.dig(agent_type)
151
- return limit if limit.present?
152
- return nil unless inherit_global_defaults
153
-
154
- global_config&.dig(:per_agent_monthly, agent_type)
155
- end
156
-
157
- # Returns the effective daily token limit, considering inheritance
158
- #
159
- # @return [Integer, nil] The daily token limit or nil if not set
160
- def effective_daily_token_limit
161
- return daily_token_limit if daily_token_limit.present?
162
- return nil unless inherit_global_defaults
163
-
164
- global_config&.dig(:global_daily_tokens)
165
- end
166
-
167
- # Returns the effective monthly token limit, considering inheritance
168
- #
169
- # @return [Integer, nil] The monthly token limit or nil if not set
170
- def effective_monthly_token_limit
171
- return monthly_token_limit if monthly_token_limit.present?
172
- return nil unless inherit_global_defaults
173
-
174
- global_config&.dig(:global_monthly_tokens)
175
- end
176
-
177
- # Returns the effective daily execution limit, considering inheritance
178
- #
179
- # @return [Integer, nil] The daily execution limit or nil if not set
180
- def effective_daily_execution_limit
181
- return daily_execution_limit if daily_execution_limit.present?
182
- return nil unless inherit_global_defaults
183
-
184
- global_config&.dig(:global_daily_executions)
185
- end
186
-
187
- # Returns the effective monthly execution limit, considering inheritance
188
- #
189
- # @return [Integer, nil] The monthly execution limit or nil if not set
190
- def effective_monthly_execution_limit
191
- return monthly_execution_limit if monthly_execution_limit.present?
192
- return nil unless inherit_global_defaults
193
-
194
- global_config&.dig(:global_monthly_executions)
195
- end
196
-
197
- # Returns the effective enforcement mode
198
- #
199
- # @return [Symbol] :none, :soft, or :hard
200
- def effective_enforcement
201
- return enforcement.to_sym if enforcement.present?
202
- return :soft unless inherit_global_defaults
203
-
204
- RubyLLM::Agents.configuration.budget_enforcement
205
- end
206
-
207
- # Checks if budget enforcement is enabled for this tenant
208
- #
209
- # @return [Boolean] true if enforcement is :soft or :hard
210
- def budgets_enabled?
211
- effective_enforcement != :none
212
- end
213
-
214
- # Returns a hash suitable for BudgetTracker
215
- #
216
- # @return [Hash] Budget configuration hash
217
- def to_budget_config
218
- {
219
- enabled: budgets_enabled?,
220
- enforcement: effective_enforcement,
221
- # Cost limits
222
- global_daily: effective_daily_limit,
223
- global_monthly: effective_monthly_limit,
224
- per_agent_daily: merged_per_agent_daily,
225
- per_agent_monthly: merged_per_agent_monthly,
226
- # Token limits
227
- global_daily_tokens: effective_daily_token_limit,
228
- global_monthly_tokens: effective_monthly_token_limit,
229
- # Execution limits
230
- global_daily_executions: effective_daily_execution_limit,
231
- global_monthly_executions: effective_monthly_execution_limit
232
- }
233
- end
234
-
235
- private
236
-
237
- # Returns the global budgets configuration
238
- #
239
- # @return [Hash, nil] Global budget config
240
- def global_config
241
- RubyLLM::Agents.configuration.budgets
242
- end
243
-
244
- # Merges per-agent daily limits with global defaults
245
- #
246
- # @return [Hash] Merged per-agent daily limits
247
- def merged_per_agent_daily
248
- return per_agent_daily || {} unless inherit_global_defaults
249
-
250
- (global_config&.dig(:per_agent_daily) || {}).merge(per_agent_daily || {})
251
- end
252
-
253
- # Merges per-agent monthly limits with global defaults
254
- #
255
- # @return [Hash] Merged per-agent monthly limits
256
- def merged_per_agent_monthly
257
- return per_agent_monthly || {} unless inherit_global_defaults
258
-
259
- (global_config&.dig(:per_agent_monthly) || {}).merge(per_agent_monthly || {})
260
- end
261
- end
19
+ # @see Tenant
20
+ TenantBudget = Tenant
262
21
  end
263
22
  end
@@ -10,8 +10,11 @@ module RubyLlmAgents
10
10
  # rails generate ruby_llm_agents:multi_tenancy
11
11
  #
12
12
  # This will create migrations for:
13
- # - ruby_llm_agents_tenant_budgets table for per-tenant budget configuration
14
- # - Adding tenant_id column to ruby_llm_agents_executions
13
+ # - ruby_llm_agents_tenants table for per-tenant configuration
14
+ # - Adding tenant columns to ruby_llm_agents_executions
15
+ #
16
+ # For users upgrading from an older version:
17
+ # - Renames ruby_llm_agents_tenant_budgets to ruby_llm_agents_tenants
15
18
  #
16
19
  class MultiTenancyGenerator < ::Rails::Generators::Base
17
20
  include ::ActiveRecord::Generators::Migration
@@ -20,21 +23,31 @@ module RubyLlmAgents
20
23
 
21
24
  desc "Adds multi-tenancy support to RubyLLM::Agents"
22
25
 
23
- def create_tenant_budgets_migration
24
- if table_exists?(:ruby_llm_agents_tenant_budgets)
25
- say_status :skip, "ruby_llm_agents_tenant_budgets table already exists", :yellow
26
+ def create_tenants_migration
27
+ if table_exists?(:ruby_llm_agents_tenants)
28
+ say_status :skip, "ruby_llm_agents_tenants table already exists", :yellow
26
29
  return
27
30
  end
28
31
 
29
- migration_template(
30
- "create_tenant_budgets_migration.rb.tt",
31
- File.join(db_migrate_path, "create_ruby_llm_agents_tenant_budgets.rb")
32
- )
32
+ if table_exists?(:ruby_llm_agents_tenant_budgets)
33
+ # Upgrade path: rename existing table
34
+ say_status :upgrade, "Renaming tenant_budgets to tenants", :blue
35
+ migration_template(
36
+ "rename_tenant_budgets_to_tenants_migration.rb.tt",
37
+ File.join(db_migrate_path, "rename_tenant_budgets_to_tenants.rb")
38
+ )
39
+ else
40
+ # Fresh install: create new table
41
+ migration_template(
42
+ "create_tenants_migration.rb.tt",
43
+ File.join(db_migrate_path, "create_ruby_llm_agents_tenants.rb")
44
+ )
45
+ end
33
46
  end
34
47
 
35
48
  def create_add_tenant_to_executions_migration
36
49
  if column_exists?(:ruby_llm_agents_executions, :tenant_id)
37
- say_status :skip, "tenant_id column already exists", :yellow
50
+ say_status :skip, "tenant_id column already exists on executions", :yellow
38
51
  return
39
52
  end
40
53
 
@@ -50,23 +63,30 @@ module RubyLlmAgents
50
63
  say ""
51
64
  say "Next steps:"
52
65
  say " 1. Run: rails db:migrate"
53
- say " 2. Configure multi-tenancy in your initializer:"
66
+ say " 2. Add llm_tenant to your tenant model:"
54
67
  say ""
55
- say " RubyLLM::Agents.configure do |config|"
56
- say " config.multi_tenancy_enabled = true"
57
- say " config.tenant_resolver = -> { Current.tenant&.id }"
68
+ say " class Organization < ApplicationRecord"
69
+ say " include RubyLLM::Agents::LLMTenant"
70
+ say ""
71
+ say " llm_tenant id: :id, # Method for tenant_id"
72
+ say " budget: true, # Auto-create budget on creation"
73
+ say " limits: { # Optional default limits"
74
+ say " daily_cost: 100,"
75
+ say " monthly_cost: 1000"
76
+ say " },"
77
+ say " enforcement: :hard # :none, :soft, or :hard"
58
78
  say " end"
59
79
  say ""
60
- say " 3. Set Current.tenant in your ApplicationController"
80
+ say " 3. Pass tenant to agents:"
81
+ say ""
82
+ say " MyAgent.call(prompt, tenant: current_organization)"
61
83
  say ""
62
- say " 4. Create tenant budgets:"
84
+ say " 4. Query usage:"
63
85
  say ""
64
- say " RubyLLM::Agents::TenantBudget.create!("
65
- say " tenant_id: 'acme_corp',"
66
- say " daily_limit: 50.0,"
67
- say " monthly_limit: 500.0,"
68
- say " enforcement: 'hard'"
69
- say " )"
86
+ say " tenant = RubyLLM::Agents::Tenant.for(organization)"
87
+ say " tenant.cost_today # => 12.34"
88
+ say " tenant.tokens_this_month # => 50000"
89
+ say " tenant.usage_summary # => { cost: ..., tokens: ..., ... }"
70
90
  say ""
71
91
  end
72
92
 
@@ -1,12 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Migration to add tenant_id column to executions for multi-tenancy support
3
+ # Migration to add tenant columns to executions for multi-tenancy support
4
4
  #
5
- # This migration adds a tenant_id column to track which tenant each execution
5
+ # This migration adds tenant columns to track which tenant each execution
6
6
  # belongs to, enabling:
7
7
  # - Filtering executions by tenant
8
8
  # - Tenant-scoped analytics and reporting
9
9
  # - Per-tenant budget tracking and circuit breakers
10
+ # - Polymorphic association with tenant models (e.g., Organization, Account)
10
11
  #
11
12
  # Run with: rails db:migrate
12
13
  class AddTenantIdToRubyLLMAgentsExecutions < ActiveRecord::Migration<%= migration_version %>
@@ -14,10 +15,20 @@ class AddTenantIdToRubyLLMAgentsExecutions < ActiveRecord::Migration<%= migratio
14
15
  # Add tenant_id column (nullable for backward compatibility)
15
16
  add_column :ruby_llm_agents_executions, :tenant_id, :string
16
17
 
18
+ # Polymorphic association to tenant model (for llm_tenant DSL)
19
+ # Uses string type for tenant_record_id to support both integer and UUID primary keys
20
+ add_column :ruby_llm_agents_executions, :tenant_record_type, :string
21
+ add_column :ruby_llm_agents_executions, :tenant_record_id, :string
22
+
17
23
  # Add indexes for efficient tenant-scoped queries
18
24
  add_index :ruby_llm_agents_executions, :tenant_id
19
25
  add_index :ruby_llm_agents_executions, [:tenant_id, :created_at]
20
26
  add_index :ruby_llm_agents_executions, [:tenant_id, :agent_type]
21
27
  add_index :ruby_llm_agents_executions, [:tenant_id, :status]
28
+
29
+ # Index for polymorphic tenant record lookup
30
+ add_index :ruby_llm_agents_executions,
31
+ [:tenant_record_type, :tenant_record_id],
32
+ name: "index_executions_on_tenant_record"
22
33
  end
23
34
  end
@@ -36,10 +36,21 @@ class CreateRubyLLMAgentsTenantBudgets < ActiveRecord::Migration<%= migration_ve
36
36
  # Whether to inherit from global config for unset limits
37
37
  t.boolean :inherit_global_defaults, default: true
38
38
 
39
+ # Polymorphic association to tenant model (e.g., Organization, Account)
40
+ # Allows direct association with ActiveRecord models using llm_tenant DSL
41
+ # Uses string type for tenant_record_id to support both integer and UUID primary keys
42
+ t.string :tenant_record_type
43
+ t.string :tenant_record_id
44
+
39
45
  t.timestamps
40
46
  end
41
47
 
42
48
  # Ensure unique tenant IDs
43
49
  add_index :ruby_llm_agents_tenant_budgets, :tenant_id, unique: true
50
+
51
+ # Index for polymorphic tenant record lookup
52
+ add_index :ruby_llm_agents_tenant_budgets,
53
+ [:tenant_record_type, :tenant_record_id],
54
+ name: "index_tenant_budgets_on_tenant_record"
44
55
  end
45
56
  end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Migration to create the tenants table for multi-tenancy support
4
+ #
5
+ # This table stores tenant configuration including:
6
+ # - Budget limits (cost, tokens, executions)
7
+ # - Enforcement mode (none, soft, hard)
8
+ # - Polymorphic link to user's tenant model
9
+ #
10
+ # Run with: rails db:migrate
11
+ class CreateRubyLLMAgentsTenants < ActiveRecord::Migration<%= migration_version %>
12
+ def change
13
+ create_table :ruby_llm_agents_tenants do |t|
14
+ # Identity
15
+ t.string :tenant_id, null: false
16
+ t.string :name
17
+
18
+ # Polymorphic association to user's tenant model (e.g., Organization, Account)
19
+ # Uses string type for tenant_record_id to support both integer and UUID primary keys
20
+ t.string :tenant_record_type
21
+ t.string :tenant_record_id
22
+
23
+ # Cost limits (in USD, 6 decimal precision)
24
+ t.decimal :daily_limit, precision: 12, scale: 6
25
+ t.decimal :monthly_limit, precision: 12, scale: 6
26
+
27
+ # Token limits
28
+ t.bigint :daily_token_limit
29
+ t.bigint :monthly_token_limit
30
+
31
+ # Execution/call limits
32
+ t.bigint :daily_execution_limit
33
+ t.bigint :monthly_execution_limit
34
+
35
+ # Per-agent limits (JSON hash)
36
+ # Format: { "AgentName" => limit_value }
37
+ t.json :per_agent_daily, null: false, default: {}
38
+ t.json :per_agent_monthly, null: false, default: {}
39
+
40
+ # Enforcement mode: "none", "soft", or "hard"
41
+ # - none: no enforcement, only tracking
42
+ # - soft: log warnings when limits exceeded
43
+ # - hard: block execution when limits exceeded
44
+ t.string :enforcement, default: "soft"
45
+
46
+ # Whether to inherit from global config for unset limits
47
+ t.boolean :inherit_global_defaults, default: true
48
+
49
+ # Status (for soft-delete/disable)
50
+ t.boolean :active, default: true
51
+
52
+ # Extensible metadata (JSON)
53
+ t.json :metadata, null: false, default: {}
54
+
55
+ t.timestamps
56
+ end
57
+
58
+ # Ensure unique tenant IDs
59
+ add_index :ruby_llm_agents_tenants, :tenant_id, unique: true
60
+
61
+ # Index for name lookups
62
+ add_index :ruby_llm_agents_tenants, :name
63
+
64
+ # Index for active/inactive filtering
65
+ add_index :ruby_llm_agents_tenants, :active
66
+
67
+ # Index for polymorphic tenant record lookup
68
+ add_index :ruby_llm_agents_tenants,
69
+ [:tenant_record_type, :tenant_record_id],
70
+ name: "index_tenants_on_tenant_record"
71
+ end
72
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Migration to rename tenant_budgets table to tenants
4
+ #
5
+ # This migration is for users upgrading from an older version of the gem
6
+ # where the table was called 'ruby_llm_agents_tenant_budgets'.
7
+ #
8
+ # If you're doing a fresh install, use the create_tenants_migration instead.
9
+ #
10
+ # Run with: rails db:migrate
11
+ class RenameTenantBudgetsToTenants < ActiveRecord::Migration<%= migration_version %>
12
+ def change
13
+ # Rename the table
14
+ rename_table :ruby_llm_agents_tenant_budgets, :ruby_llm_agents_tenants
15
+
16
+ # Add new columns for Tenant model
17
+ add_column :ruby_llm_agents_tenants, :active, :boolean, default: true
18
+ add_column :ruby_llm_agents_tenants, :metadata, :json, null: false, default: {}
19
+
20
+ # Add index for active status
21
+ add_index :ruby_llm_agents_tenants, :active
22
+
23
+ # Rename indexes to match new table name
24
+ # Note: Some databases handle this automatically when renaming tables
25
+ # If you get an error about missing indexes, you may need to adjust this
26
+
27
+ # The polymorphic index needs to be renamed if it exists
28
+ if index_exists?(:ruby_llm_agents_tenants, [:tenant_record_type, :tenant_record_id], name: "index_tenant_budgets_on_tenant_record")
29
+ rename_index :ruby_llm_agents_tenants,
30
+ "index_tenant_budgets_on_tenant_record",
31
+ "index_tenants_on_tenant_record"
32
+ end
33
+ end
34
+ end
@@ -147,6 +147,26 @@ module RubyLlmAgents
147
147
  )
148
148
  end
149
149
 
150
+ def create_rename_tenant_budgets_migration
151
+ # Skip if already using new table name
152
+ if table_exists?(:ruby_llm_agents_tenants)
153
+ say_status :skip, "ruby_llm_agents_tenants table already exists", :yellow
154
+ return
155
+ end
156
+
157
+ # Only run if old table exists (needs upgrade)
158
+ unless table_exists?(:ruby_llm_agents_tenant_budgets)
159
+ say_status :skip, "No tenant_budgets table to upgrade", :yellow
160
+ return
161
+ end
162
+
163
+ say_status :upgrade, "Renaming tenant_budgets to tenants", :blue
164
+ migration_template(
165
+ "rename_tenant_budgets_to_tenants_migration.rb.tt",
166
+ File.join(db_migrate_path, "rename_tenant_budgets_to_tenants.rb")
167
+ )
168
+ end
169
+
150
170
  def migrate_agents_directory
151
171
  root_dir = RubyLLM::Agents.configuration.root_directory
152
172
  namespace = RubyLLM::Agents.configuration.root_namespace
@@ -211,6 +231,12 @@ module RubyLlmAgents
211
231
  false
212
232
  end
213
233
 
234
+ def table_exists?(table)
235
+ ActiveRecord::Base.connection.table_exists?(table)
236
+ rescue StandardError
237
+ false
238
+ end
239
+
214
240
  def migrate_directory(old_dir, new_dir, namespace)
215
241
  source = Rails.root.join("app", old_dir)
216
242
  destination = Rails.root.join("app", new_dir)