Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# frozen_string_literal: true

gem 'ruby_llm', path: ENV['RUBYLLM_PATH'] || '../../../..'

after_bundle do
migration_version = "[#{Rails::VERSION::MAJOR}.#{Rails::VERSION::MINOR}]"

file 'config/initializers/ruby_llm.rb', <<~RUBY
RubyLLM.configure do |config|
config.openai_api_key = ENV.fetch("OPENAI_API_KEY", "test")
end
RUBY

file 'app/models/conversation.rb', <<~RUBY
class Conversation < ApplicationRecord
acts_as_chat
end
RUBY

file 'app/models/chat_message.rb', <<~RUBY
class ChatMessage < ApplicationRecord
acts_as_message
end
RUBY

file 'app/models/tool_call.rb', <<~RUBY
class ToolCall < ApplicationRecord
acts_as_tool_call
end
RUBY

file 'db/migrate/20200101000000_create_conversations.rb', <<~RUBY
class CreateConversations < ActiveRecord::Migration#{migration_version}
def change
create_table :conversations do |t|
t.string :model_id
t.string :provider
t.timestamps
end
end
end
RUBY

file 'db/migrate/20200101000001_create_chat_messages.rb', <<~RUBY
class CreateChatMessages < ActiveRecord::Migration#{migration_version}
def change
create_table :chat_messages do |t|
t.integer :conversation_id
t.string :model_id
t.string :provider
t.timestamps
end
end
end
RUBY

file 'db/migrate/20200101000002_create_tool_calls.rb', <<~RUBY
class CreateToolCalls < ActiveRecord::Migration#{migration_version}
def change
create_table :tool_calls do |t|
t.integer :chat_message_id
t.timestamps
end
end
end
RUBY

rails_command 'db:migrate'

generate 'ruby_llm:upgrade_to_v1_7', 'chat:Conversation', 'message:ChatMessage'
generate 'ruby_llm:upgrade_to_v1_9', 'message:ChatMessage'
end

73 changes: 73 additions & 0 deletions spec/fixtures/templates/upgrade_generators_default_template.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# frozen_string_literal: true

gem 'ruby_llm', path: ENV['RUBYLLM_PATH'] || '../../../..'

after_bundle do
migration_version = "[#{Rails::VERSION::MAJOR}.#{Rails::VERSION::MINOR}]"

file 'config/initializers/ruby_llm.rb', <<~RUBY
RubyLLM.configure do |config|
config.openai_api_key = ENV.fetch("OPENAI_API_KEY", "test")
end
RUBY

file 'app/models/chat.rb', <<~RUBY
class Chat < ApplicationRecord
acts_as_chat
end
RUBY

file 'app/models/message.rb', <<~RUBY
class Message < ApplicationRecord
acts_as_message
end
RUBY

file 'app/models/tool_call.rb', <<~RUBY
class ToolCall < ApplicationRecord
acts_as_tool_call
end
RUBY

file 'db/migrate/20200101000000_create_chats.rb', <<~RUBY
class CreateChats < ActiveRecord::Migration#{migration_version}
def change
create_table :chats do |t|
t.string :model_id
t.string :provider
t.timestamps
end
end
end
RUBY

file 'db/migrate/20200101000001_create_messages.rb', <<~RUBY
class CreateMessages < ActiveRecord::Migration#{migration_version}
def change
create_table :messages do |t|
t.integer :chat_id
t.string :model_id
t.string :provider
t.timestamps
end
end
end
RUBY

file 'db/migrate/20200101000002_create_tool_calls.rb', <<~RUBY
class CreateToolCalls < ActiveRecord::Migration#{migration_version}
def change
create_table :tool_calls do |t|
t.integer :message_id
t.timestamps
end
end
end
RUBY

rails_command 'db:migrate'

generate 'ruby_llm:upgrade_to_v1_7'
generate 'ruby_llm:upgrade_to_v1_9'
end

153 changes: 153 additions & 0 deletions spec/ruby_llm/generators/upgrade_generators_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
# frozen_string_literal: true

require 'spec_helper'
require 'fileutils'
require 'generators/ruby_llm/upgrade_to_v1_7/upgrade_to_v1_7_generator'
require 'generators/ruby_llm/upgrade_to_v1_9/upgrade_to_v1_9_generator'
require_relative '../../support/generator_test_helpers'

RSpec.describe 'RubyLLM upgrade generators', :generator, type: :generator do
include GeneratorTestHelpers

let(:template_path) { File.expand_path('../../fixtures/templates', __dir__) }

def migrations_containing(pattern)
Dir.glob('db/migrate/*.rb').select { |path| path.include?(pattern) }
end

describe 'with default model names' do
let(:app_name) { 'test_upgrade_generators_default' }
let(:app_path) { File.join(Dir.tmpdir, app_name) }

before(:all) do # rubocop:disable RSpec/BeforeAfterAll
template_path = File.expand_path('../../fixtures/templates', __dir__)
GeneratorTestHelpers.cleanup_test_app(File.join(Dir.tmpdir, 'test_upgrade_generators_default'))
GeneratorTestHelpers.create_test_app(
'test_upgrade_generators_default',
template: 'upgrade_generators_default_template.rb',
template_path: template_path
)
end

