sql_wrangler 0.0.3 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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.
|