sql_wrangler 0.0.3 → 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.
- data/lib/sql_wrangler.rb +36 -34
- data/test/sql_wrangler_tests.rb +48 -25
- metadata +12 -6
data/lib/sql_wrangler.rb
CHANGED
@@ -1,70 +1,72 @@
|
|
1
1
|
require 'sqlite3'
|
2
2
|
|
3
3
|
module SqlWrangler
|
4
|
-
|
4
|
+
|
5
5
|
class SqlConnection
|
6
|
-
|
7
|
-
def query(sql_string)
|
8
|
-
Query.new
|
6
|
+
|
7
|
+
def query(sql_string, params={})
|
8
|
+
Query.new(self, sql_string, params)
|
9
9
|
end
|
10
|
-
|
10
|
+
|
11
11
|
end
|
12
|
-
|
12
|
+
|
13
13
|
class SqLiteConnection < SqlConnection
|
14
|
-
|
14
|
+
|
15
15
|
def initialize(db_path)
|
16
16
|
@db = SQLite3::Database.new db_path
|
17
17
|
end
|
18
|
-
|
19
|
-
def execute_sql(sql_string)
|
20
|
-
@db.execute2(sql_string)
|
18
|
+
|
19
|
+
def execute_sql(sql_string, params={})
|
20
|
+
@db.execute2(sql_string, params)
|
21
21
|
end
|
22
22
|
|
23
23
|
def command(sql_string)
|
24
|
-
@db.
|
24
|
+
@db.execute_batch(sql_string)
|
25
25
|
end
|
26
26
|
|
27
27
|
def close
|
28
28
|
@db.close
|
29
29
|
end
|
30
|
-
|
30
|
+
|
31
31
|
end
|
32
|
-
|
32
|
+
|
33
33
|
class Query
|
34
|
-
|
34
|
+
|
35
35
|
attr_reader :sql_string
|
36
36
|
attr_reader :groupings
|
37
37
|
attr_reader :conn
|
38
|
-
|
39
|
-
|
38
|
+
attr_accessor :params
|
39
|
+
|
40
|
+
def initialize(conn, sql_string, params={})
|
40
41
|
@conn = conn
|
41
42
|
@sql_string = sql_string
|
42
43
|
@groupings = []
|
44
|
+
@params = params
|
43
45
|
end
|
44
|
-
|
46
|
+
|
45
47
|
def execute
|
46
|
-
raw_result = @conn.execute_sql(@sql_string)
|
48
|
+
raw_result = @conn.execute_sql(@sql_string, @params)
|
47
49
|
init_groups_for_execution raw_result[0]
|
48
50
|
return format_query_result(raw_result)
|
49
51
|
end
|
50
|
-
|
52
|
+
|
51
53
|
def format_query_result(raw_result)
|
52
54
|
formatted_result = []
|
53
55
|
columns_by_index = get_columns_by_index raw_result[0]
|
54
|
-
|
56
|
+
|
55
57
|
raw_result[1,raw_result.length-1].each do |raw_row|
|
56
58
|
merge_row raw_row, @groupings, formatted_result, columns_by_index
|
57
59
|
end
|
58
|
-
|
60
|
+
|
59
61
|
return formatted_result
|
60
62
|
end
|
61
|
-
|
63
|
+
|
62
64
|
def get_columns_by_index columns
|
63
65
|
columns_by_index = {}
|
64
66
|
(0..columns.length-1).each { |i| columns_by_index[i] = columns[i] }
|
65
67
|
return columns_by_index
|
66
68
|
end
|
67
|
-
|
69
|
+
|
68
70
|
def init_groups_for_execution columns
|
69
71
|
used_indexes = []
|
70
72
|
@groupings.each do |group|
|
@@ -80,9 +82,9 @@ module SqlWrangler
|
|
80
82
|
end
|
81
83
|
end
|
82
84
|
end
|
83
|
-
|
85
|
+
|
84
86
|
def merge_row(row, groups, grouped_data, columns_by_index)
|
85
|
-
|
87
|
+
|
86
88
|
if not @groupings.any?
|
87
89
|
flat_row = {}
|
88
90
|
columns_by_index.each { |index, column| flat_row[column] = row[index] }
|
@@ -112,7 +114,7 @@ module SqlWrangler
|
|
112
114
|
end
|
113
115
|
|
114
116
|
end
|
115
|
-
|
117
|
+
|
116
118
|
def get_existing_grouped_row grouped_vals, grouped_data
|
117
119
|
grouped_data.each do |grouped_row|
|
118
120
|
if not grouped_vals.keys.any? { |key| grouped_vals[key] != grouped_row[key] }
|
@@ -121,29 +123,29 @@ module SqlWrangler
|
|
121
123
|
end
|
122
124
|
return nil
|
123
125
|
end
|
124
|
-
|
126
|
+
|
125
127
|
def group_by columns, options = {}
|
126
128
|
new_grouping = QueryGrouping.new(options[:into], columns)
|
127
129
|
new_grouping.level = @groupings.length
|
128
130
|
@groupings << new_grouping
|
129
131
|
return self
|
130
132
|
end
|
131
|
-
|
133
|
+
|
132
134
|
end
|
133
|
-
|
135
|
+
|
134
136
|
class QueryGrouping
|
135
|
-
|
137
|
+
|
136
138
|
attr_reader :name
|
137
139
|
attr_reader :columns
|
138
140
|
attr_accessor :content_indexes
|
139
141
|
attr_accessor :group_indexes
|
140
142
|
attr_accessor :level
|
141
|
-
|
143
|
+
|
142
144
|
def initialize(name, columns)
|
143
145
|
@name = name
|
144
146
|
@columns = columns
|
145
147
|
end
|
146
|
-
|
148
|
+
|
147
149
|
end
|
148
|
-
|
149
|
-
end
|
150
|
+
|
151
|
+
end
|
data/test/sql_wrangler_tests.rb
CHANGED
@@ -2,15 +2,15 @@ require 'fas_test'
|
|
2
2
|
require './lib/sql_wrangler'
|
3
3
|
|
4
4
|
class SqLiteConnectionTests < FasTest::TestClass
|
5
|
-
|
5
|
+
|
6
6
|
def class_setup
|
7
7
|
@conn = SqlWrangler::SqLiteConnection.new ":memory:"
|
8
8
|
end
|
9
|
-
|
9
|
+
|
10
10
|
def class_teardown
|
11
11
|
@conn.close
|
12
12
|
end
|
13
|
-
|
13
|
+
|
14
14
|
def test__command
|
15
15
|
begin
|
16
16
|
@conn.command("CREATE TABLE users (id int PRIMARY KEY, username VARCHAR(100), password VARCHAR(100))")
|
@@ -21,26 +21,26 @@ class SqLiteConnectionTests < FasTest::TestClass
|
|
21
21
|
@conn.command("DROP TABLE users")
|
22
22
|
end
|
23
23
|
end
|
24
|
-
|
24
|
+
|
25
25
|
def test__query__without_executing_it
|
26
26
|
query = @conn.query("SELECT * FROM users")
|
27
27
|
assert_equal("SELECT * FROM users", query.sql_string)
|
28
28
|
end
|
29
|
-
|
29
|
+
|
30
30
|
end
|
31
31
|
|
32
32
|
class QueryTests < FasTest::TestClass
|
33
|
-
|
33
|
+
|
34
34
|
def class_setup
|
35
35
|
@conn = SqlWrangler::SqLiteConnection.new ":memory:"
|
36
36
|
@conn.execute_sql("CREATE TABLE users (username VARCHAR(100), password VARCHAR(100))")
|
37
37
|
@conn.execute_sql("CREATE TABLE groups (group_name VARCHAR(100))")
|
38
38
|
@conn.execute_sql("CREATE TABLE users_groups (username VARCHAR(100), group_name VARCHAR(100))")
|
39
|
-
|
39
|
+
|
40
40
|
@conn.execute_sql("create table articles (id int primary key, title varchar(100), content varchar(100), author varchar(100))")
|
41
41
|
@conn.execute_sql("create table comments (id int primary key, content text, article_id int)")
|
42
42
|
end
|
43
|
-
|
43
|
+
|
44
44
|
def test_setup
|
45
45
|
@conn.execute_sql("INSERT INTO users VALUES ('username1', 'password1');")
|
46
46
|
@conn.execute_sql("INSERT INTO users VALUES ('username2', 'password2');")
|
@@ -49,7 +49,7 @@ class QueryTests < FasTest::TestClass
|
|
49
49
|
@conn.execute_sql("INSERT INTO users_groups VALUES ('username1', 'group one')")
|
50
50
|
@conn.execute_sql("INSERT INTO users_groups VALUES ('username1', 'group two')")
|
51
51
|
@conn.execute_sql("INSERT INTO users_groups VALUES ('username2', 'group one')")
|
52
|
-
|
52
|
+
|
53
53
|
@conn.execute_sql("insert into articles values (1, 'article1', 'content1', 'username1')")
|
54
54
|
@conn.execute_sql("insert into comments values (1, 'comment on article1 #1', 1)")
|
55
55
|
@conn.execute_sql("insert into comments values (2, 'comment on article1 #2', 1)")
|
@@ -62,11 +62,11 @@ class QueryTests < FasTest::TestClass
|
|
62
62
|
@conn.execute_sql("delete from articles")
|
63
63
|
@conn.execute_sql("delete from comments")
|
64
64
|
end
|
65
|
-
|
65
|
+
|
66
66
|
def class_teardown
|
67
67
|
@conn.close
|
68
68
|
end
|
69
|
-
|
69
|
+
|
70
70
|
def test__execute__has_correct_values_on_simple_query
|
71
71
|
result = @conn.query("SELECT * FROM users").execute
|
72
72
|
assert_equal(2, result.length)
|
@@ -75,16 +75,39 @@ class QueryTests < FasTest::TestClass
|
|
75
75
|
assert_equal("username2", result[1]['username'])
|
76
76
|
assert_equal("password2", result[1]['password'])
|
77
77
|
end
|
78
|
-
|
78
|
+
|
79
79
|
def test__execute__has_correct_columns_on_simple_query
|
80
80
|
first = @conn.query("SELECT * FROM users").execute[0]
|
81
81
|
assert_true(first.keys.any? { |m| m == 'username' })
|
82
82
|
assert_true(first.keys.any? { |m| m == 'password' })
|
83
83
|
end
|
84
|
-
|
84
|
+
|
85
|
+
def test__execute__works_with_param_setter
|
86
|
+
query = @conn.query("select password from users where username=:username")
|
87
|
+
query.params = {:username => 'username1'}
|
88
|
+
result = query.execute
|
89
|
+
assert_equal(1, result.length)
|
90
|
+
assert_equal('password1', result[0]['password'])
|
91
|
+
end
|
92
|
+
|
93
|
+
def test__execute__works_with_params_in_ctor
|
94
|
+
query = @conn.query("select password from users where username=:username", :username => 'username1')
|
95
|
+
result = query.execute
|
96
|
+
assert_equal(1, result.length)
|
97
|
+
assert_equal('password1', result[0]['password'])
|
98
|
+
end
|
99
|
+
|
100
|
+
def test__execute__works_with_param_modification
|
101
|
+
query = @conn.query("select password from users where username=:username")
|
102
|
+
query.params[:username] = 'username1'
|
103
|
+
result = query.execute
|
104
|
+
assert_equal(1, result.length)
|
105
|
+
assert_equal('password1', result[0]['password'])
|
106
|
+
end
|
107
|
+
|
85
108
|
def test__execute__works_with_a_more_complex_query
|
86
109
|
result = @conn.query("
|
87
|
-
select u.username, g.group_name
|
110
|
+
select u.username, g.group_name
|
88
111
|
from groups g
|
89
112
|
inner join users_groups ug on ug.group_name = g.group_name
|
90
113
|
inner join users u on u.username = ug.username
|
@@ -97,10 +120,10 @@ class QueryTests < FasTest::TestClass
|
|
97
120
|
assert_equal("username2", result[2]["username"])
|
98
121
|
assert_equal("group one", result[2]["group_name"])
|
99
122
|
end
|
100
|
-
|
123
|
+
|
101
124
|
def test__execute__works_with_a_simple_grouping
|
102
125
|
result = @conn.query("
|
103
|
-
select u.username, g.group_name
|
126
|
+
select u.username, g.group_name
|
104
127
|
from groups g
|
105
128
|
inner join users_groups ug on ug.group_name = g.group_name
|
106
129
|
inner join users u on u.username = ug.username
|
@@ -114,10 +137,10 @@ class QueryTests < FasTest::TestClass
|
|
114
137
|
assert_equal(1, result[1]["users"].length)
|
115
138
|
assert_equal("username1", result[1]["users"][0]["username"])
|
116
139
|
end
|
117
|
-
|
140
|
+
|
118
141
|
def test__group__modifies_query_object_correctly_with_single_grouping
|
119
142
|
query = @conn.query("
|
120
|
-
select u.username, g.group_name
|
143
|
+
select u.username, g.group_name
|
121
144
|
from groups g
|
122
145
|
inner join users_groups ug on ug.group_name = g.group_name
|
123
146
|
inner join users u on u.username = ug.username
|
@@ -127,7 +150,7 @@ class QueryTests < FasTest::TestClass
|
|
127
150
|
assert_equal(1, query.groupings[0].columns.length)
|
128
151
|
assert_equal("group_name", query.groupings[0].columns[0])
|
129
152
|
end
|
130
|
-
|
153
|
+
|
131
154
|
def test__execute__works_with_multi_level_grouping
|
132
155
|
result = @conn.query("
|
133
156
|
select u.username, g.group_name, a.title, a.content article_content, c.id comment_id, c.content comment_content
|
@@ -141,16 +164,16 @@ class QueryTests < FasTest::TestClass
|
|
141
164
|
.group_by(["username"], :into => "articles") \
|
142
165
|
.group_by(["article_content", "title"], :into => "comments") \
|
143
166
|
.execute
|
144
|
-
|
167
|
+
|
145
168
|
assert_equal(2, result.length)
|
146
169
|
assert_equal("group one", result[0]["group_name"])
|
147
170
|
assert_equal(2, result[0]["users"].length)
|
148
171
|
assert_equal("username1", result[0]["users"][0]["username"])
|
149
|
-
|
172
|
+
|
150
173
|
assert_equal(1, result[0]["users"][0]["articles"].length)
|
151
174
|
assert_equal("article1", result[0]["users"][0]["articles"][0]["title"])
|
152
175
|
assert_equal("content1", result[0]["users"][0]["articles"][0]["article_content"])
|
153
|
-
|
176
|
+
|
154
177
|
assert_equal(2, result[0]["users"][0]["articles"][0]["comments"].length)
|
155
178
|
assert_equal(1, result[0]["users"][0]["articles"][0]["comments"][0]["comment_id"])
|
156
179
|
assert_equal("comment on article1 #1", result[0]["users"][0]["articles"][0]["comments"][0]["comment_content"])
|
@@ -158,11 +181,11 @@ class QueryTests < FasTest::TestClass
|
|
158
181
|
assert_equal("comment on article1 #2", result[0]["users"][0]["articles"][0]["comments"][1]["comment_content"])
|
159
182
|
assert_equal("username2", result[0]["users"][1]["username"])
|
160
183
|
assert_equal(0, result[0]["users"][1]["articles"].length)
|
161
|
-
|
184
|
+
|
162
185
|
assert_equal("group two", result[1]["group_name"])
|
163
186
|
assert_equal(1, result[1]["users"].length)
|
164
187
|
assert_equal("username1", result[1]["users"][0]["username"])
|
165
188
|
assert_equal(1, result[1]["users"][0]["articles"].length)
|
166
189
|
end
|
167
|
-
|
168
|
-
end
|
190
|
+
|
191
|
+
end
|
metadata
CHANGED
@@ -1,8 +1,12 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sql_wrangler
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
prerelease:
|
5
|
-
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 1
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
version: 1.0.0
|
6
10
|
platform: ruby
|
7
11
|
authors:
|
8
12
|
- Graeme Hill
|
@@ -10,7 +14,7 @@ autorequire:
|
|
10
14
|
bindir: bin
|
11
15
|
cert_chain: []
|
12
16
|
|
13
|
-
date: 2011-
|
17
|
+
date: 2011-10-15 00:00:00 -07:00
|
14
18
|
default_executable:
|
15
19
|
dependencies: []
|
16
20
|
|
@@ -38,21 +42,23 @@ rdoc_options: []
|
|
38
42
|
require_paths:
|
39
43
|
- lib
|
40
44
|
required_ruby_version: !ruby/object:Gem::Requirement
|
41
|
-
none: false
|
42
45
|
requirements:
|
43
46
|
- - ">="
|
44
47
|
- !ruby/object:Gem::Version
|
48
|
+
segments:
|
49
|
+
- 0
|
45
50
|
version: "0"
|
46
51
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
47
|
-
none: false
|
48
52
|
requirements:
|
49
53
|
- - ">="
|
50
54
|
- !ruby/object:Gem::Version
|
55
|
+
segments:
|
56
|
+
- 0
|
51
57
|
version: "0"
|
52
58
|
requirements: []
|
53
59
|
|
54
60
|
rubyforge_project:
|
55
|
-
rubygems_version: 1.
|
61
|
+
rubygems_version: 1.3.6
|
56
62
|
signing_key:
|
57
63
|
specification_version: 3
|
58
64
|
summary: A simple ORM alternative that makes it easier to deal with SQL queries in ruby.
|