kit 0.3.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +18 -0
- data/.rspec +0 -0
- data/.yardopts +2 -0
- data/CHANGELOG.rdoc +10 -1
- data/Gemfile +2 -0
- data/LICENSE.txt +22 -0
- data/README.rdoc +84 -19
- data/Rakefile +21 -0
- data/kit.gemspec +27 -0
- data/lib/kit.rb +67 -170
- data/lib/kit/db_support.rb +71 -0
- data/lib/kit/models/bit.rb +34 -0
- data/lib/kit/models/group.rb +5 -0
- data/lib/kit/models/permission.rb +6 -0
- data/lib/kit/models/user.rb +6 -0
- data/lib/kit/rake/admin.rb +3 -0
- data/lib/kit/rake/admin/database.rb +28 -0
- data/lib/kit/rake/admin/make.rb +19 -0
- data/lib/kit/rake/admin/manage.rb +8 -0
- data/lib/kit/version.rb +4 -0
- data/spec/bit_spec.rb +57 -0
- data/spec/kit_db_support_spec.rb +110 -0
- data/spec/kit_spec.rb +56 -0
- metadata +108 -19
- data/LICENCE.txt +0 -21
- data/lib/kit/bit.rb +0 -173
- data/lib/kit/db_sqlite3.rb +0 -212
data/LICENCE.txt
DELETED
@@ -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.
|
data/lib/kit/bit.rb
DELETED
@@ -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
|
-
|
data/lib/kit/db_sqlite3.rb
DELETED
@@ -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
|