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,155 +0,0 @@
1
- require "#{$knjpath}event_handler"
2
-
3
- class Baza::ModelCustom
4
- # Used to determine if this is a knj-datarow-object.
5
- def is_knj?
6
- true
7
- end
8
-
9
- # Initializes variables on the class from objects.
10
- def self.datarow_init(d)
11
- @@ob = d.ob
12
- @@db = d.db
13
- end
14
-
15
- def self.has_one(arr)
16
- arr.each do |val|
17
- methodname = nil
18
- colname = nil
19
- classname = nil
20
-
21
- if val.is_a?(Symbol)
22
- classname = val
23
- methodname = val.to_s.downcase.to_sym
24
- colname = "#{val.to_s.downcase}_id".to_sym
25
- elsif val.is_a?(Array)
26
- classname, colname, methodname = *val
27
- elsif val.is_a?(Hash)
28
- classname = val[:class]
29
- colname = val[:col]
30
- methodname = val[:method]
31
- else
32
- raise "Unknown argument-type: '#{arr.class.name}'."
33
- end
34
-
35
- methodname = classname.to_s.downcase unless methodname
36
- colname = "#{classname.to_s.downcase}_id".to_sym unless colname
37
-
38
- define_method(methodname) do
39
- return @@ob.get_try(self, colname, classname)
40
- end
41
-
42
- methodname_html = "#{methodname}_html".to_sym
43
- define_method(methodname_html) do |*args|
44
- obj = send(methodname)
45
- return @@ob.events.call(:no_html, classname) unless obj
46
-
47
- raise "Class '#{classname}' does not have a 'html'-method." unless obj.respond_to?(:html)
48
- return obj.html(*args)
49
- end
50
- end
51
- end
52
-
53
- def self.events
54
- unless @events
55
- @events = Knj::Event_handler.new
56
- @events.add_event(name: :add, connections_max: 1)
57
- @events.add_event(name: :update, connections_max: 1)
58
- @events.add_event(name: :data_from_id, connections_max: 1)
59
- @events.add_event(name: :delete, connections_max: 1)
60
- end
61
-
62
- @events
63
- end
64
-
65
- def self.classname
66
- name.split("::").last
67
- end
68
-
69
- def self.add(d)
70
- @events.call(:add, d)
71
- end
72
-
73
- def self.table
74
- name.split("::").last
75
- end
76
-
77
- def deleted?
78
- return true unless @data
79
- false
80
- end
81
-
82
- def table
83
- self.class.table
84
- end
85
-
86
- def initialize(data, _args)
87
- if data.is_a?(Hash)
88
- @data = Knj::ArrayExt.hash_sym(data)
89
- @id = id
90
- else
91
- @id = data
92
- reload
93
- end
94
- end
95
-
96
- def reload
97
- raise "No 'data_from_id'-event connected to class." unless self.class.events.connected?(:data_from_id)
98
- data = self.class.events.call(:data_from_id, Knj::Hash_methods.new(id: @id))
99
- raise "No data was received from the event: 'data_from_id'." unless data
100
- raise "Data expected to be a hash but wasnt: '#{data.class.name}'." unless data.is_a?(Hash)
101
- @data = Knj::ArrayExt.hash_sym(data)
102
- end
103
-
104
- def update(data)
105
- ret = self.class.events.call(:update, Knj::Hash_methods.new(object: self, data: data))
106
- reload
107
- ret
108
- end
109
-
110
- # Returns a key from the hash that this object is holding or raises an error if it doesnt exist.
111
- def [](key)
112
- raise "No data spawned on object." unless @data
113
- raise "No such key: '#{key}'. Available keys are: '#{@data.keys.sort.join(", ")}'." unless @data.key?(key)
114
- @data[key]
115
- end
116
-
117
- # Returns the ID of the object.
118
- def id
119
- self[:id]
120
- end
121
-
122
- # Returns the name of the object, which can be taken from various data or various defined methods.
123
- def name
124
- if @data.key?(:title)
125
- return @data[:title]
126
- elsif @data.key?(:name)
127
- return @data[:name]
128
- end
129
-
130
- obj_methods = self.class.instance_methods(false)
131
- [:name, :title].each do |method_name|
132
- return method(method_name).call if obj_methods.index(method_name)
133
- end
134
-
135
- raise "Couldnt figure out the title/name of the object on class #{self.class.name}."
136
- end
137
-
138
- alias_method :title, :name
139
-
140
- def delete
141
- self.class.events.call(:delete, Knj::Hash_methods.new(object: self))
142
- end
143
-
144
- def destroy
145
- @data = nil
146
- end
147
-
148
- def each(&args)
149
- @data.each(&args)
150
- end
151
-
152
- def to_hash
153
- @data.clone
154
- end
155
- end
@@ -1,910 +0,0 @@
1
- require "#{File.dirname(__FILE__)}/model_handler_sqlhelper.rb"
2
- require "string-cases"
3
-
4
- class Baza::ModelHandler
5
- attr_reader :args, :events, :data, :ids_cache, :ids_cache_should
6
-
7
- def initialize(args)
8
- require "array_enumerator" if args[:array_enum]
9
- require "event_handler"
10
- require "monitor"
11
- require "ostruct"
12
-
13
- @callbacks = {}
14
- @args = args
15
- @args[:col_id] = :id unless @args[:col_id]
16
- @args[:class_pre] = "class_" unless @args[:class_pre]
17
- @args[:module] = Kernel unless @args[:module]
18
- @args[:cache] = :weak unless @args.key?(:cache)
19
- @objects = {}
20
- @locks = {}
21
- @data = {}
22
- @lock_require = Monitor.new
23
-
24
- # Set up various events.
25
- @events = EventHandler.new
26
- @events.add_event(name: :no_html, connections_max: 1)
27
- @events.add_event(name: :no_name, connections_max: 1)
28
- @events.add_event(name: :no_date, connections_max: 1)
29
- @events.add_event(name: :missing_class, connections_max: 1)
30
- @events.add_event(name: :require_class, connections_max: 1)
31
-
32
- raise "No DB given." if !@args[:db] && !@args[:custom]
33
- raise "No class path given." if !@args[:class_path] && (@args[:require] || !@args.key?(:require))
34
-
35
- if args[:require_all]
36
- loads = []
37
-
38
- Dir.foreach(@args[:class_path]) do |file|
39
- next if file == "." || file == ".." || !file.match(/\.rb$/)
40
- file_parsed = file
41
- file_parsed.gsub!(@args[:class_pre], "") if @args.key?(:class_pre)
42
- file_parsed.gsub!(/\.rb$/, "")
43
- file_parsed = StringCases.snake_to_camel(file_parsed)
44
-
45
- loads << file_parsed
46
- requireclass(file_parsed, load: false)
47
- end
48
-
49
- loads.each do |load_class|
50
- self.load_class(load_class)
51
- end
52
- end
53
-
54
- # Set up ID-caching.
55
- @ids_cache_should = {}
56
-
57
- return unless @args[:models]
58
-
59
- @ids_cache = {}
60
-
61
- @args[:models].each do |classname, classargs|
62
- @ids_cache_should[classname] = true if classargs[:cache_ids]
63
- cache_ids(classname)
64
- end
65
- end
66
-
67
- # Caches all IDs for a specific classname.
68
- def cache_ids(classname)
69
- classname = classname.to_sym
70
- return nil if !@ids_cache_should || !@ids_cache_should[classname]
71
-
72
- newcache = {}
73
- @args[:db].q("SELECT `#{@args[:col_id]}` FROM `#{classname}` ORDER BY `#{@args[:col_id]}`") do |data|
74
- newcache[data[@args[:col_id]].to_i] = true
75
- end
76
-
77
- @ids_cache[classname] = newcache
78
- end
79
-
80
- def init_class(classname)
81
- classname = classname.to_sym
82
- return false if @objects.key?(classname)
83
-
84
- if @args[:cache] == :weak
85
- @objects[classname] = Wref::Map.new
86
- else
87
- @objects[classname] = {}
88
- end
89
-
90
- @locks[classname] = Monitor.new
91
- end
92
-
93
- def uninit_class(classname)
94
- @objects.delete(classname)
95
- @locks.delete(classname)
96
- end
97
-
98
- # Returns a cloned version of the @objects variable. Cloned because iteration on it may crash some of the other methods in Ruby 1.9+
99
- def objects
100
- objs_cloned = {}
101
-
102
- @objects.keys.each do |key|
103
- objs_cloned[key] = @objects[key].clone
104
- end
105
-
106
- objs_cloned
107
- end
108
-
109
- # Returns the database-connection used by this instance of Objects.
110
- def db
111
- @args[:db]
112
- end
113
-
114
- # Returns the total count of objects currently held by this instance.
115
- def count_objects
116
- count = 0
117
- @objects.keys.each do |key|
118
- count += @objects[key].length
119
- end
120
-
121
- count
122
- end
123
-
124
- # This connects a block to an event. When the event is called the block will be executed.
125
- def connect(args, &block)
126
- raise "No object given." unless args["object"]
127
- raise "No signals given." if !args.key?("signal") && !args.key?("signals")
128
- args["block"] = block if block_given?
129
- object = args["object"].to_sym
130
-
131
- @callbacks[object] = {} unless @callbacks[object]
132
- conn_id = @callbacks[object].length.to_s
133
- @callbacks[object][conn_id] = args
134
- conn_id
135
- end
136
-
137
- # Returns true if the given signal is connected to the given object.
138
- def connected?(args)
139
- raise "No object given." unless args["object"]
140
- raise "No signal given." unless args.key?("signal")
141
- object = args["object"].to_sym
142
-
143
- if @callbacks.key?(object)
144
- @callbacks[object].clone.each do |_ckey, callback|
145
- return true if callback.key?("signal") && callback["signal"].to_s == args["signal"].to_s
146
- return true if callback.key?("signals") && (callback["signals"].include?(args["signal"].to_s) || callback["signals"].include?(args["signal"].to_sym))
147
- end
148
- end
149
-
150
- false
151
- end
152
-
153
- # Unconnects a connect by 'object' and 'conn_id'.
154
- def unconnect(args)
155
- raise ArgumentError, "No object given." unless args["object"]
156
- object = args["object"].to_sym
157
- raise ArgumentError, "Object doesnt exist: '#{object}'." unless @callbacks.key?(object)
158
-
159
- if args["conn_id"]
160
- conn_ids = [args["conn_id"]]
161
- elsif args["conn_ids"]
162
- conn_ids = args["conn_ids"]
163
- else
164
- raise ArgumentError, "Could not figure out connection IDs."
165
- end
166
-
167
- conn_ids.each do |conn_id|
168
- raise Errno::ENOENT, "Conn ID doest exist: '#{conn_id}' (#{args})." unless @callbacks[object].key?(conn_id)
169
- @callbacks[object].delete(conn_id)
170
- end
171
- end
172
-
173
- # This method is used to call the connected callbacks for an event.
174
- def call(args, &_block)
175
- classstr = args["object"].class.classname.to_sym
176
-
177
- return unless @callbacks.key?(classstr)
178
-
179
- @callbacks[classstr].clone.each do |_callback_key, callback|
180
- docall = false
181
-
182
- if callback.key?("signal") && args.key?("signal") && callback["signal"].to_s == args["signal"].to_s
183
- docall = true
184
- elsif callback["signals"] && args["signal"] && (callback["signals"].include?(args["signal"].to_s) || callback["signals"].include?(args["signal"].to_sym))
185
- docall = true
186
- end
187
-
188
- next unless docall
189
-
190
- if callback["block"]
191
- callargs = []
192
- arity = callback["block"].arity
193
- if arity <= 0
194
- # do nothing
195
- elsif arity == 1
196
- callargs << args["object"]
197
- else
198
- raise "Unknown number of arguments: #{arity}"
199
- end
200
-
201
- callback["block"].call(*callargs)
202
- else
203
- raise "No valid callback given."
204
- end
205
- end
206
- end
207
-
208
- def requireclass(classname, args = {})
209
- classname = classname.to_sym
210
- return false if @objects.key?(classname)
211
- classname_snake = StringCases.camel_to_snake(classname)
212
-
213
- @lock_require.synchronize do
214
- # Maybe the classname got required meanwhile the synchronized wait - check again.
215
- return false if @objects.key?(classname)
216
-
217
- if @events.connected?(:require_class)
218
- @events.call(:require_class, class: classname)
219
- else
220
- doreq = false
221
-
222
- if args[:require]
223
- doreq = true
224
- elsif args.key?(:require) && !args[:require]
225
- doreq = false
226
- elsif @args[:require] || !@args.key?(:require)
227
- doreq = true
228
- end
229
-
230
- if doreq
231
- filename = "#{@args[:class_path]}/#{@args[:class_pre]}#{classname_snake}.rb"
232
- filename_req = "#{@args[:class_path]}/#{@args[:class_pre]}#{classname_snake}"
233
- raise "Class file could not be found: #{filename}." unless File.exist?(filename)
234
- require filename_req
235
- end
236
- end
237
-
238
- if args[:class]
239
- classob = args[:class]
240
- else
241
- begin
242
- classob = @args[:module].const_get(classname)
243
- rescue NameError => e
244
- if @events.connected?(:missing_class)
245
- @events.call(:missing_class, class: classname)
246
- classob = @args[:module].const_get(classname)
247
- else
248
- raise e
249
- end
250
- end
251
- end
252
-
253
- if (classob.respond_to?(:load_columns) || classob.respond_to?(:datarow_init)) && (!args.key?(:load) || args[:load])
254
- load_class(classname, args)
255
- end
256
-
257
- init_class(classname)
258
- end
259
- end
260
-
261
- # Loads a Datarow-class by calling various static methods.
262
- def load_class(classname, args = {})
263
- if args[:class]
264
- classob = args[:class]
265
- else
266
- classob = @args[:module].const_get(classname)
267
- end
268
-
269
- pass_arg = OpenStruct.new(ob: self, db: @args[:db])
270
- classob.load_columns(pass_arg) if classob.respond_to?(:load_columns)
271
- classob.datarow_init(pass_arg) if classob.respond_to?(:datarow_init)
272
- end
273
-
274
- # Returns the instance of classname, but only if it already exists.
275
- def get_if_cached(classname, id)
276
- classname = classname.to_sym
277
- id = id.to_i
278
-
279
- if wref_map = @objects[classname] && obj = wref_map.get(id)
280
- return obj
281
- end
282
-
283
- nil
284
- end
285
-
286
- # Returns true if a row of the given classname and the ID exists. Will use ID-cache if set in arguments and spawned otherwise it will do an actual lookup.
287
- #===Examples
288
- # print "User 5 exists." if ob.exists?(:User, 5)
289
- def exists?(classname, id)
290
- # Make sure the given data are in the correct types.
291
- classname = classname.to_sym
292
- id = id.to_i
293
-
294
- # Check if ID-cache is enabled for that classname. Avoid SQL-lookup by using that.
295
- if @ids_cache_should.key?(classname)
296
- if @ids_cache[classname].key?(id)
297
- return true
298
- else
299
- return false
300
- end
301
- end
302
-
303
- # If the object currently exists in cache, we dont have to do a lookup either.
304
- return true if @objects.key?(classname) && (obj = @objects[classname].get(id)) && !obj.deleted?
305
-
306
- # Okay - no other options than to actually do a real lookup.
307
- begin
308
- table = @args[:module].const_get(classname).table
309
- row = @args[:db].single(table, @args[:col_id] => id)
310
-
311
- if row
312
- return true
313
- else
314
- return false
315
- end
316
- rescue Errno::ENOENT
317
- return false
318
- end
319
- end
320
-
321
- # Gets an object from the ID or the full data-hash in the database.
322
- #===Examples
323
- # inst = ob.get(:User, 5)
324
- def get(classname, data, args = nil)
325
- classname = classname.to_sym
326
-
327
- if data.is_a?(Integer) || data.is_a?(String) || data.is_a?(Fixnum)
328
- id = data.to_i
329
- elsif data.is_a?(Hash) && data.key?(@args[:col_id].to_sym)
330
- id = data[@args[:col_id].to_sym].to_i
331
- elsif data.is_a?(Hash) && data.key?(@args[:col_id].to_s)
332
- id = data[@args[:col_id].to_s].to_i
333
- else
334
- raise ArgumentError, "Unknown data for class '#{classname}': '#{data.class}' (#{data})."
335
- end
336
-
337
- if @objects.key?(classname)
338
- case @args[:cache]
339
- when :weak
340
- if (obj = @objects[classname].get(id)) && obj.id.to_i == id
341
- return obj
342
- end
343
- else
344
- return @objects[classname][id] if @objects[classname].key?(id)
345
- end
346
- end
347
-
348
- requireclass(classname) unless @objects.key?(classname)
349
-
350
- @locks[classname].synchronize do
351
- # Maybe the object got spawned while we waited for the lock? If so we shouldnt spawn another instance.
352
- if @args[:cache] == :weak && obj = @objects[classname].get(id) && obj.id.to_i == id
353
- return obj
354
- end
355
-
356
- # Spawn object.
357
- obj = @args[:module].const_get(classname).new(data, args)
358
-
359
- # Save object in cache.
360
- case @args[:cache]
361
- when :none
362
- return obj
363
- else
364
- @objects[classname][id] = obj
365
- return obj
366
- end
367
- end
368
-
369
- raise "Unexpected run?"
370
- end
371
-
372
- # Same as normal get but returns false if not found instead of raising error.
373
- def get!(*args, &block)
374
- return get(*args, &block)
375
- rescue Errno::ENOENT
376
- return false
377
- end
378
-
379
- def object_finalizer(id)
380
- classname = @objects_idclass[id]
381
- return unless classname
382
- @objects[classname].delete(id)
383
- @objects_idclass.delete(id)
384
- end
385
-
386
- # Returns the first object found from the given arguments. Also automatically limits the results to 1.
387
- def get_by(classname, args = {})
388
- classname = classname.to_sym
389
- requireclass(classname)
390
- classob = @args[:module].const_get(classname)
391
-
392
- raise "list-function has not been implemented for '#{classname}'." unless classob.respond_to?(:list)
393
-
394
- args["limit"] = 1
395
- list(classname, args) do |obj|
396
- return obj
397
- end
398
-
399
- false
400
- end
401
-
402
- # Searches for an object with the given data. If not found it creates it. Returns the found or created object in the end.
403
- def get_or_add(classname, data, _args = nil)
404
- obj = get_by(classname, data.clone)
405
- obj = add(classname, data) unless obj
406
- obj
407
- end
408
-
409
- def get_try(obj, col_name, obj_name = nil)
410
- unless obj_name
411
- if match = col_name.to_s.match(/^(.+)_id$/)
412
- obj_name = Php4r.ucwords(match[1]).to_sym
413
- else
414
- raise "Could not figure out objectname for: #{col_name}."
415
- end
416
- end
417
-
418
- id_data = obj[col_name].to_i
419
- return false if id_data.to_i <= 0
420
-
421
- begin
422
- return get(obj_name, id_data)
423
- rescue Errno::ENOENT
424
- return false
425
- end
426
- end
427
-
428
- # Returns an array-list of objects. If given a block the block will be called for each element and memory will be spared if running weak-link-mode.
429
- #===Examples
430
- # ob.list(:User) do |user|
431
- # print "Username: #{user.name}\n"
432
- # end
433
- def list(classname, args = {}, &block)
434
- args = {} if args == nil
435
- classname = classname.to_sym
436
- requireclass(classname)
437
- classob = @args[:module].const_get(classname)
438
-
439
- raise "list-function has not been implemented for '#{classname}'." unless classob.respond_to?("list")
440
- ret = classob.list(OpenStruct.new(args: args, ob: self, db: @args[:db]), &block)
441
-
442
- # If 'ret' is an array and a block is given then the list-method didnt return blocks. We emulate it instead with the following code.
443
- if block && ret.is_a?(Array)
444
- ret.each do |obj|
445
- block.call(obj)
446
- end
447
- return nil
448
- elsif block && !ret.nil?
449
- raise "Return should return nil because of block but didnt. It wasnt an array either..."
450
- elsif block
451
- return nil
452
- else
453
- return ret
454
- end
455
- end
456
-
457
- # Yields every object that is missing certain required objects (based on 'has_many' required-argument).
458
- def list_invalid_required(args, &block)
459
- enum = Enumerator.new do |yielder|
460
- classname = args[:class]
461
- classob = @args[:module].const_get(classname)
462
- required_data = classob.required_data
463
-
464
- if required_data && !required_data.empty?
465
- required_data.each do |req_data|
466
- list(args[:class]) do |obj|
467
- puts "Checking #{obj.classname}(#{obj.id}) for required #{req_data[:class]}." if args[:debug]
468
- id = obj[req_data[:col]]
469
-
470
- begin
471
- raise Errno::ENOENT unless id
472
- obj_req = get(req_data[:class], id)
473
- rescue Errno::ENOENT
474
- yielder << {obj: obj, type: :required, id: id, data: req_data}
475
- end
476
- end
477
- end
478
- end
479
- end
480
-
481
- if block
482
- enum.each(&block)
483
- else
484
- return ArrayEnumerator.new(enum)
485
- end
486
- end
487
-
488
- # Returns select-options-HTML for inserting into a HTML-select-element.
489
- def list_opts(classname, args = {})
490
- Knj::ArrayExt.hash_sym(args)
491
- classname = classname.to_sym
492
-
493
- if args[:list_args].is_a?(Hash)
494
- list_args = args[:list_args]
495
- else
496
- list_args = {}
497
- end
498
-
499
- html = ""
500
-
501
- if args[:addnew] || args[:add]
502
- html << "<option"
503
- html << " selected=\"selected\"" unless args[:selected]
504
- html << " value=\"\">#{_("Add new")}</option>"
505
- elsif args[:none]
506
- html << "<option"
507
- html << " selected=\"selected\"" unless args[:selected]
508
- html << " value=\"\">#{_("None")}</option>"
509
- end
510
-
511
- list(classname, args[:list_args]) do |object|
512
- html << "<option value=\"#{object.id.html}\""
513
-
514
- selected = false
515
- if args[:selected].is_a?(Array) && !args[:selected].index(object).nil?
516
- selected = true
517
- elsif args[:selected] && args[:selected].respond_to?("is_knj?") && args[:selected].id.to_s == object.id.to_s
518
- selected = true
519
- end
520
-
521
- html << " selected=\"selected\"" if selected
522
-
523
- obj_methods = object.class.instance_methods(false)
524
-
525
- begin
526
- if !obj_methods.index("name").nil? || !obj_methods.index(:name).nil?
527
- objhtml = object.name.html
528
- elsif !obj_methods.index("title").nil? || !obj_methods.index(:title).nil?
529
- objhtml = object.title.html
530
- elsif object.respond_to?(:data)
531
- obj_data = object.data
532
-
533
- if obj_data.key?(:name)
534
- objhtml = obj_data[:name]
535
- elsif obj_data.key?(:title)
536
- objhtml = obj_data[:title]
537
- end
538
- else
539
- objhtml = ""
540
- end
541
-
542
- raise "Could not figure out which name-method to call?" unless objhtml
543
- html << ">#{objhtml}</option>"
544
- rescue => e
545
- html << ">[#{object.class.name}: #{e.message}]</option>"
546
- end
547
- end
548
-
549
- html
550
- end
551
-
552
- # Returns a hash which can be used to generate HTML-select-elements.
553
- def list_optshash(classname, args = {})
554
- classname = classname.to_sym
555
-
556
- if args[:list_args].is_a?(Hash)
557
- list_args = args[:list_args]
558
- else
559
- list_args = {}
560
- end
561
-
562
- list = {}
563
-
564
- if args[:addnew] || args[:add]
565
- list["0"] = _("Add new")
566
- elsif args[:choose]
567
- list["0"] = _("Choose") + ":"
568
- elsif args[:all]
569
- list["0"] = _("All")
570
- elsif args[:none]
571
- list["0"] = _("None")
572
- end
573
-
574
- self.list(classname, args[:list_args]) do |object|
575
- if object.respond_to?(:name)
576
- list[object.id] = object.name
577
- elsif object.respond_to?(:title)
578
- list[object.id] = object.title
579
- else
580
- raise "Object of class '#{object.class.name}' doesnt support 'name' or 'title."
581
- end
582
- end
583
-
584
- list
585
- end
586
-
587
- # Returns a list of a specific object by running specific SQL against the database.
588
- def list_bysql(classname, sql, args = nil, &block)
589
- classname = classname.to_sym
590
- ret = [] unless block
591
- qargs = nil
592
-
593
- if args
594
- args.each do |key, _val|
595
- case key
596
- when :cloned_ubuf
597
- qargs = {cloned_ubuf: true}
598
- else
599
- raise "Invalid key: '#{key}'."
600
- end
601
- end
602
- end
603
-
604
- if @args[:array_enum]
605
- enum = Enumerator.new do |yielder|
606
- @args[:db].q(sql, qargs) do |d_obs|
607
- yielder << get(classname, d_obs)
608
- end
609
- end
610
-
611
- if block
612
- enum.each(&block)
613
- return nil
614
- else
615
- return ArrayEnumerator.new(enum)
616
- end
617
- else
618
- @args[:db].q(sql, qargs) do |d_obs|
619
- if block
620
- block.call(get(classname, d_obs))
621
- else
622
- ret << get(classname, d_obs)
623
- end
624
- end
625
-
626
- if !block
627
- return ret
628
- else
629
- return nil
630
- end
631
- end
632
- end
633
-
634
- # Add a new object to the database and to the cache.
635
- #===Examples
636
- # obj = ob.add(:User, {:username => "User 1"})
637
- def add(classname, data = {}, args = nil)
638
- raise "data-variable was not a hash: '#{data.class.name}'." unless data.is_a?(Hash)
639
- classname = classname.to_sym
640
- requireclass(classname)
641
-
642
- if @args[:custom]
643
- classobj = @args[:module].const_get(classname)
644
- retob = classobj.add(OpenStruct.new(
645
- ob: self,
646
- data: data
647
- ))
648
- else
649
- classobj = @args[:module].const_get(classname)
650
-
651
- # Run the class 'add'-method to check various data.
652
- classobj.add(OpenStruct.new(ob: self, db: @args[:db], data: data)) if classobj.respond_to?(:add)
653
-
654
- # Check if various required data is given. If not then raise an error telling about it.
655
- required_data = classobj.required_data
656
- required_data.each do |req_data|
657
- raise "No '#{req_data[:class]}' given by the data '#{req_data[:col]}'." unless data.key?(req_data[:col])
658
- raise "The '#{req_data[:class]}' by ID '#{data[req_data[:col]]}' could not be found with the data '#{req_data[:col]}'." unless self.exists?(req_data[:class], data[req_data[:col]])
659
- end
660
-
661
- # If 'skip_ret' is given, then the ID wont be looked up and the object wont be spawned. Be aware the connected events wont be executed either. In return it will go a lot faster.
662
- if args && args[:skip_ret] && !@ids_cache_should.key?(classname)
663
- ins_args = nil
664
- else
665
- ins_args = {return_id: true}
666
- end
667
-
668
- # Insert and (maybe?) get ID.
669
- ins_id = @args[:db].insert(classobj.table, data, ins_args).to_i
670
-
671
- # Add ID to ID-cache if ID-cache is active for that classname.
672
- @ids_cache[classname][ins_id] = true if ins_id != 0 && @ids_cache_should.key?(classname)
673
-
674
- # Skip the rest if we are told not to return result.
675
- return nil if args && args[:skip_ret]
676
-
677
- # Spawn the object.
678
- retob = get(classname, ins_id, skip_reload: true)
679
- end
680
-
681
- call("object" => retob, "signal" => "add")
682
- retob.send(:add_after, {}) if retob.respond_to?(:add_after)
683
-
684
- retob
685
- end
686
-
687
- # Adds several objects to the database at once. This is faster than adding every single object by itself, since this will do multi-inserts if supported by the database.
688
- #===Examples
689
- # ob.adds(:User, [{:username => "User 1"}, {:username => "User 2"})
690
- def adds(classname, datas)
691
- if @args[:module].const_get(classname).respond_to?(:add)
692
- datas.each do |data|
693
- @args[:module].const_get(classname).add(OpenStruct.new(
694
- ob: self,
695
- db: db,
696
- data: data
697
- ))
698
- end
699
- end
700
-
701
- db.insert_multi(classname, datas)
702
- cache_ids(classname)
703
- end
704
-
705
- # Calls a static method on a class. Passes the d-variable which contains the Objects-object, database-reference and more...
706
- def static(class_name, method_name, *args, &block)
707
- class_name = class_name
708
- method_name = method_name
709
-
710
- requireclass(class_name)
711
- class_obj = @args[:module].const_get(class_name)
712
-
713
- # Sometimes this raises the exception but actually responds to the class? Therefore commented out. - knj
714
- # raise "The class '#{class_obj.name}' has no such method: '#{method_name}' (#{class_obj.methods.sort.join(", ")})." if !class_obj.respond_to?(method_name)
715
-
716
- pass_args = [OpenStruct.new(ob: self, db: db)]
717
-
718
- args.each do |arg|
719
- pass_args << arg
720
- end
721
-
722
- class_obj.send(method_name, *pass_args, &block)
723
- end
724
-
725
- # Unset object. Do this if you are sure, that there are no more references left. This will be done automatically when deleting it.
726
- def unset(object)
727
- if object.is_a?(Array)
728
- object.each do |obj|
729
- unset(obj)
730
- end
731
- return nil
732
- end
733
-
734
- classname = object.class.name
735
-
736
- classname = classname.gsub(@args[:module].name + "::", "") if @args[:module]
737
-
738
- classname = classname.to_sym
739
- @objects[classname].delete(object.id.to_i)
740
- end
741
-
742
- def unset_class(classname)
743
- if classname.is_a?(Array)
744
- classname.each do |classn|
745
- unset_class(classn)
746
- end
747
-
748
- return false
749
- end
750
-
751
- classname = classname.to_sym
752
-
753
- return false unless @objects.key?(classname)
754
- @objects.delete(classname)
755
- end
756
-
757
- # Delete an object. Both from the database and from the cache.
758
- #===Examples
759
- # user = ob.get(:User, 1)
760
- # ob.delete(user)
761
- def delete(object, args = nil)
762
- # Return false if the object has already been deleted.
763
- return false if object.deleted?
764
- classname = object.class.classname.to_sym
765
-
766
- call("object" => object, "signal" => "delete_before")
767
- unset(object)
768
- obj_id = object.id
769
- object.delete if object.respond_to?(:delete)
770
-
771
- # If autodelete is set by 'has_many'-method, go through it and delete the various objects first.
772
- if autodelete_data = object.class.autodelete_data
773
- autodelete_data.each do |adel_data|
774
- list(adel_data[:classname], adel_data[:colname].to_s => object.id) do |obj_del|
775
- delete(obj_del, args)
776
- end
777
- end
778
- end
779
-
780
- # If depend is set by 'has_many'-method, check if any objects exists and raise error if so.
781
- if dep_datas = object.class.depending_data
782
- dep_datas.each do |dep_data|
783
- if obj = get_by(dep_data[:classname], dep_data[:colname].to_s => object.id)
784
- raise "Cannot delete <#{object.class.name}:#{object.id}> because <#{obj.class.name}:#{obj.id}> depends on it."
785
- end
786
- end
787
- end
788
-
789
- # If autozero is set by 'has_many'-method, check if any objects exists and set the ID to zero.
790
- if autozero_datas = object.class.autozero_data
791
- autozero_datas.each do |zero_data|
792
- list(zero_data[:classname], zero_data[:colname].to_s => object.id) do |obj_zero|
793
- obj_zero[zero_data[:colname].to_sym] = 0
794
- end
795
- end
796
- end
797
-
798
- # Delete any translations that has been set on the object by 'has_translation'-method.
799
- if object.class.translations
800
- begin
801
- _hb.trans_del(object)
802
- rescue NameError
803
- _kas.trans_del(object)
804
- end
805
- end
806
-
807
-
808
- # If a buffer is given in arguments, then use that to delete the object.
809
- if args && buffer = args[:db_buffer]
810
- buffer.delete(object.table, id: obj_id)
811
- else
812
- @args[:db].delete(object.table, id: obj_id)
813
- end
814
-
815
- @ids_cache[classname].delete(obj_id.to_i) if @ids_cache_should.key?(classname)
816
- call("object" => object, "signal" => "delete")
817
- object.destroy
818
- nil
819
- end
820
-
821
- # Deletes several objects as one. If running datarow-mode it checks all objects before it starts to actually delete them. Its faster than deleting every single object by itself...
822
- def deletes(objs)
823
- tables = {}
824
-
825
- begin
826
- objs.each do |obj|
827
- next if obj.deleted?
828
- tablen = obj.table
829
-
830
- tables[tablen] = [] unless tables.key?(tablen)
831
-
832
- tables[tablen] << obj.id
833
- obj.delete if obj.respond_to?(:delete)
834
-
835
- # Remove from ID-cache.
836
- classname = obj.class.classname.to_sym
837
- @ids_cache[classname].delete(obj.id.to_i) if @ids_cache_should.key?(classname)
838
-
839
- # Unset any data on the object, so it seems deleted.
840
- obj.destroy
841
- end
842
- ensure
843
- # An exception may occur, and we should make sure, that objects that has gotten 'delete' called also are deleted from their tables.
844
- tables.each do |table, ids|
845
- ids.each_slice(1000) do |ids_slice|
846
- @args[:db].delete(table, id: ids_slice)
847
- end
848
- end
849
- end
850
- end
851
-
852
- # Deletes all objects with the given IDs 500 at a time to prevent memory exhaustion or timeout.
853
- #===Examples
854
- # ob.delete_ids(:class => :Person, :ids => [1, 3, 5, 6, 7, 8, 9])
855
- def delete_ids(args)
856
- while !args[:ids].empty? && (ids = args[:ids].shift(500))
857
- objs = list(args[:class], "id" => ids)
858
- deletes(objs)
859
- end
860
-
861
- nil
862
- end
863
-
864
- # Try to clean up objects by unsetting everything, start the garbagecollector, get all the remaining objects via ObjectSpace and set them again. Some (if not all) should be cleaned up and our cache should still be safe... dirty but works.
865
- def clean(classn)
866
- if classn.is_a?(Array)
867
- classn.each do |realclassn|
868
- clean(realclassn)
869
- end
870
-
871
- return nil
872
- end
873
-
874
- if @args[:cache] == :weak
875
- @objects[classn].clean
876
- elsif @args[:cache] == :none
877
- return false
878
- else
879
- return false unless @objects.key?(classn)
880
- @objects[classn] = {}
881
- GC.start
882
-
883
- @objects.keys.each do |classname|
884
- data = @objects[classname]
885
- classobj = @args[:module].const_get(classname)
886
- ObjectSpace.each_object(classobj) do |obj|
887
- begin
888
- data[obj.id.to_i] = obj
889
- rescue => e
890
- if e.message == "No data on object."
891
- # Object has been unset - skip it.
892
- next
893
- end
894
-
895
- raise e
896
- end
897
- end
898
- end
899
- end
900
- end
901
-
902
- # Erases the whole cache and regenerates it from ObjectSpace if not running weak-link-caching. If running weaklink-caching then it will only removes the dead links.
903
- def clean_all
904
- clean(@objects.keys)
905
- end
906
-
907
- def classes_loaded
908
- @objects.keys
909
- end
910
- end