22
33require 'rails/generators'
44require 'rails/generators/active_record'
5+ require_relative '../generator_helpers'
56
67module RubyLLM
78 # Generator for RubyLLM Rails models and migrations
89 class InstallGenerator < Rails ::Generators ::Base
910 include Rails ::Generators ::Migration
11+ include RubyLLM ::GeneratorHelpers
1012
1113 namespace 'ruby_llm:install'
1214
@@ -24,127 +26,6 @@ def self.next_migration_number(dirname)
2426 ::ActiveRecord ::Generators ::Base . next_migration_number ( dirname )
2527 end
2628
27- def migration_version
28- "[#{ Rails ::VERSION ::MAJOR } .#{ Rails ::VERSION ::MINOR } ]"
29- end
30-
31- def postgresql?
32- ::ActiveRecord ::Base . connection . adapter_name . downcase . include? ( 'postgresql' )
33- rescue StandardError
34- false
35- end
36-
37- def parse_model_mappings
38- @model_names = {
39- chat : 'Chat' ,
40- message : 'Message' ,
41- tool_call : 'ToolCall' ,
42- model : 'Model'
43- }
44-
45- model_mappings . each do |mapping |
46- if mapping . include? ( ':' )
47- key , value = mapping . split ( ':' , 2 )
48- @model_names [ key . to_sym ] = value . classify
49- end
50- end
51-
52- @model_names
53- end
54-
55- %i[ chat message tool_call model ] . each do |type |
56- define_method ( "#{ type } _model_name" ) do
57- @model_names ||= parse_model_mappings
58- @model_names [ type ]
59- end
60-
61- define_method ( "#{ type } _table_name" ) do
62- table_name_for ( send ( "#{ type } _model_name" ) )
63- end
64- end
65-
66- def acts_as_chat_declaration
67- acts_as_chat_params = [ ]
68- messages_assoc = message_model_name . tableize . to_sym
69- model_assoc = model_model_name . underscore . to_sym
70-
71- if messages_assoc != :messages
72- acts_as_chat_params << "messages: :#{ messages_assoc } "
73- if message_model_name != messages_assoc . to_s . classify
74- acts_as_chat_params << "message_class: '#{ message_model_name } '"
75- end
76- end
77-
78- if model_assoc != :model
79- acts_as_chat_params << "model: :#{ model_assoc } "
80- acts_as_chat_params << "model_class: '#{ model_model_name } '" if model_model_name != model_assoc . to_s . classify
81- end
82-
83- if acts_as_chat_params . any?
84- "acts_as_chat #{ acts_as_chat_params . join ( ', ' ) } "
85- else
86- 'acts_as_chat'
87- end
88- end
89-
90- def acts_as_message_declaration
91- params = [ ]
92-
93- add_message_association_params ( params , :chat , chat_model_name )
94- add_message_association_params ( params , :tool_calls , tool_call_model_name , tableize : true )
95- add_message_association_params ( params , :model , model_model_name )
96-
97- params . any? ? "acts_as_message #{ params . join ( ', ' ) } " : 'acts_as_message'
98- end
99-
100- private
101-
102- def add_message_association_params ( params , default_assoc , model_name , tableize : false )
103- assoc = tableize ? model_name . tableize . to_sym : model_name . underscore . to_sym
104-
105- return if assoc == default_assoc
106-
107- params << "#{ default_assoc } : :#{ assoc } "
108- expected_class = assoc . to_s . classify
109- params << "#{ default_assoc . to_s . singularize } _class: '#{ model_name } '" if model_name != expected_class
110- end
111-
112- public
113-
114- def acts_as_tool_call_declaration
115- acts_as_tool_call_params = [ ]
116- message_assoc = message_model_name . underscore . to_sym
117-
118- if message_assoc != :message
119- acts_as_tool_call_params << "message: :#{ message_assoc } "
120- if message_model_name != message_assoc . to_s . classify
121- acts_as_tool_call_params << "message_class: '#{ message_model_name } '"
122- end
123- end
124-
125- if acts_as_tool_call_params . any?
126- "acts_as_tool_call #{ acts_as_tool_call_params . join ( ', ' ) } "
127- else
128- 'acts_as_tool_call'
129- end
130- end
131-
132- def acts_as_model_declaration
133- acts_as_model_params = [ ]
134- chats_assoc = chat_model_name . tableize . to_sym
135-
136- if chats_assoc != :chats
137- acts_as_model_params << "chats: :#{ chats_assoc } "
138- acts_as_model_params << "chat_class: '#{ chat_model_name } '" if chat_model_name != chats_assoc . to_s . classify
139- end
140-
141- if acts_as_model_params . any?
142- "acts_as_model #{ acts_as_model_params . join ( ', ' ) } "
143- else
144- 'acts_as_model'
145- end
146- end
147-
14829 def create_migration_files
14930 # Create migrations with timestamps to ensure proper order
15031 # First create chats table
@@ -168,6 +49,8 @@ def create_migration_files
16849 end
16950
17051 def create_model_files
52+ create_namespace_modules
53+
17154 template 'chat_model.rb.tt' , "app/models/#{ chat_model_name . underscore } .rb"
17255 template 'message_model.rb.tt' , "app/models/#{ message_model_name . underscore } .rb"
17356 template 'tool_call_model.rb.tt' , "app/models/#{ tool_call_model_name . underscore } .rb"
@@ -186,12 +69,6 @@ def install_active_storage
18669 rails_command 'active_storage:install'
18770 end
18871
189- def table_name_for ( model_name )
190- # Convert namespaced model names to proper table names
191- # e.g., "Assistant::Chat" -> "assistant_chats" (not "assistant/chats")
192- model_name . underscore . pluralize . tr ( '/' , '_' )
193- end
194-
19572 def show_install_info
19673 say "\n ✅ RubyLLM installed!" , :green
19774
0 commit comments