baza 0.0.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.
- data/.document +5 -0
- data/.rspec +1 -0
- data/Gemfile +18 -0
- data/Gemfile.lock +55 -0
- data/LICENSE.txt +20 -0
- data/README.rdoc +19 -0
- data/Rakefile +49 -0
- data/VERSION +1 -0
- data/include/db.rb +784 -0
- data/include/dbtime.rb +35 -0
- data/include/drivers/.DS_Store +0 -0
- data/include/drivers/mysql/mysql.rb +604 -0
- data/include/drivers/mysql/mysql_columns.rb +155 -0
- data/include/drivers/mysql/mysql_indexes.rb +69 -0
- data/include/drivers/mysql/mysql_sqlspecs.rb +5 -0
- data/include/drivers/mysql/mysql_tables.rb +443 -0
- data/include/drivers/sqlite3/libknjdb_java_sqlite3.rb +83 -0
- data/include/drivers/sqlite3/libknjdb_sqlite3_ironruby.rb +69 -0
- data/include/drivers/sqlite3/sqlite3.rb +184 -0
- data/include/drivers/sqlite3/sqlite3_columns.rb +177 -0
- data/include/drivers/sqlite3/sqlite3_indexes.rb +29 -0
- data/include/drivers/sqlite3/sqlite3_sqlspecs.rb +5 -0
- data/include/drivers/sqlite3/sqlite3_tables.rb +449 -0
- data/include/dump.rb +122 -0
- data/include/idquery.rb +109 -0
- data/include/model.rb +873 -0
- data/include/model_custom.rb +153 -0
- data/include/model_handler.rb +957 -0
- data/include/model_handler_sqlhelper.rb +499 -0
- data/include/query_buffer.rb +87 -0
- data/include/revision.rb +342 -0
- data/include/row.rb +153 -0
- data/include/sqlspecs.rb +5 -0
- data/lib/baza.rb +8 -0
- data/spec/baza_spec.rb +286 -0
- data/spec/db_spec_encoding_test_file.txt +1 -0
- data/spec/spec_helper.rb +12 -0
- metadata +215 -0
data/include/dump.rb
ADDED
@@ -0,0 +1,122 @@
|
|
1
|
+
#This class can be used to make SQL-dumps of databases, tables or however you want it.
|
2
|
+
class Baza::Dump
|
3
|
+
#Constructor.
|
4
|
+
#===Examples
|
5
|
+
# dump = Baza::Dump.new(:db => db)
|
6
|
+
def initialize(args)
|
7
|
+
@args = args
|
8
|
+
@debug = @args[:debug]
|
9
|
+
end
|
10
|
+
|
11
|
+
#Method used to update the status.
|
12
|
+
def update_status
|
13
|
+
return nil if !@on_status
|
14
|
+
rows_count = Knj::Locales.number_out(@rows_count, 0)
|
15
|
+
rows_count_total = Knj::Locales.number_out(@rows_count_total, 0)
|
16
|
+
percent = (@rows_count.to_f / @rows_count_total.to_f) * 100
|
17
|
+
percent_text = Knj::Locales.number_out(percent, 1)
|
18
|
+
@on_status.call(:text => "Dumping table: '#{@table_obj.name}' (#{rows_count}/#{rows_count_total} - #{percent_text}%).")
|
19
|
+
end
|
20
|
+
|
21
|
+
#Dumps all tables into the given IO.
|
22
|
+
def dump(io)
|
23
|
+
print "Going through tables.\n" if @debug
|
24
|
+
@rows_count = 0
|
25
|
+
|
26
|
+
if @args[:tables]
|
27
|
+
tables = @args[:tables]
|
28
|
+
else
|
29
|
+
tables = @args[:db].tables.list.values
|
30
|
+
end
|
31
|
+
|
32
|
+
if @on_status
|
33
|
+
@on_status.call(:text => "Preparing.")
|
34
|
+
|
35
|
+
@rows_count_total = 0
|
36
|
+
tables.each do |table_obj|
|
37
|
+
@rows_count_total += table_obj.rows_count
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
tables.each do |table_obj|
|
42
|
+
table_obj = @args[:db].tables[table_obj] if table_obj.is_a?(String) or table_obj.is_a?(Symbol)
|
43
|
+
next if table_obj.native?
|
44
|
+
|
45
|
+
#Figure out keys.
|
46
|
+
@keys = []
|
47
|
+
table_obj.columns do |col|
|
48
|
+
@keys << col.name
|
49
|
+
end
|
50
|
+
|
51
|
+
@table_obj = table_obj
|
52
|
+
self.update_status
|
53
|
+
print "Dumping table: '#{table_obj.name}'.\n" if @debug
|
54
|
+
self.dump_table(io, table_obj)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
#A block can be executed when a new status occurs.
|
59
|
+
def on_status(&block)
|
60
|
+
@on_status = block
|
61
|
+
end
|
62
|
+
|
63
|
+
#Dumps the given table into the given IO.
|
64
|
+
def dump_table(io, table_obj)
|
65
|
+
#Get SQL for creating table and add it to IO.
|
66
|
+
sqls = @args[:db].tables.create(table_obj.name, table_obj.data, :return_sql => true)
|
67
|
+
sqls.each do |sql|
|
68
|
+
io.write("#{sql};\n")
|
69
|
+
end
|
70
|
+
|
71
|
+
|
72
|
+
#Try to find a primary column in the table.
|
73
|
+
prim_col = nil
|
74
|
+
table_obj.columns do |col|
|
75
|
+
if col.primarykey?
|
76
|
+
prim_col = col
|
77
|
+
break
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
|
82
|
+
#Set up rows and way to fill rows.
|
83
|
+
rows = []
|
84
|
+
block_data = proc do |row|
|
85
|
+
rows << row
|
86
|
+
@rows_count += 1
|
87
|
+
|
88
|
+
if rows.length >= 1000
|
89
|
+
self.update_status
|
90
|
+
self.dump_insert_multi(io, table_obj, rows)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
|
95
|
+
#If a primary column is found then use IDQuery. Otherwise use cloned unbuffered query.
|
96
|
+
args = {:idquery => prim_col.name.to_sym} if prim_col
|
97
|
+
|
98
|
+
|
99
|
+
#Clone the connecting with array-results and execute query.
|
100
|
+
@args[:db].clone_conn(:result => "array") do |db|
|
101
|
+
db.select(table_obj.name, nil, args, &block_data)
|
102
|
+
end
|
103
|
+
|
104
|
+
|
105
|
+
#Dump the last rows if any.
|
106
|
+
self.dump_insert_multi(io, table_obj, rows) if !rows.empty?
|
107
|
+
end
|
108
|
+
|
109
|
+
#Dumps the given rows from the given table into the given IO.
|
110
|
+
def dump_insert_multi(io, table_obj, rows)
|
111
|
+
print "Inserting #{rows.length} into #{table_obj.name}.\n" if @debug
|
112
|
+
sqls = @args[:db].insert_multi(table_obj.name, rows, :return_sql => true, :keys => @keys)
|
113
|
+
sqls.each do |sql|
|
114
|
+
io.write("#{sql};\n")
|
115
|
+
end
|
116
|
+
|
117
|
+
rows.clear
|
118
|
+
|
119
|
+
#Ensure garbage collection or we might start using A LOT of memory.
|
120
|
+
GC.start
|
121
|
+
end
|
122
|
+
end
|
data/include/idquery.rb
ADDED
@@ -0,0 +1,109 @@
|
|
1
|
+
#This class takes a lot of IDs and runs a query against them.
|
2
|
+
class Baza::Idquery
|
3
|
+
#An array containing all the IDs that will be looked up.
|
4
|
+
attr_reader :ids
|
5
|
+
|
6
|
+
#Constructor.
|
7
|
+
#===Examples
|
8
|
+
# idq = Baza::Idquery(:db => db, :table => :users)
|
9
|
+
# idq.ids + [1, 5, 9]
|
10
|
+
# idq.each do |user|
|
11
|
+
# print "Name: #{user[:name]}\n"
|
12
|
+
# end
|
13
|
+
def initialize(args, &block)
|
14
|
+
@args = args
|
15
|
+
@ids = []
|
16
|
+
@debug = @args[:debug]
|
17
|
+
|
18
|
+
if @args[:query]
|
19
|
+
@args[:db].q(@args[:query]) do |data|
|
20
|
+
@args[:col] = data.keys.first if !@args[:col]
|
21
|
+
|
22
|
+
if data.is_a?(Array)
|
23
|
+
@ids << data.first
|
24
|
+
else
|
25
|
+
@ids << data[@args[:col]]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
@args[:col] = :id if !@args[:col]
|
31
|
+
@args[:size] = 200 if !@args[:size]
|
32
|
+
|
33
|
+
if block
|
34
|
+
raise "No query was given but a block was." if !@args[:query]
|
35
|
+
self.each(&block)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
#Fetches results.
|
40
|
+
#===Examples
|
41
|
+
# data = idq.fetch #=> Hash
|
42
|
+
def fetch
|
43
|
+
return nil if !@args
|
44
|
+
|
45
|
+
if @res
|
46
|
+
data = @res.fetch if @res
|
47
|
+
@res = nil if !data
|
48
|
+
return data if data
|
49
|
+
end
|
50
|
+
|
51
|
+
@res = new_res if !@res
|
52
|
+
if !@res
|
53
|
+
destroy
|
54
|
+
return nil
|
55
|
+
end
|
56
|
+
|
57
|
+
data = @res.fetch
|
58
|
+
if !data
|
59
|
+
destroy
|
60
|
+
return nil
|
61
|
+
end
|
62
|
+
|
63
|
+
return data
|
64
|
+
end
|
65
|
+
|
66
|
+
#Yields a block for every result.
|
67
|
+
#===Examples
|
68
|
+
# idq.each do |data|
|
69
|
+
# print "Name: #{data[:name]}\n"
|
70
|
+
# end
|
71
|
+
def each
|
72
|
+
while data = self.fetch
|
73
|
+
yield(data)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
private
|
78
|
+
|
79
|
+
#Spawns a new database-result to read from.
|
80
|
+
def new_res
|
81
|
+
table_esc = "`#{@args[:db].esc_table(@args[:table])}`"
|
82
|
+
col_esc = "`#{@args[:db].esc_col(@args[:col])}`"
|
83
|
+
ids = @ids.shift(@args[:size])
|
84
|
+
|
85
|
+
if ids.empty?
|
86
|
+
destroy
|
87
|
+
return nil
|
88
|
+
end
|
89
|
+
|
90
|
+
ids_sql = Knj::ArrayExt.join(
|
91
|
+
:arr => ids,
|
92
|
+
:callback => proc{|val| @args[:db].esc(val)},
|
93
|
+
:sep => ",",
|
94
|
+
:surr => "'"
|
95
|
+
)
|
96
|
+
|
97
|
+
query_str = "SELECT * FROM #{table_esc} WHERE #{table_esc}.#{col_esc} IN (#{ids_sql})"
|
98
|
+
print "Query: #{query_str}\n" if @debug
|
99
|
+
|
100
|
+
return @args[:db].q(query_str)
|
101
|
+
end
|
102
|
+
|
103
|
+
#Removes all variables on the object. This is done when no more results are available.
|
104
|
+
def destroy
|
105
|
+
@args = nil
|
106
|
+
@ids = nil
|
107
|
+
@debug = nil
|
108
|
+
end
|
109
|
+
end
|
data/include/model.rb
ADDED
@@ -0,0 +1,873 @@
|
|
1
|
+
#This class helps create models in a framework with Baza::Db and Baza::ModelHandler.
|
2
|
+
#===Examples
|
3
|
+
# db = Baza::Db.new(:type => "sqlite3", :path => "somepath.sqlite3")
|
4
|
+
# ob = Baza::ModelHandler.new(:db => db, :datarow => true, :path => "path_of_model_class_files")
|
5
|
+
# user = ob.get(:User, 1) #=> <Models::User> that extends <Baza::Datarow>
|
6
|
+
class Baza::Model
|
7
|
+
@@refs = {}
|
8
|
+
|
9
|
+
#Returns the Baza::ModelHandler which handels this model.
|
10
|
+
def ob
|
11
|
+
return self.class.ob
|
12
|
+
end
|
13
|
+
|
14
|
+
#Returns the Baza::Db which handels this model.
|
15
|
+
def db
|
16
|
+
return self.class.db
|
17
|
+
end
|
18
|
+
|
19
|
+
#Returns the 'Baza::ModelHandler'-object that handels this class.
|
20
|
+
def self.ob
|
21
|
+
return @ob
|
22
|
+
end
|
23
|
+
|
24
|
+
#Returns the 'Baza::Db'-object that handels this class.
|
25
|
+
def self.db
|
26
|
+
return @db
|
27
|
+
end
|
28
|
+
|
29
|
+
#This is used by 'Baza::ModelHandler' to find out what data is required for this class. Returns the array that tells about required data.
|
30
|
+
#===Examples
|
31
|
+
#When adding a new user, this can fail if the ':group_id' is not given, or the ':group_id' doesnt refer to a valid group-row in the db.
|
32
|
+
# class Models::User < Baza::Datarow
|
33
|
+
# has_one [
|
34
|
+
# {:class => :Group, :col => :group_id, :method => :group, :required => true}
|
35
|
+
# ]
|
36
|
+
# end
|
37
|
+
def self.required_data
|
38
|
+
@required_data = [] if !@required_data
|
39
|
+
return @required_data
|
40
|
+
end
|
41
|
+
|
42
|
+
#This is used by 'Baza::ModelHandler' to find out what other objects this class depends on. Returns the array that tells about depending data.
|
43
|
+
#===Examples
|
44
|
+
#This will tell Baza::ModelHandler that files depends on users. It can prevent the user from being deleted, if any files depend on it.
|
45
|
+
# class Models::User < Baza::Datarow
|
46
|
+
# has_many [
|
47
|
+
# {:class => :File, :col => :user_id, :method => :files, :depends => true}
|
48
|
+
# ]
|
49
|
+
# end
|
50
|
+
def self.depending_data
|
51
|
+
return @depending_data
|
52
|
+
end
|
53
|
+
|
54
|
+
#Returns true if this class has been initialized.
|
55
|
+
def self.initialized?
|
56
|
+
return false if !@columns_sqlhelper_args
|
57
|
+
return true
|
58
|
+
end
|
59
|
+
|
60
|
+
#This is used by 'Baza::ModelHandler' to find out which other objects should be deleted when an object of this class is deleted automatically. Returns the array that tells about autodelete data.
|
61
|
+
#===Examples
|
62
|
+
#This will trigger Baza::ModelHandler to automatically delete all the users pictures, when deleting the current user.
|
63
|
+
# class Models::User < Baza::Datarow
|
64
|
+
# has_many [
|
65
|
+
# {:class => :Picture, :col => :user_id, :method => :pictures, :autodelete => true}
|
66
|
+
# ]
|
67
|
+
# end
|
68
|
+
def self.autodelete_data
|
69
|
+
return @autodelete_data
|
70
|
+
end
|
71
|
+
|
72
|
+
#Returns the autozero-data (if any).
|
73
|
+
def self.autozero_data
|
74
|
+
return @autozero_data
|
75
|
+
end
|
76
|
+
|
77
|
+
#This helps various parts of the framework determine if this is a datarow class without requiring it.
|
78
|
+
#===Examples
|
79
|
+
# print "This is a knj-object." if obj.respond_to?("is_knj?")
|
80
|
+
def is_knj?
|
81
|
+
return true
|
82
|
+
end
|
83
|
+
|
84
|
+
#This tests if a certain string is a date-null-stamp.
|
85
|
+
#===Examples
|
86
|
+
# time_str = dbrow[:date]
|
87
|
+
# print "No valid date on the row." if Baza::Datarow.is_nullstamp?(time_str)
|
88
|
+
def self.is_nullstamp?(stamp)
|
89
|
+
return true if !stamp or stamp == "0000-00-00 00:00:00" or stamp == "0000-00-00"
|
90
|
+
return false
|
91
|
+
end
|
92
|
+
|
93
|
+
#This is used to define datarows that this object can have a lot of.
|
94
|
+
#===Examples
|
95
|
+
#This will define the method "pictures" on 'Models::User' that will return all pictures for the users and take possible Objects-sql-arguments. It will also enabling joining pictures when doing Objects-sql-lookups.
|
96
|
+
# class Models::User < Baza::Datarow
|
97
|
+
# has_many [
|
98
|
+
# [:Picture, :user_id, :pictures],
|
99
|
+
# {:class => :File, :col => :user_id, :method => :files}
|
100
|
+
# ]
|
101
|
+
# end
|
102
|
+
def self.has_many(arr)
|
103
|
+
arr.each do |val|
|
104
|
+
if val.is_a?(Array)
|
105
|
+
classname, colname, methodname = *val
|
106
|
+
elsif val.is_a?(Hash)
|
107
|
+
classname, colname, methodname = nil, nil, nil
|
108
|
+
|
109
|
+
val.each do |hkey, hval|
|
110
|
+
case hkey
|
111
|
+
when :class
|
112
|
+
classname = hval
|
113
|
+
when :col
|
114
|
+
colname = hval
|
115
|
+
when :method
|
116
|
+
methodname = hval
|
117
|
+
when :depends, :autodelete, :autozero, :where
|
118
|
+
#ignore
|
119
|
+
else
|
120
|
+
raise "Invalid key for 'has_many': '#{hkey}'."
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
colname = "#{self.name.to_s.split("::").last.to_s.downcase}_id".to_sym if colname.to_s.empty?
|
125
|
+
|
126
|
+
if val[:depends]
|
127
|
+
@depending_data = [] if !@depending_data
|
128
|
+
@depending_data << {
|
129
|
+
:colname => colname,
|
130
|
+
:classname => classname
|
131
|
+
}
|
132
|
+
end
|
133
|
+
|
134
|
+
if val[:autodelete]
|
135
|
+
@autodelete_data = [] if !@autodelete_data
|
136
|
+
@autodelete_data << {
|
137
|
+
:colname => colname,
|
138
|
+
:classname => classname
|
139
|
+
}
|
140
|
+
end
|
141
|
+
|
142
|
+
if val[:autozero]
|
143
|
+
@autozero_data = [] if !@autozero_data
|
144
|
+
@autozero_data << {
|
145
|
+
:colname => colname,
|
146
|
+
:classname => classname
|
147
|
+
}
|
148
|
+
end
|
149
|
+
else
|
150
|
+
raise "Unknown argument: '#{val.class.name}'."
|
151
|
+
end
|
152
|
+
|
153
|
+
raise "No classname given." if !classname
|
154
|
+
methodname = "#{classname.to_s.downcase}s".to_sym if !methodname
|
155
|
+
raise "No column was given for '#{self.name}' regarding has-many-class: '#{classname}'." if !colname
|
156
|
+
|
157
|
+
if val.is_a?(Hash) and val.key?(:where)
|
158
|
+
where_args = val[:where]
|
159
|
+
else
|
160
|
+
where_args = nil
|
161
|
+
end
|
162
|
+
|
163
|
+
self.define_many_methods(classname, methodname, colname, where_args)
|
164
|
+
|
165
|
+
self.joined_tables(
|
166
|
+
classname => {
|
167
|
+
:where => {
|
168
|
+
colname.to_s => {:type => :col, :name => :id}
|
169
|
+
}
|
170
|
+
}
|
171
|
+
)
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
#This define is this object has one element of another datarow-class. It define various methods and joins based on that.
|
176
|
+
#===Examples
|
177
|
+
# class Models::User < Baza::Datarow
|
178
|
+
# has_one [
|
179
|
+
# #Defines the method 'group', which returns a 'Group'-object by the column 'group_id'.
|
180
|
+
# :Group,
|
181
|
+
#
|
182
|
+
# #Defines the method 'type', which returns a 'Type'-object by the column 'type_id'.
|
183
|
+
# {:class => :Type, :col => :type_id, :method => :type}
|
184
|
+
# ]
|
185
|
+
# end
|
186
|
+
def self.has_one(arr)
|
187
|
+
arr.each do |val|
|
188
|
+
methodname = nil
|
189
|
+
colname = nil
|
190
|
+
classname = nil
|
191
|
+
|
192
|
+
if val.is_a?(Symbol)
|
193
|
+
classname = val
|
194
|
+
methodname = val.to_s.downcase.to_sym
|
195
|
+
colname = "#{val.to_s.downcase}_id".to_sym
|
196
|
+
elsif val.is_a?(Array)
|
197
|
+
classname, colname, methodname = *val
|
198
|
+
elsif val.is_a?(Hash)
|
199
|
+
classname, colname, methodname = nil, nil, nil
|
200
|
+
|
201
|
+
val.each do |hkey, hval|
|
202
|
+
case hkey
|
203
|
+
when :class
|
204
|
+
classname = hval
|
205
|
+
when :col
|
206
|
+
colname = hval
|
207
|
+
when :method
|
208
|
+
methodname = hval
|
209
|
+
when :required
|
210
|
+
#ignore
|
211
|
+
else
|
212
|
+
raise "Invalid key for class '#{self.name}' functionality 'has_many': '#{hkey}'."
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
if val[:required]
|
217
|
+
colname = "#{classname.to_s.downcase}_id".to_sym if !colname
|
218
|
+
self.required_data << {
|
219
|
+
:col => colname,
|
220
|
+
:class => classname
|
221
|
+
}
|
222
|
+
end
|
223
|
+
else
|
224
|
+
raise "Unknown argument-type: '#{arr.class.name}'."
|
225
|
+
end
|
226
|
+
|
227
|
+
methodname = classname.to_s.downcase if !methodname
|
228
|
+
colname = "#{classname.to_s.downcase}_id".to_sym if !colname
|
229
|
+
self.define_one_methods(classname, methodname, colname)
|
230
|
+
|
231
|
+
self.joined_tables(
|
232
|
+
classname => {
|
233
|
+
:where => {
|
234
|
+
"id" => {:type => :col, :name => colname}
|
235
|
+
}
|
236
|
+
}
|
237
|
+
)
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
#This method initializes joins, sets methods to update translations and makes the translations automatically be deleted when the object is deleted.
|
242
|
+
#===Examples
|
243
|
+
# class Models::Article < Baza::Datarow
|
244
|
+
# #Defines methods such as: 'title', 'title=', 'content', 'content='. When used with Knjappserver these methods will change what they return and set based on the current language of the session.
|
245
|
+
# has_translation [:title, :content]
|
246
|
+
# end
|
247
|
+
#
|
248
|
+
# article = ob.get(:Article, 1)
|
249
|
+
# print "The title in the current language is: '#{article.title}'."
|
250
|
+
#
|
251
|
+
# article.title = 'Title in english if the language is english'
|
252
|
+
def self.has_translation(arr)
|
253
|
+
@translations = [] if !@translations
|
254
|
+
|
255
|
+
arr.each do |val|
|
256
|
+
@translations << val
|
257
|
+
|
258
|
+
val_dc = val.to_s.downcase
|
259
|
+
table_name = "Translation_#{val_dc}".to_sym
|
260
|
+
|
261
|
+
joined_tables(
|
262
|
+
table_name => {
|
263
|
+
:where => {
|
264
|
+
"object_class" => self.name,
|
265
|
+
"object_id" => {:type => :col, :name => :id},
|
266
|
+
"key" => val.to_s,
|
267
|
+
"locale" => proc{|d| _session[:locale]}
|
268
|
+
},
|
269
|
+
:parent_table => :Translation,
|
270
|
+
:datarow => Knj::Translations::Translation,
|
271
|
+
:ob => @ob
|
272
|
+
}
|
273
|
+
)
|
274
|
+
|
275
|
+
self.define_translation_methods(:val => val, :val_dc => val_dc)
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
279
|
+
#This returns all translations for this datarow-class.
|
280
|
+
def self.translations
|
281
|
+
return @translations
|
282
|
+
end
|
283
|
+
|
284
|
+
#Returns data about joined tables for this class.
|
285
|
+
def self.joined_tables(hash)
|
286
|
+
@columns_joined_tables = {} if !@columns_joined_tables
|
287
|
+
@columns_joined_tables.merge!(hash)
|
288
|
+
end
|
289
|
+
|
290
|
+
#Returns various data for the objects-sql-helper. This can be used to view various informations about the columns and more.
|
291
|
+
def self.columns_sqlhelper_args
|
292
|
+
raise "No SQLHelper arguments has been spawned yet." if !@columns_sqlhelper_args
|
293
|
+
return @columns_sqlhelper_args
|
294
|
+
end
|
295
|
+
|
296
|
+
#Called by Baza::ModelHandler to initialize the model and load column-data on-the-fly.
|
297
|
+
def self.load_columns(d)
|
298
|
+
@ob = d.ob
|
299
|
+
@db = d.db
|
300
|
+
|
301
|
+
@classname = self.name.split("::").last.to_sym if !@classname
|
302
|
+
@table = @classname if !@table
|
303
|
+
@mutex = Monitor.new if !@mutex
|
304
|
+
|
305
|
+
#Cache these to avoid method-lookups.
|
306
|
+
@sep_col = @db.sep_col
|
307
|
+
@sep_table = @db.sep_table
|
308
|
+
@table_str = "#{@sep_table}#{@db.esc_table(@table)}#{@sep_table}"
|
309
|
+
|
310
|
+
@mutex.synchronize do
|
311
|
+
inst_methods = self.instance_methods(false)
|
312
|
+
|
313
|
+
sqlhelper_args = {
|
314
|
+
:db => @db,
|
315
|
+
:table => @table,
|
316
|
+
:cols_bools => [],
|
317
|
+
:cols_date => [],
|
318
|
+
:cols_dbrows => [],
|
319
|
+
:cols_num => [],
|
320
|
+
:cols_str => [],
|
321
|
+
:cols => {}
|
322
|
+
}
|
323
|
+
|
324
|
+
sqlhelper_args[:table] = @table
|
325
|
+
|
326
|
+
@db.tables[table].columns do |col_obj|
|
327
|
+
col_name = col_obj.name.to_s
|
328
|
+
col_name_sym = col_name.to_sym
|
329
|
+
col_type = col_obj.type
|
330
|
+
col_type = :int if col_type == :bigint or col_type == :tinyint or col_type == :mediumint or col_type == :smallint
|
331
|
+
sqlhelper_args[:cols][col_name] = true
|
332
|
+
|
333
|
+
self.define_bool_methods(inst_methods, col_name)
|
334
|
+
|
335
|
+
if col_type == :enum and col_obj.maxlength == "'0','1'"
|
336
|
+
sqlhelper_args[:cols_bools] << col_name
|
337
|
+
elsif col_type == :int and col_name.slice(-3, 3) == "_id"
|
338
|
+
sqlhelper_args[:cols_dbrows] << col_name
|
339
|
+
elsif col_type == :int or col_type == :decimal
|
340
|
+
sqlhelper_args[:cols_num] << col_name
|
341
|
+
elsif col_type == :varchar or col_type == :text or col_type == :enum
|
342
|
+
sqlhelper_args[:cols_str] << col_name
|
343
|
+
elsif col_type == :date or col_type == :datetime
|
344
|
+
sqlhelper_args[:cols_date] << col_name
|
345
|
+
self.define_date_methods(inst_methods, col_name_sym)
|
346
|
+
end
|
347
|
+
|
348
|
+
if col_type == :int or col_type == :decimal
|
349
|
+
self.define_numeric_methods(inst_methods, col_name_sym)
|
350
|
+
end
|
351
|
+
|
352
|
+
if col_type == :int or col_type == :varchar
|
353
|
+
self.define_text_methods(inst_methods, col_name_sym)
|
354
|
+
end
|
355
|
+
|
356
|
+
if col_type == :time
|
357
|
+
self.define_time_methods(inst_methods, col_name_sym)
|
358
|
+
end
|
359
|
+
end
|
360
|
+
|
361
|
+
if @columns_joined_tables
|
362
|
+
@columns_joined_tables.each do |table_name, table_data|
|
363
|
+
table_data[:where].each do |key, val|
|
364
|
+
val[:table] = @table if val.is_a?(Hash) and !val.key?(:table) and val[:type].to_sym == :col
|
365
|
+
end
|
366
|
+
|
367
|
+
table_data[:datarow] = @ob.args[:module].const_get(table_name.to_sym) if !table_data.key?(:datarow)
|
368
|
+
end
|
369
|
+
|
370
|
+
sqlhelper_args[:joined_tables] = @columns_joined_tables
|
371
|
+
end
|
372
|
+
|
373
|
+
@columns_sqlhelper_args = sqlhelper_args
|
374
|
+
end
|
375
|
+
|
376
|
+
self.init_class(d) if self.respond_to?(:init_class)
|
377
|
+
end
|
378
|
+
|
379
|
+
#This method helps returning objects and supports various arguments. It should be called by Object#list.
|
380
|
+
#===Examples
|
381
|
+
# ob.list(:User, {"username_lower" => "john doe"}) do |user|
|
382
|
+
# print user.id
|
383
|
+
# end
|
384
|
+
#
|
385
|
+
# array = ob.list(:User, {"id" => 1})
|
386
|
+
# print array.length
|
387
|
+
def self.list(d, &block)
|
388
|
+
args = d.args
|
389
|
+
|
390
|
+
if args["count"]
|
391
|
+
count = true
|
392
|
+
args.delete("count")
|
393
|
+
sql = "SELECT COUNT(#{@table_str}.#{@sep_col}id#{@sep_col}) AS count"
|
394
|
+
elsif args["select_col_as_array"]
|
395
|
+
select_col_as_array = true
|
396
|
+
sql = "SELECT #{@table_str}.#{@sep_col}#{args["select_col_as_array"]}#{@sep_col} AS id"
|
397
|
+
args.delete("select_col_as_array")
|
398
|
+
else
|
399
|
+
sql = "SELECT #{@table_str}.*"
|
400
|
+
end
|
401
|
+
|
402
|
+
qargs = nil
|
403
|
+
ret = self.list_helper(d)
|
404
|
+
|
405
|
+
sql << " FROM #{@table_str}"
|
406
|
+
sql << ret[:sql_joins]
|
407
|
+
sql << " WHERE 1=1"
|
408
|
+
sql << ret[:sql_where]
|
409
|
+
|
410
|
+
args.each do |key, val|
|
411
|
+
case key
|
412
|
+
when "return_sql"
|
413
|
+
#ignore
|
414
|
+
when :cloned_ubuf
|
415
|
+
qargs = {:cloned_ubuf => true}
|
416
|
+
else
|
417
|
+
raise "Invalid key: '#{key}' for '#{self.name}'. Valid keys are: '#{@columns_sqlhelper_args[:cols].keys.sort}'. Date-keys: '#{@columns_sqlhelper_args[:cols_date]}'."
|
418
|
+
end
|
419
|
+
end
|
420
|
+
|
421
|
+
#The count will bug if there is a group-by-statement.
|
422
|
+
grp_shown = false
|
423
|
+
if !count and !ret[:sql_groupby]
|
424
|
+
sql << " GROUP BY #{@table_str}.#{@sep_col}id#{@sep_col}"
|
425
|
+
grp_shown = true
|
426
|
+
end
|
427
|
+
|
428
|
+
if ret[:sql_groupby]
|
429
|
+
if !grp_shown
|
430
|
+
sql << " GROUP BY"
|
431
|
+
else
|
432
|
+
sql << ", "
|
433
|
+
end
|
434
|
+
|
435
|
+
sql << ret[:sql_groupby]
|
436
|
+
end
|
437
|
+
|
438
|
+
sql << ret[:sql_order]
|
439
|
+
sql << ret[:sql_limit]
|
440
|
+
|
441
|
+
return sql.to_s if args["return_sql"]
|
442
|
+
|
443
|
+
if select_col_as_array
|
444
|
+
enum = Enumerator.new do |yielder|
|
445
|
+
@db.q(sql, qargs) do |data|
|
446
|
+
yielder << data[:id]
|
447
|
+
end
|
448
|
+
end
|
449
|
+
|
450
|
+
if block
|
451
|
+
enum.each(&block)
|
452
|
+
return nil
|
453
|
+
elsif @ob.args[:array_enum]
|
454
|
+
return Array_enumerator.new(enum)
|
455
|
+
else
|
456
|
+
return enum.to_a
|
457
|
+
end
|
458
|
+
elsif count
|
459
|
+
ret = @db.query(sql).fetch
|
460
|
+
return ret[:count].to_i if ret
|
461
|
+
return 0
|
462
|
+
end
|
463
|
+
|
464
|
+
return @ob.list_bysql(self.classname, sql, qargs, &block)
|
465
|
+
end
|
466
|
+
|
467
|
+
#Helps call 'sqlhelper' on Baza::ModelHandler to generate SQL-strings.
|
468
|
+
def self.list_helper(d)
|
469
|
+
self.load_columns(d) if !@columns_sqlhelper_args
|
470
|
+
@columns_sqlhelper_args[:table] = @table
|
471
|
+
return @ob.sqlhelper(d.args, @columns_sqlhelper_args)
|
472
|
+
end
|
473
|
+
|
474
|
+
#Returns the classname of the object without any subclasses.
|
475
|
+
def self.classname
|
476
|
+
return @classname
|
477
|
+
end
|
478
|
+
|
479
|
+
#Sets the classname to something specific in order to hack the behaviour.
|
480
|
+
def self.classname=(newclassname)
|
481
|
+
@classname = newclassname
|
482
|
+
end
|
483
|
+
|
484
|
+
#Returns the table-name that should be used for this datarow.
|
485
|
+
#===Examples
|
486
|
+
# db.query("SELECT * FROM `#{Models::User.table}` WHERE username = 'John Doe'") do |data|
|
487
|
+
# print data[:id]
|
488
|
+
# end
|
489
|
+
def self.table
|
490
|
+
return @table
|
491
|
+
end
|
492
|
+
|
493
|
+
#This can be used to manually set the table-name. Useful when meta-programming classes that extends the datarow-class.
|
494
|
+
#===Examples
|
495
|
+
# Models::User.table = "prefix_User"
|
496
|
+
def self.table=(newtable)
|
497
|
+
@table = newtable
|
498
|
+
@columns_sqlhelper_args[:table] = @table if @columns_sqlhelper_args.is_a?(Hash)
|
499
|
+
end
|
500
|
+
|
501
|
+
#Returns the class-name but without having to call the class-table-method. To make code look shorter.
|
502
|
+
#===Examples
|
503
|
+
# user = ob.get_by(:User, {:username => 'John Doe'})
|
504
|
+
# db.query("SELECT * FROM `#{user.table}` WHERE username = 'John Doe'") do |data|
|
505
|
+
# print data[:id]
|
506
|
+
# end
|
507
|
+
def table
|
508
|
+
return self.class.table
|
509
|
+
end
|
510
|
+
|
511
|
+
#Initializes the object. This should be called from 'Baza::ModelHandler' and not manually.
|
512
|
+
#===Examples
|
513
|
+
# user = ob.get(:User, 3)
|
514
|
+
def initialize(data, args = nil)
|
515
|
+
if data.is_a?(Hash) and data.key?(:id)
|
516
|
+
@data = data
|
517
|
+
@id = @data[:id].to_i
|
518
|
+
elsif data
|
519
|
+
@id = data.to_i
|
520
|
+
|
521
|
+
classname = self.class.classname.to_sym
|
522
|
+
if self.class.ob.ids_cache_should.key?(classname)
|
523
|
+
#ID caching is enabled for this model - dont reload until first use.
|
524
|
+
raise Errno::ENOENT, "ID was not found in cache: '#{id}'." if !self.class.ob.ids_cache[classname].key?(@id)
|
525
|
+
@should_reload = true
|
526
|
+
else
|
527
|
+
#ID caching is not enabled - reload now to check if row exists. Else set 'should_reload'-variable if 'skip_reload' is set.
|
528
|
+
if !args or !args[:skip_reload]
|
529
|
+
self.reload
|
530
|
+
else
|
531
|
+
@should_reload = true
|
532
|
+
end
|
533
|
+
end
|
534
|
+
else
|
535
|
+
raise ArgumentError, "Could not figure out the data from '#{data.class.name}'."
|
536
|
+
end
|
537
|
+
|
538
|
+
if @id.to_i <= 0
|
539
|
+
raise "Invalid ID: '#{@id}' from '#{@data}'." if @data
|
540
|
+
raise "Invalid ID: '#{@id}'."
|
541
|
+
end
|
542
|
+
end
|
543
|
+
|
544
|
+
#Reloads the data from the database.
|
545
|
+
#===Examples
|
546
|
+
# old_username = user[:username]
|
547
|
+
# user.reload
|
548
|
+
# print "The username changed in the database!" if user[:username] != old_username
|
549
|
+
def reload
|
550
|
+
@data = self.class.db.single(self.class.table, {:id => @id})
|
551
|
+
raise Errno::ENOENT, "Could not find any data for the object with ID: '#{@id}' in the table '#{self.class.table}'." if !@data
|
552
|
+
@should_reload = false
|
553
|
+
end
|
554
|
+
|
555
|
+
#Tells the object that it should reloads its data because it has changed. It wont reload before it is required though, which may save you a couple of SQL-calls.
|
556
|
+
#===Examples
|
557
|
+
# obj = _ob.get(:User, 5)
|
558
|
+
# obj.should_reload
|
559
|
+
def should_reload
|
560
|
+
@should_reload = true
|
561
|
+
@data = nil
|
562
|
+
end
|
563
|
+
|
564
|
+
#Returns the data-hash that contains all the data from the database.
|
565
|
+
def data
|
566
|
+
self.reload if @should_reload
|
567
|
+
return @data
|
568
|
+
end
|
569
|
+
|
570
|
+
#Writes/updates new data for the object.
|
571
|
+
#===Examples
|
572
|
+
# user.update(:username => 'New username', :date_changed => Time.now)
|
573
|
+
def update(newdata)
|
574
|
+
self.class.db.update(self.class.table, newdata, {:id => @id})
|
575
|
+
self.should_reload
|
576
|
+
self.class.ob.call("object" => self, "signal" => "update")
|
577
|
+
end
|
578
|
+
|
579
|
+
#Forcefully destroys the object. This is done after deleting it and should not be called manually.
|
580
|
+
def destroy
|
581
|
+
@id = nil
|
582
|
+
@data = nil
|
583
|
+
@should_reload = nil
|
584
|
+
end
|
585
|
+
|
586
|
+
#Returns true if that key exists on the object.
|
587
|
+
#===Examples
|
588
|
+
# print "Looks like the user has a name." if user.key?(:name)
|
589
|
+
def key?(key)
|
590
|
+
self.reload if @should_reload
|
591
|
+
return @data.key?(key.to_sym)
|
592
|
+
end
|
593
|
+
alias has_key? key?
|
594
|
+
|
595
|
+
#Returns true if the object has been deleted.
|
596
|
+
#===Examples
|
597
|
+
# print "That user is deleted." if user.deleted?
|
598
|
+
def deleted?
|
599
|
+
return true if !@data and !@id
|
600
|
+
return false
|
601
|
+
end
|
602
|
+
|
603
|
+
#Returns true if the given object no longer exists in the database. Also destroys the data on the object and sets it to deleted-status, if it no longer exists.
|
604
|
+
#===Examples
|
605
|
+
# print "That user is deleted." if user.deleted_from_db?
|
606
|
+
def deleted_from_db?
|
607
|
+
#Try to avoid db-query if object is already deleted.
|
608
|
+
return true if self.deleted?
|
609
|
+
|
610
|
+
#Try to reload data. Destroy object and return true if the row is gone from the database.
|
611
|
+
begin
|
612
|
+
self.reload
|
613
|
+
return false
|
614
|
+
rescue Errno::ENOENT
|
615
|
+
self.destroy
|
616
|
+
return true
|
617
|
+
end
|
618
|
+
end
|
619
|
+
|
620
|
+
#Returns a specific data from the object by key.
|
621
|
+
# print "Username: #{user[:username]}\n"
|
622
|
+
# print "ID: #{user[:id]}\n"
|
623
|
+
# print "ID again: #{user.id}\n"
|
624
|
+
def [](key)
|
625
|
+
raise "Key was not a symbol: '#{key.class.name}'." if !key.is_a?(Symbol)
|
626
|
+
return @id if !@data and key == :id and @id
|
627
|
+
self.reload if @should_reload
|
628
|
+
raise "No data was loaded on the object? Maybe you are trying to call a deleted object? (#{self.class.classname}(#{@id}), #{@should_reload})" if !@data
|
629
|
+
return @data[key] if @data.key?(key)
|
630
|
+
raise "No such key: '#{key}' on '#{self.class.name}' (#{@data.keys.join(", ")}) (#{@should_reload})."
|
631
|
+
end
|
632
|
+
|
633
|
+
#Writes/updates a keys value on the object.
|
634
|
+
# user = ob.get_by(:User, {"username" => "John Doe"})
|
635
|
+
# user[:username] = 'New username'
|
636
|
+
def []=(key, value)
|
637
|
+
self.update(key.to_sym => value)
|
638
|
+
self.should_reload
|
639
|
+
end
|
640
|
+
|
641
|
+
#Returns the objects ID.
|
642
|
+
def id
|
643
|
+
raise Errno::ENOENT, "This object has been deleted." if self.deleted?
|
644
|
+
raise "No ID on object." if !@id
|
645
|
+
return @id
|
646
|
+
end
|
647
|
+
|
648
|
+
#This enable Wref to not return the wrong object.
|
649
|
+
def __object_unique_id__
|
650
|
+
return 0 if self.deleted?
|
651
|
+
return self.id
|
652
|
+
end
|
653
|
+
|
654
|
+
#Tries to figure out, and returns, the possible name or title for the object.
|
655
|
+
def name
|
656
|
+
self.reload if @should_reload
|
657
|
+
|
658
|
+
if @data.key?(:title)
|
659
|
+
return @data[:title]
|
660
|
+
elsif @data.key?(:name)
|
661
|
+
return @data[:name]
|
662
|
+
end
|
663
|
+
|
664
|
+
obj_methods = self.class.instance_methods(false)
|
665
|
+
[:name, :title].each do |method_name|
|
666
|
+
return self.method(method_name).call if obj_methods.index(method_name)
|
667
|
+
end
|
668
|
+
|
669
|
+
raise "Couldnt figure out the title/name of the object on class #{self.class.name}."
|
670
|
+
end
|
671
|
+
|
672
|
+
#Calls the name-method and returns a HTML-escaped value. Also "[no name]" if the name is empty.
|
673
|
+
def name_html
|
674
|
+
name_str = self.name.to_s
|
675
|
+
name_str = "[no name]" if name_str.length <= 0
|
676
|
+
return name_str
|
677
|
+
end
|
678
|
+
|
679
|
+
alias title name
|
680
|
+
|
681
|
+
#Loops through the data on the object.
|
682
|
+
#===Examples
|
683
|
+
# user = ob.get(:User, 1)
|
684
|
+
# user.each do |key, val|
|
685
|
+
# print "#{key}: #{val}\n" #=> username: John Doe
|
686
|
+
# end
|
687
|
+
def each(*args, &block)
|
688
|
+
self.reload if @should_reload
|
689
|
+
return @data.each(*args, &block)
|
690
|
+
end
|
691
|
+
|
692
|
+
#Hash-compatible.
|
693
|
+
def to_hash
|
694
|
+
self.reload if @should_reload
|
695
|
+
return @data.clone
|
696
|
+
end
|
697
|
+
|
698
|
+
#Returns a default-URL to show the object.
|
699
|
+
def url
|
700
|
+
cname = self.class.classname.to_s.downcase
|
701
|
+
return "?show=#{cname}_show&#{cname}_id=#{self.id}"
|
702
|
+
end
|
703
|
+
|
704
|
+
#Returns the URL for editting the object.
|
705
|
+
def url_edit
|
706
|
+
cname = self.class.classname.to_s.downcase
|
707
|
+
return "?show=#{cname}_edit&#{cname}_id=#{self.id}"
|
708
|
+
end
|
709
|
+
|
710
|
+
#Returns the HTML for making a link to the object.
|
711
|
+
def html(args = nil)
|
712
|
+
if args and args[:edit]
|
713
|
+
url = self.url_edit
|
714
|
+
else
|
715
|
+
url = self.url
|
716
|
+
end
|
717
|
+
|
718
|
+
return "<a href=\"#{Knj::Web.ahref_parse(url)}\">#{self.name_html}</a>"
|
719
|
+
end
|
720
|
+
|
721
|
+
private
|
722
|
+
|
723
|
+
#Various methods to define methods based on the columns for the datarow.
|
724
|
+
def self.define_translation_methods(args)
|
725
|
+
define_method("#{args[:val_dc]}=") do |newtransval|
|
726
|
+
begin
|
727
|
+
_hb.trans_set(self, {
|
728
|
+
args[:val] => newtransval
|
729
|
+
})
|
730
|
+
rescue NameError
|
731
|
+
_kas.trans_set(self, {
|
732
|
+
args[:val] => newtransval
|
733
|
+
})
|
734
|
+
end
|
735
|
+
end
|
736
|
+
|
737
|
+
define_method("#{args[:val_dc]}") do
|
738
|
+
begin
|
739
|
+
return _hb.trans(self, args[:val])
|
740
|
+
rescue NameError
|
741
|
+
return _kas.trans(self, args[:val])
|
742
|
+
end
|
743
|
+
end
|
744
|
+
|
745
|
+
define_method("#{args[:val_dc]}_html") do
|
746
|
+
begin
|
747
|
+
str = _hb.trans(self, args[:val])
|
748
|
+
rescue NameError
|
749
|
+
str = _kas.trans(self, args[:val])
|
750
|
+
end
|
751
|
+
|
752
|
+
if str.to_s.strip.length <= 0
|
753
|
+
return "[no translation for #{args[:val]}]"
|
754
|
+
end
|
755
|
+
|
756
|
+
return str
|
757
|
+
end
|
758
|
+
end
|
759
|
+
|
760
|
+
#Defines the boolean-methods based on enum-columns.
|
761
|
+
def self.define_bool_methods(inst_methods, col_name)
|
762
|
+
#Spawns a method on the class which returns true if the data is 1.
|
763
|
+
if !inst_methods.include?("#{col_name}?".to_sym)
|
764
|
+
define_method("#{col_name}?") do
|
765
|
+
return true if self[col_name.to_sym].to_s == "1"
|
766
|
+
return false
|
767
|
+
end
|
768
|
+
end
|
769
|
+
end
|
770
|
+
|
771
|
+
#Defines date- and time-columns based on datetime- and date-columns.
|
772
|
+
def self.define_date_methods(inst_methods, col_name)
|
773
|
+
if !inst_methods.include?("#{col_name}_str".to_sym)
|
774
|
+
define_method("#{col_name}_str") do |*method_args|
|
775
|
+
if Datet.is_nullstamp?(self[col_name])
|
776
|
+
return self.class.ob.events.call(:no_date, self.class.name)
|
777
|
+
end
|
778
|
+
|
779
|
+
return Datet.in(self[col_name]).out(*method_args)
|
780
|
+
end
|
781
|
+
end
|
782
|
+
|
783
|
+
if !inst_methods.include?(col_name)
|
784
|
+
define_method(col_name) do |*method_args|
|
785
|
+
return false if Datet.is_nullstamp?(self[col_name])
|
786
|
+
return Datet.in(self[col_name])
|
787
|
+
end
|
788
|
+
end
|
789
|
+
end
|
790
|
+
|
791
|
+
#Define various methods based on integer-columns.
|
792
|
+
def self.define_numeric_methods(inst_methods, col_name)
|
793
|
+
if !inst_methods.include?("#{col_name}_format".to_sym)
|
794
|
+
define_method("#{col_name}_format") do |*method_args|
|
795
|
+
return Knj::Locales.number_out(self[col_name], *method_args)
|
796
|
+
end
|
797
|
+
end
|
798
|
+
end
|
799
|
+
|
800
|
+
#Define methods to look up objects directly.
|
801
|
+
#===Examples
|
802
|
+
# user = Models::User.by_username('John Doe')
|
803
|
+
# print user.id
|
804
|
+
def self.define_text_methods(inst_methods, col_name)
|
805
|
+
if !inst_methods.include?("by_#{col_name}".to_sym) and RUBY_VERSION.to_s.slice(0, 3) != "1.8"
|
806
|
+
define_singleton_method("by_#{col_name}") do |arg|
|
807
|
+
return self.class.ob.get_by(self.class.table, {col_name.to_s => arg})
|
808
|
+
end
|
809
|
+
end
|
810
|
+
end
|
811
|
+
|
812
|
+
#Defines dbtime-methods based on time-columns.
|
813
|
+
def self.define_time_methods(inst_methods, col_name)
|
814
|
+
if !inst_methods.include?("#{col_name}_dbt".to_sym)
|
815
|
+
define_method("#{col_name}_dbt") do
|
816
|
+
return Baza::Db::Dbtime.new(self[col_name.to_sym])
|
817
|
+
end
|
818
|
+
end
|
819
|
+
end
|
820
|
+
|
821
|
+
#Memory friendly helper method that defines methods for 'has_many'.
|
822
|
+
def self.define_many_methods(classname, methodname, colname, where_args)
|
823
|
+
define_method(methodname) do |*args, &block|
|
824
|
+
if args and args[0]
|
825
|
+
list_args = args[0]
|
826
|
+
else
|
827
|
+
list_args = {}
|
828
|
+
end
|
829
|
+
|
830
|
+
list_args.merge!(where_args) if where_args
|
831
|
+
list_args[colname.to_s] = self.id
|
832
|
+
|
833
|
+
return self.class.ob.list(classname, list_args, &block)
|
834
|
+
end
|
835
|
+
|
836
|
+
define_method("#{methodname}_count".to_sym) do |*args|
|
837
|
+
list_args = args[0] if args and args[0]
|
838
|
+
list_args = {} if !list_args
|
839
|
+
list_args[colname.to_s] = self.id
|
840
|
+
list_args["count"] = true
|
841
|
+
|
842
|
+
return self.class.ob.list(classname, list_args)
|
843
|
+
end
|
844
|
+
|
845
|
+
define_method("#{methodname}_last".to_sym) do |args|
|
846
|
+
args = {} if !args
|
847
|
+
return self.class.ob.list(classname, {"orderby" => [["id", "desc"]], "limit" => 1}.merge(args))
|
848
|
+
end
|
849
|
+
end
|
850
|
+
|
851
|
+
#Memory friendly helper method that defines methods for 'has_one'.
|
852
|
+
def self.define_one_methods(classname, methodname, colname)
|
853
|
+
define_method(methodname) do
|
854
|
+
return self.class.ob.get_try(self, colname, classname)
|
855
|
+
end
|
856
|
+
|
857
|
+
methodname_html = "#{methodname}_html".to_sym
|
858
|
+
define_method(methodname_html) do |*args|
|
859
|
+
obj = self.__send__(methodname)
|
860
|
+
return self.class.ob.events.call(:no_html, classname) if !obj
|
861
|
+
|
862
|
+
raise "Class '#{classname}' does not have a 'html'-method." if !obj.respond_to?(:html)
|
863
|
+
return obj.html(*args)
|
864
|
+
end
|
865
|
+
|
866
|
+
methodname_name = "#{methodname}_name".to_sym
|
867
|
+
define_method(methodname_name) do |*args|
|
868
|
+
obj = self.__send__(methodname)
|
869
|
+
return self.class.ob.events.call(:no_name, classname) if !obj
|
870
|
+
return obj.name(*args)
|
871
|
+
end
|
872
|
+
end
|
873
|
+
end
|