kit 0.3.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,21 +0,0 @@
1
- The MIT License
2
-
3
- Copyright (c) 2011 Evan Boyd Sosenko
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in
13
- all copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
- THE SOFTWARE.
@@ -1,173 +0,0 @@
1
- # Class to manage individual bits.
2
- class Bit < Kit
3
-
4
- # Exceptions.
5
-
6
- # Raised when element exists but was not expected.
7
- class DuplicateElement < RuntimeError
8
- end
9
-
10
- # Raised when no element exists.
11
- class NoElement < RuntimeError
12
- end
13
-
14
- # Raised when not enough info is given to perform the operation.
15
- class MissingValues < RuntimeError
16
- end
17
-
18
- # Raised when an action runs but fails due to an invalid combination of options.
19
- class InvalidAction < RuntimeError
20
- end
21
-
22
- # Raised when an action runs but encounters a fatal error.
23
- class FailedAction < RuntimeError
24
- end
25
-
26
- attr_reader :id
27
-
28
- # Loads all bit info from database, or attempts to add new bit to database.
29
- # Raises a TypeError if not given an integer or hash.
30
- # @param [Integer, Hash] info id of bit or info for new bit
31
- def initialize info
32
- @id = \
33
- if info.is_a? Integer
34
- info
35
- elsif info.is_a? Hash
36
- info.each do |key, value|
37
- instance_variable_set "@#{key}", value
38
- end
39
- g = @group_name if @group_name
40
- g = @group_id if @group_id
41
- self.group = g, info
42
- insert_new
43
- else
44
- fail TypeError
45
- end
46
- load_info
47
- end
48
-
49
- private
50
-
51
- # Add support fot the dynamic instance variables.
52
- class ::Array
53
- # Makes a hash that maps symbols in the array to instance variable values.
54
- # @param [Object] obj the object with the instance variables to use
55
- # @param [Array] exclude instance variables named in this array as symbols are excluded from the hash
56
- # @return [Hash] mapping of symbols in the array to instance variable values
57
- def hash_ivars obj, exclude = []
58
- h = {}
59
- self.each do |x|
60
- h[x] = obj.instance_variable_get "@#{x}" unless exclude.include? x
61
- end
62
- return h
63
- end
64
- end
65
-
66
- # Set group by name or id.
67
- # @param [Integer, String] group id or info for group
68
- def group= group
69
- @group_id = group if group.is_a? Integer
70
- @group_name = group if group.is_a? String
71
-
72
- if @group_id.nil?
73
- g = @@db.select_info_by_name :groups, [ :rowid ], @group_name
74
- @group_id = if g.nil? then insert_new_group else g[:rowid] end
75
- end
76
- end
77
-
78
- # Inserts a new bit into the database using the availible instance variables.
79
- # Raises a MissingValues if required values are not given.
80
- # Raises a DuplicateElement if a bit with unique values already exists.
81
- def insert_new
82
- uniq = @@unique[:bits].hash_ivars self
83
- uniq.each { |x| fail MissingValues if x.nil? }
84
-
85
- fail DuplicateElement if ( lookup_id uniq ).first
86
-
87
- data = @@info[:bits].hash_ivars self, [ :rowid ]
88
- @@db.insert_info :bits, data
89
- end
90
-
91
- # (see #insert_new)
92
- def insert_new_group
93
- fail MissingValues if @group_name.nil?
94
- fail DuplicateElement if @@db.select_info_by_name :groups, @@unique[:groups], @group_name
95
-
96
- data = @@info[:groups].hash_ivars self, [ :rowid, :name ]
97
- data[:name] = @group_name
98
-
99
- @@db.insert_info :groups, data
100
- end
101
-
102
- # Loads all bit info from the database into instance variables.
103
- def load_info
104
- fail MissingValues if @id.nil?
105
- info = @@db.select_info_by_id :bits, @@info[:bits], @id
106
- fail NoElement unless info
107
-
108
- group_info = @@db.select_info_by_id :groups, @@info[:groups], info[:group_id]
109
- info[:group_name] = group_info[:name]
110
- group_info.delete :rowid
111
- group_info.delete :name
112
-
113
- info.merge! group_info
114
- info.delete :rowid
115
-
116
- info.each do |key, value|
117
- instance_variable_set "@#{key}", value
118
- self.class.send :attr_reader, key
119
- self.class.send :public, key
120
- end
121
- end
122
-
123
- public
124
- # Add a task to the array of pending tasks.
125
- # @param [Hash] task info for task
126
- def queue_task task
127
- @tasks = [] unless @tasks
128
- @tasks << task
129
- end
130
-
131
- # def clear_task task
132
- # action = task[:action]
133
- # id = task[:rowid]
134
- #
135
- # @@db.delete_action_by_id action, id
136
- # end
137
-
138
- # Runs all tasks in the list of pending tasks and returns the status of each run task.
139
- # @return [Hash] key is task id
140
- def run_all
141
- tasks = @tasks
142
- status = {}
143
-
144
- tasks.each do |t|
145
- a = t[:action]
146
- id = t[:rowid]
147
- status[a] ||= {}
148
- begin
149
- @@db.update_action_status a, id, { :status => :running }
150
- self.send a, t
151
- stat = :complete
152
- msg = nil
153
- rescue InvalidAction, FailedAction => e
154
- stat = :failed
155
- msg = "#{e.class}: #{e.message}"
156
- ensure
157
- s = { :status => stat, :message => msg }
158
- status[a].merge! ( { id => s } )
159
- @@db.update_action_status a, id, s
160
- end
161
- end
162
- status
163
- end
164
-
165
- # Finds bit ids that match given criteria.
166
- # @param [Hash] criteria field / value pairs that will be matched against
167
- # @return [Array] bit ids that match criteria
168
- def lookup_id criteria
169
- @@db.select_info_by_criteria :bits, [:rowid], criteria
170
- end
171
-
172
- end
173
-
@@ -1,212 +0,0 @@
1
- require 'sqlite3'
2
-
3
- # Methods to make sqlite3 interation more concise.
4
- module SQLite3Tools
5
-
6
- # Array methods.
7
- class ::Array
8
- # Converts an array of symbols to a string of backquoted strings for use in SELECT statement
9
- # @return [String]
10
- # @example
11
- # a = [ :col_1, :col_2 ]
12
- # a.sqlite3_to_str #=> "`col_1`, `col_2`"
13
- def sqlite3_to_str
14
- self.map { |sym| "`" + sym.to_s + "`" }.join(", ")
15
- end
16
-
17
- # Converts an sqlite3 result array to a hash.
18
- # @param keys [Array]
19
- # @return [Hash]
20
- # @example
21
- # a = [ "val_1", 42 ]
22
- # k = [ :col_1, :col_2 ]
23
- # a.to_hash k #=> { :col_1 => "val_1", :col_2 => 42 }
24
- def to_hash keys
25
- Hash[ *( 0...self.size ).inject( [] ) { |arr, ix| arr.push( keys[ix], self[ix] ) } ]
26
- end
27
- end
28
-
29
- # Integer methods.
30
- class ::Integer
31
- # Generates placeholder string for INSERT statements.
32
- # @return String placeholder
33
- # @example
34
- # 3.make_placeholders #=> "?, ?, ?"
35
- def make_placeholders
36
- n = self - 1
37
- placeholders = "?"
38
- n.times { placeholders << ", ?" }
39
- placeholders
40
- end
41
- end
42
-
43
- # SQLite3 database methods.
44
- class SQLite3::Database
45
- # Selects given columns using given query and converts results to to hashes.
46
- # @param [Array<Symbol>] columns names of columns to select
47
- # @param [String] query sqlite3 query fragment to append to SELECT part of query
48
- # @return [Array<Hash>] results with each row a hash in :col => value form
49
- def select columns, query
50
- result = [];
51
- self.execute "SELECT #{columns.sqlite3_to_str} #{query}" do |row|
52
- result.push row.to_hash columns
53
- end
54
- result
55
- end
56
- end
57
- end
58
-
59
- # Backend abstracts database interactions.
60
- class Backend < Kit
61
-
62
- include SQLite3Tools
63
-
64
- attr_reader :db_paths
65
-
66
- # (see #db_prepare)
67
- def initialize db_paths
68
- db_paths.each do |key, db|
69
- name = File.basename db
70
- dir = File.dirname db
71
- dir = @@config_path unless [ "/", "~" ].include? dir[0]
72
- db_paths[key] = "#{dir}/#{name}"
73
- end
74
-
75
- @db_paths = db_paths
76
-
77
- dbs = db_prepare @db_paths
78
- @info_db = dbs[:info]
79
- @action_db = dbs[:actions]
80
- end
81
-
82
- private
83
- # Loads existing database files or creates new ones with kit database schema
84
- # @param [Hash] db_paths absolute or relitive paths to database files
85
- def db_prepare db_paths
86
-
87
- # Makes kit database schema
88
- # @param [Symbol] type name of database
89
- # @param db [SQLite3::Database] database object to load schema into
90
- def db_initialize type, db
91
- sql = File.read @@kit_path + "/sqlite3_#{type}.sql"
92
- db.execute_batch sql
93
- end
94
-
95
- dbs = {}
96
- db_paths.each do |type, path|
97
- dbs[type] = [ ( File.exists? path ), ( SQLite3::Database.new path ) ]
98
- end
99
-
100
- # Set sqlite3 options here
101
- dbs.each do |type, db|
102
- db[1].type_translation = true
103
- end
104
-
105
- dbs.each do |type, db|
106
- begin
107
- ( db_initialize type, db[1] ) unless db[0]
108
- rescue
109
- File.delete db_paths[type]
110
- end
111
- end
112
-
113
- dbs.each do |type, db|
114
- dbs[type] = db[1]
115
- end
116
-
117
- return dbs
118
-
119
- end
120
-
121
- public
122
-
123
- # Deletes database files.
124
- def delete
125
- @db_paths.each do |key, f|
126
- File.delete f
127
- end
128
- end
129
-
130
- # Gets the row from an info table with the given id.
131
- # @param [Symbol] table what database table to query
132
- # @param [Array] fields list of column names to return
133
- # @param [Integer] id rowid of record to return
134
- # @return [Hash] first matched row with a key for each requested field
135
- def select_info_by_id table, fields, id
136
- info = @info_db.select fields, "FROM `#{table}` WHERE `rowid` = '#{id}'"
137
- info.first
138
- end
139
-
140
- # Gets the rows from an info table with the given name.
141
- # @param table (see #select_info_by_id)
142
- # @param fields (see #select_info_by_id)
143
- # @param [String] name of records to return
144
- # @return (see #select_info_by_id)
145
- def select_info_by_name table, fields, name
146
- info = @info_db.select fields, "FROM `#{table}` WHERE `name` = '#{name}'"
147
- info.first
148
- end
149
-
150
- # Gets the rows from an info table with the given criteria.
151
- # @param table (see #select_info_by_id)
152
- # @param fields (see #select_info_by_id)
153
- # @param [Hash] criteria key / value pairs required to match
154
- # @return [Array] hash for each returned row with a key for each requested field
155
- def select_info_by_criteria table, fields, criteria
156
- q = []
157
- criteria.each do |key, value|
158
- q << "`#{key}` = '#{value}'"
159
- end
160
-
161
- info = @info_db.select fields, "FROM `#{table}` WHERE #{q.join " AND "}"
162
- end
163
-
164
- # Inserts a new row into an info table.
165
- # @param table (see #select_info_by_id)
166
- # @param [Hash] data key / value pairs for new row
167
- # @return [Integer] rowid of new row
168
- def insert_info table, data
169
- @info_db.execute "INSERT INTO #{table} ( `#{data.keys.join "`, `"}` ) VALUES ( #{data.length.make_placeholders} )", data.values
170
- @info_db.last_insert_row_id
171
- end
172
-
173
- # Gets the rows from the action table with given status.
174
- # @param table (see #select_info_by_id)
175
- # @param fields (see #select_info_by_id)
176
- # @param [Symbol] status name of status to match
177
- # @return (see #select_info_by_id)
178
- def select_all_actions_by_status table, fields, status
179
- query = "FROM `#{table}` WHERE `status` = '#{status}'"
180
- rows = @action_db.select fields, query
181
-
182
- rows.map { |t| t.merge ( { :action => table, :status => t[:status].to_sym } ) }
183
- end
184
-
185
- # Inserts a new row into an action table.
186
- # @param (see #insert_info)
187
- # @return (see #insert_info)
188
- def insert_action table, data
189
- data.merge! ( { :status => :pending.to_s, :time => Time.now.to_i } )
190
- @action_db.execute "INSERT INTO #{table} ( `#{data.keys.join "`, `"}` ) VALUES ( #{data.length.make_placeholders} )", data.values
191
- @action_db.last_insert_row_id
192
- end
193
-
194
- # Updates the status of a task in an action table.
195
- # @param [Symbol] table name of the action table
196
- # @param [Integer] action rowid to update
197
- # @param [Hash] data key / value pairs to set
198
- def update_action_status table, id, data
199
- data.merge! ( { :status => data[:status].to_s, :time => Time.now.to_i } )
200
- set = []
201
- data.each do |key, value|
202
- set << "`#{key}` = '#{value}'"
203
- end
204
-
205
- @action_db.execute "UPDATE #{table} SET #{set.join ", "} WHERE `rowid` = '#{id}'"
206
- end
207
-
208
- # def delete_action_by_id action, id
209
- # puts "DELETE FROM `#{action}` WHERE `rowid` = #{id}"
210
- # end
211
-
212
- end