baza 0.0.20 → 0.0.21

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