baza 0.0.20 → 0.0.21

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.
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