-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathrails_model.rb
252 lines (216 loc) · 7.51 KB
/
rails_model.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
require 'active_support/inflector'
class RailsModel
class << self
attr_accessor :model_list
def find_or_create(name)
model_list[name] || new(name)
end
def model(name, options=nil, &block)
new_model = find_or_create(name)
new_model.has(options) if options
new_model.instance_eval &block if block_given?
end
def models(*names)
names.each { |name| model(name.singularize) }
end
def timestamp
Time.now.to_s.split(' ')[0..1].join('').gsub(/\D/, '')
end
def create_file(directory, file_name, contents)
Dir.chdir(directory)
File.new("#{file_name}.rb", "w")
File.write("#{file_name}.rb", contents)
Dir.chdir("../..")
end
end
@model_list = {}
connection_methods = [:has_many, :has_one, :belong_to, :belongs_to, :has_and_belongs_to_many]
Symbol.class_eval do
def singularize
self.to_s.singularize.to_sym
end
def pluralize
self.to_s.pluralize.to_sym
end
connection_methods.each do |method|
define_method(method) do |*args|
if RailsModel.model_list.include? self.singularize
RailsModel.model_list[self.singularize].send(method, *args)
else
method_missing(method, *args)
end
end
end
end
attr_accessor :name, :attributes, :methods, :has_many_of_these, :has_one_of_these, :belongs_to_these, :has_and_belongs_to_many_of_these
def initialize(name)
@name = name
RailsModel.model_list[name] = self
@attributes = {}
@methods = {}
@has_many_of_these = {} # Plural
@has_one_of_these = {} # Singular
@belongs_to_these = {} # Singular
@has_and_belongs_to_many_of_these = {} # Plural
end
def has(attributes)
# attributes is a hash where the key is the name of the instance variable, and the value is the type.
@attributes.merge! attributes if attributes.is_a? Hash
if attributes.is_a? Symbol
id = attributes.to_s.singularize.+('_id').to_sym
has id => :integer
end
end
def can(method, description='')
@methods[method] = description
end
def has_many(*others)
others.each do |other|
other = RailsModel.find_or_create(other.singularize)
@has_many_of_these[other.name.pluralize] = nil
other.belongs_to_these[@name] = nil
other.has @name
end
end
def has_one(*others)
# Puts 'has_many other' on self.
# For each thing in others, creates it if it doesn't exist, and adds 'belongs_to' to each.
# Adds this RailsModel as an attribute to each thing on others.
if !others[1].is_a? Hash
others.each do |other|
other = RailsModel.find_or_create(other)
@has_one_of_these[other.name] = nil
other.belongs_to_these[@name] = nil
other.has @name
end
elsif join = others[1][:through]
other = RailsModel.find_or_create(others[0])
join = RailsModel.find_or_create(join)
@has_one_of_these[join.name] = nil
@has_one_of_these[other.name] = join.name
other.belongs_to_these[join.name] = nil
other.has join.name
join.has_one_of_these[other.name] = nil
join.belongs_to_these[@name] = nil
join.has @name
end
end
def belong_to(*others, options)
# Only used for polymorphism, has_many.
polymorphic = options[:as]
@belongs_to_these[polymorphic] = :polymorphic
RailsModel.find_or_create(@name.singularize).has polymorphic
others.each do |other|
RailsModel.find_or_create(other).has_many_of_these[@name.pluralize] = {as: :votable}
end
end
def belongs_to(*others, options)
# Only used for polymorphism, has_one.
end
def has_and_belongs_to_many(other, options={})
# Used for direct HABTM, or has_many to has_many.
other = RailsModel.find_or_create(other.singularize)
if options.empty?
@has_and_belongs_to_many_of_these[other.name.pluralize] = nil
other.has_and_belongs_to_many_of_these[@name.pluralize] = nil
elsif join = options[:through]
join = RailsModel.find_or_create(join)
join.belongs_to_these[@name] = nil
join.belongs_to_these[other.name] = nil
join.has @name
join.has other.name
@has_many_of_these[other.name.pluralize] = {through: join.name}
other.has_many_of_these[@name.pluralize] = {through: join.name}
end
end
def make_model
# Gets into the app/models directory and creates the file #{name}.rb, and writes the string to it.
attr_names = @attributes.keys.map{ |key| ':' + key.to_s.gsub(/_id\Z/, '') }
lines = []
lines << "class #{@name.capitalize} < ActiveRecord::Base"
lines << " attr_accessible " + attr_names.join(', ') unless @attributes.empty?
lines << ""
@has_many_of_these.select{|k,v| v.nil?}.keys.each do |other|
lines << " has_many :#{other}"
end
@has_many_of_these.select{|k,v| !v.nil?}.each do |other, join|
lines << " has_many :#{other}, #{join.keys.first}: :#{join.values.first}"
end
@has_one_of_these.select{|k,v| v.nil?}.keys.each do |other|
lines << " has_one :#{other}"
end
@has_one_of_these.select{|k,v| !v.nil?}.each do |other, join|
lines << " has_one :#{other}, through: :#{join}"
end
@belongs_to_these.select{|k,v| v.nil?}.keys.each do |other|
lines << " belongs_to :#{other}"
end
@belongs_to_these.select{|k,v| !v.nil?}.keys.each do |other|
lines << " belongs_to :#{other}, polymorphic: true"
end
@has_and_belongs_to_many_of_these.keys.each do |other|
lines << " has_and_belongs_to_many :#{other}"
end
lines << ""
@methods.each do |name, description|
lines << " def #{name}"
lines << " \# #{description}"
lines << " end"
lines << ""
end
lines << "end"
RailsModel.create_file("app/models", @name, lines.join("\n"))
end
def make_migration
table_name = @name.to_s.tableize
class_name = 'Create' + table_name.camelize
file_name = RailsModel.timestamp + '_' + class_name.underscore
lines = []
lines << "class #{class_name} < ActiveRecord::Migration"
lines << " def change"
lines << " create_table :#{table_name} do |t|"
attributes.each do |name, type|
lines << " t.#{type} :#{name}"
end
lines << ""
lines << " t.timestamps"
lines << " end"
lines << " end"
lines << "end"
Dir.mkdir("db/migrate") unless File.exists?("db/migrate")
RailsModel.create_file("db/migrate", file_name, lines.join("\n"))
if !has_and_belongs_to_many_of_these.empty?
has_and_belongs_to_many_of_these.keys.each do |other|
other = other.singularize
if @name < other
sleep 1.01
make_join_table_migration(other)
end
end
end
end
def make_join_table_migration(other)
table_name = @name.to_s.pluralize + '_' + other.to_s.pluralize
class_name = 'Create' + table_name.camelize + 'JoinTable'
file_name = RailsModel.timestamp + '_' + class_name.underscore
lines = []
lines << "class #{class_name} < ActiveRecord::Migration"
lines << " def change"
lines << " create_table :#{table_name}, id: false do |t|"
lines << " t.integer :#{@name}_id"
lines << " t.integer :#{other}_id"
lines << ""
lines << " t.timestamps"
lines << " end"
lines << " end"
lines << "end"
Dir.mkdir("db/migrate") unless File.exists?("db/migrate")
RailsModel.create_file("db/migrate", file_name, lines.join("\n"))
end
end
def describe_models(&block)
RailsModel.class_eval &block
end
def describe_associations(&block)
RailsModel.class_eval &block
end