after(:all) do # rubocop:disable RSpec/BeforeAfterAll
GeneratorTestHelpers.cleanup_test_app(File.join(Dir.tmpdir, 'test_upgrade_generators_default'))
end

it 'creates the expected v1.7 migration files' do
within_test_app(app_path) do
expect(migrations_containing('create_models').any?).to be true
expect(migrations_containing('migrate_to_ruby_llm_model_references').any?).to be true
end
end

it 'updates existing models acts_as declarations when present' do
within_test_app(app_path) do
chat_model = File.read('app/models/chat.rb')
expect(chat_model).to include('acts_as_chat')
expect(chat_model).to include('messages_foreign_key')

message_model = File.read('app/models/message.rb')
expect(message_model).to include('acts_as_message')
expect(message_model).to include('tool_calls_foreign_key')

tool_call_model = File.read('app/models/tool_call.rb')
expect(tool_call_model).to include('acts_as_tool_call')
expect(tool_call_model).to include("\n acts_as_tool_call\n")
end
end

it 'updates existing initializer when present' do
within_test_app(app_path) do
initializer = File.read('config/initializers/ruby_llm.rb')
expect(initializer).to include('RubyLLM.configure')
expect(initializer).to include('config.use_new_acts_as = true')
end
end

it 'creates the expected v1.9 migration file' do
within_test_app(app_path) do
migration_path = migrations_containing('add_ruby_llm_v1_9_columns').first
expect(migration_path).not_to be_nil

migration = File.read(migration_path)
expect(migration).to include('add_column :messages, :cached_tokens')
expect(migration).to include('add_column :messages, :cache_creation_tokens')
expect(migration).to include('add_column :messages, :content_raw')
end
end
end

describe 'with custom model mappings' do
let(:app_name) { 'test_upgrade_generators_custom_mappings' }
let(:app_path) { File.join(Dir.tmpdir, app_name) }

before(:all) do # rubocop:disable RSpec/BeforeAfterAll
template_path = File.expand_path('../../fixtures/templates', __dir__)
GeneratorTestHelpers.cleanup_test_app(File.join(Dir.tmpdir, 'test_upgrade_generators_custom_mappings'))
GeneratorTestHelpers.create_test_app(
'test_upgrade_generators_custom_mappings',
template: 'upgrade_generators_custom_mappings_template.rb',
template_path: template_path
)
end

after(:all) do # rubocop:disable RSpec/BeforeAfterAll
GeneratorTestHelpers.cleanup_test_app(File.join(Dir.tmpdir, 'test_upgrade_generators_custom_mappings'))
end

it 'creates the expected v1.7 migration files' do
within_test_app(app_path) do
expect(migrations_containing('create_models').any?).to be true
expect(migrations_containing('migrate_to_ruby_llm_model_references').any?).to be true
end
end

it 'respects model mapping args in generated v1.7 migration content' do
within_test_app(app_path) do
migration_path = migrations_containing('migrate_to_ruby_llm_model_references').first
expect(migration_path).not_to be_nil

migration = File.read(migration_path)
expect(migration).to include('chat_class = Conversation')
expect(migration).to include('message_class = ChatMessage')
expect(migration).to include('collect_and_create_models(chat_class, :conversations, model_class)')
expect(migration).to include('collect_and_create_models(message_class, :chat_messages, model_class)')
expect(migration).to include('migrate_foreign_key(:conversations')
expect(migration).to include('migrate_foreign_key(:chat_messages')
end
end

it 'updates acts_as declarations using mapped table and association names' do
within_test_app(app_path) do
conversation_model = File.read('app/models/conversation.rb')
expect(conversation_model).to include('acts_as_chat')
expect(conversation_model).to include('messages: :chat_messages')
expect(conversation_model).to include('messages_foreign_key: :conversation_id')

chat_message_model = File.read('app/models/chat_message.rb')
expect(chat_message_model).to include('acts_as_message')
expect(chat_message_model).to include('chat: :conversation')
expect(chat_message_model).to include('chat_foreign_key: :conversation_id')
expect(chat_message_model).to include('tool_calls_foreign_key: :chat_message_id')

tool_call_model = File.read('app/models/tool_call.rb')
expect(tool_call_model).to include('acts_as_tool_call')
expect(tool_call_model).to include('message: :chat_message')
expect(tool_call_model).to include('message_foreign_key: :chat_message_id')
end
end

it 'creates a v1.9 migration targeting the mapped message table' do
within_test_app(app_path) do
migration_path = migrations_containing('add_ruby_llm_v1_9_columns').first
expect(migration_path).not_to be_nil

migration = File.read(migration_path)
expect(migration).to include('add_column :chat_messages, :cached_tokens')
expect(migration).to include('add_column :chat_messages, :cache_creation_tokens')
expect(migration).to include('add_column :chat_messages, :content_raw')
end
end
end
end