hyperion-sql 0.0.1.alpha2
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/hyperion/sql/datastore.rb +84 -0
- data/lib/hyperion/sql/query_builder.rb +125 -0
- data/lib/hyperion/sql/query_executor.rb +35 -0
- data/lib/hyperion/sql/sql_query.rb +19 -0
- data/lib/hyperion/sql/transaction.rb +56 -0
- data/lib/hyperion/sql/transaction_spec.rb +156 -0
- data/lib/hyperion/sql.rb +66 -0
- data/spec/hyperion/sql_spec.rb +4 -0
- metadata +88 -0
@@ -0,0 +1,84 @@
|
|
1
|
+
require 'hyperion/api'
|
2
|
+
require 'hyperion/key'
|
3
|
+
require 'hyperion/sql/query_builder'
|
4
|
+
require 'hyperion/sql/query_executor'
|
5
|
+
|
6
|
+
module Hyperion
|
7
|
+
module Sql
|
8
|
+
|
9
|
+
class Datastore
|
10
|
+
|
11
|
+
def initialize(db_strategy, query_executor_strategy, query_builder_strategy)
|
12
|
+
@db_strategy = db_strategy
|
13
|
+
@query_executor = QueryExecutor.new(query_executor_strategy)
|
14
|
+
@query_builder = QueryBuilder.new(query_builder_strategy)
|
15
|
+
end
|
16
|
+
|
17
|
+
def save(records)
|
18
|
+
records.map do |record|
|
19
|
+
if API.new?(record)
|
20
|
+
execute_save_query(query_builder.build_insert(record), record)
|
21
|
+
elsif non_empty_record?(record)
|
22
|
+
execute_save_query(query_builder.build_update(record), record)
|
23
|
+
else
|
24
|
+
record
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def find_by_key(key)
|
30
|
+
find(query_from_key(key)).first
|
31
|
+
end
|
32
|
+
|
33
|
+
def find(query)
|
34
|
+
sql_query = query_builder.build_select(query)
|
35
|
+
results = query_executor.execute_query(sql_query)
|
36
|
+
results.map { |record| record_from_db(record, query.kind) }
|
37
|
+
end
|
38
|
+
|
39
|
+
def delete_by_key(key)
|
40
|
+
delete(query_from_key(key))
|
41
|
+
end
|
42
|
+
|
43
|
+
def delete(query)
|
44
|
+
sql_query = query_builder.build_delete(query)
|
45
|
+
query_executor.execute_mutation(sql_query)
|
46
|
+
nil
|
47
|
+
end
|
48
|
+
|
49
|
+
def count(query)
|
50
|
+
sql_query = query_builder.build_count(query)
|
51
|
+
results = query_executor.execute_query(sql_query)
|
52
|
+
db_strategy.process_count_result(results[0])
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
attr_reader :query_builder, :query_executor, :db_strategy
|
58
|
+
|
59
|
+
def non_empty_record?(record)
|
60
|
+
record = record.dup
|
61
|
+
record.delete(:kind)
|
62
|
+
record.delete(:key)
|
63
|
+
!record.empty?
|
64
|
+
end
|
65
|
+
|
66
|
+
def execute_save_query(sql_query, record)
|
67
|
+
result = query_executor.execute_write(sql_query)
|
68
|
+
returned_record = db_strategy.process_result(record, result)
|
69
|
+
record_from_db(returned_record, record[:kind])
|
70
|
+
end
|
71
|
+
|
72
|
+
def record_from_db(record, table)
|
73
|
+
record[:key] = Key.compose_key(table, record.delete('id')) if API.new?(record)
|
74
|
+
record[:kind] = table
|
75
|
+
record
|
76
|
+
end
|
77
|
+
|
78
|
+
def query_from_key(key)
|
79
|
+
table, id = Key.decompose_key(key)
|
80
|
+
Query.new(table, [Filter.new(:id, '=', id)], nil, nil, nil)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,125 @@
|
|
1
|
+
require 'hyperion/key'
|
2
|
+
require 'hyperion/sql/sql_query'
|
3
|
+
|
4
|
+
module Hyperion
|
5
|
+
module Sql
|
6
|
+
|
7
|
+
class QueryBuilder
|
8
|
+
|
9
|
+
def initialize(qb_strategy)
|
10
|
+
@qb_strategy = qb_strategy
|
11
|
+
end
|
12
|
+
|
13
|
+
def build_insert(record)
|
14
|
+
record = record.dup
|
15
|
+
table = format_table(record.delete(:kind))
|
16
|
+
unless record.empty?
|
17
|
+
columns = format_array(record.keys.map {|c| format_column(c) })
|
18
|
+
values = format_array(record.values.map {|v| '?'})
|
19
|
+
query = "INSERT INTO #{table} #{columns} VALUES #{values}"
|
20
|
+
else
|
21
|
+
query = qb_strategy.empty_insert_query(table)
|
22
|
+
end
|
23
|
+
SqlQuery.new(qb_strategy.normalize_insert(query), record.values)
|
24
|
+
end
|
25
|
+
|
26
|
+
def build_update(record)
|
27
|
+
record = record.dup
|
28
|
+
table, id = Key.decompose_key(record.delete(:key))
|
29
|
+
table = format_table(record.delete(:kind))
|
30
|
+
column_values = record.keys.map {|field| "#{format_column(field)} = ?"}
|
31
|
+
query = qb_strategy.normalize_update("UPDATE #{table} SET #{column_values.join(', ')} WHERE #{quote('id')} = #{id}")
|
32
|
+
SqlQuery.new(query, record.values)
|
33
|
+
end
|
34
|
+
|
35
|
+
def build_select(query)
|
36
|
+
sql_query = SqlQuery.new("SELECT * FROM \"#{query.kind}\"")
|
37
|
+
apply_filters(sql_query, query.filters)
|
38
|
+
apply_sorts(sql_query, query.sorts)
|
39
|
+
qb_strategy.apply_limit_and_offset(sql_query, query.limit, query.offset)
|
40
|
+
sql_query
|
41
|
+
end
|
42
|
+
|
43
|
+
def build_delete(query)
|
44
|
+
sql_query = SqlQuery.new("DELETE FROM \"#{query.kind}\"")
|
45
|
+
apply_filters(sql_query, query.filters)
|
46
|
+
sql_query
|
47
|
+
end
|
48
|
+
|
49
|
+
def build_count(query)
|
50
|
+
sql_query = SqlQuery.new("SELECT COUNT(*) FROM \"#{query.kind}\"")
|
51
|
+
apply_filters(sql_query, query.filters)
|
52
|
+
sql_query
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
attr_reader :qb_strategy
|
58
|
+
|
59
|
+
def quote(str)
|
60
|
+
tick = qb_strategy.quote_tick
|
61
|
+
tick + str.to_s.gsub(tick, tick + tick) + tick
|
62
|
+
end
|
63
|
+
|
64
|
+
def format_column(column)
|
65
|
+
quote(column)
|
66
|
+
end
|
67
|
+
|
68
|
+
def format_table(table)
|
69
|
+
quote(table)
|
70
|
+
end
|
71
|
+
|
72
|
+
def format_array(arr)
|
73
|
+
"(#{arr.join(', ')})"
|
74
|
+
end
|
75
|
+
|
76
|
+
def apply_filters(sql_query, filters)
|
77
|
+
if filters.empty?
|
78
|
+
sql_query
|
79
|
+
else
|
80
|
+
filter_sql = []
|
81
|
+
filter_values = []
|
82
|
+
filters.each do |filter|
|
83
|
+
filter_sql << "#{format_column(filter.field)} #{format_operator(filter.operator)} ?"
|
84
|
+
filter_values << filter.value
|
85
|
+
end
|
86
|
+
sql_query.append("WHERE #{filter_sql.join(' AND ')}", filter_values)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def format_operator(operator)
|
91
|
+
case operator
|
92
|
+
when 'contains?'
|
93
|
+
"IN"
|
94
|
+
when '!='
|
95
|
+
"<>"
|
96
|
+
else
|
97
|
+
operator
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def apply_sorts(sql_query, sorts)
|
102
|
+
if sorts.empty?
|
103
|
+
sql_query
|
104
|
+
else
|
105
|
+
sort_sql = []
|
106
|
+
sort_values = []
|
107
|
+
sort_sql = sorts.map do |sort|
|
108
|
+
"#{format_column(sort.field)} #{format_order(sort.order)}"
|
109
|
+
end
|
110
|
+
sql_query.append("ORDER BY #{sort_sql.join(', ')}")
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def format_order(order)
|
115
|
+
case order
|
116
|
+
when :asc
|
117
|
+
"ASC"
|
118
|
+
when :desc
|
119
|
+
"DESC"
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
end
|
125
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'hyperion/sql'
|
2
|
+
|
3
|
+
module Hyperion
|
4
|
+
module Sql
|
5
|
+
|
6
|
+
class QueryExecutor
|
7
|
+
|
8
|
+
attr_reader :strategy
|
9
|
+
|
10
|
+
def initialize(strategy)
|
11
|
+
@strategy = strategy
|
12
|
+
end
|
13
|
+
|
14
|
+
def execute_mutation(sql_query)
|
15
|
+
command = connection.create_command(sql_query.query_str)
|
16
|
+
command.execute_non_query(*sql_query.bind_values)
|
17
|
+
end
|
18
|
+
|
19
|
+
def execute_query(sql_query)
|
20
|
+
command = connection.create_command(sql_query.query_str)
|
21
|
+
command.execute_reader(*sql_query.bind_values).to_a
|
22
|
+
end
|
23
|
+
|
24
|
+
def execute_write(sql_query)
|
25
|
+
strategy.execute_write(sql_query)
|
26
|
+
end
|
27
|
+
|
28
|
+
def connection
|
29
|
+
Sql.connection
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
|
2
|
+
module Hyperion
|
3
|
+
module Sql
|
4
|
+
|
5
|
+
class SqlQuery
|
6
|
+
attr_reader :query_str, :bind_values
|
7
|
+
|
8
|
+
def initialize(query_str, bind_values=[])
|
9
|
+
@query_str = query_str
|
10
|
+
@bind_values = bind_values || []
|
11
|
+
end
|
12
|
+
|
13
|
+
def append(str, values=[])
|
14
|
+
@query_str << " #{str}"
|
15
|
+
@bind_values += values if values
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'socket'
|
2
|
+
require 'digest'
|
3
|
+
require 'digest/sha2'
|
4
|
+
|
5
|
+
module Hyperion
|
6
|
+
module Sql
|
7
|
+
class Transaction
|
8
|
+
|
9
|
+
HOST = "#{Socket::gethostbyname(Socket::gethostname)[0]}" rescue "localhost"
|
10
|
+
|
11
|
+
attr_reader :connection
|
12
|
+
|
13
|
+
@@counter = 0
|
14
|
+
|
15
|
+
def initialize(connection)
|
16
|
+
@connection = connection
|
17
|
+
end
|
18
|
+
|
19
|
+
def begin
|
20
|
+
run "BEGIN"
|
21
|
+
end
|
22
|
+
|
23
|
+
def commit
|
24
|
+
run "COMMIT"
|
25
|
+
end
|
26
|
+
|
27
|
+
def rollback
|
28
|
+
run "ROLLBACK"
|
29
|
+
end
|
30
|
+
|
31
|
+
def begin_savepoint
|
32
|
+
id = new_savepoint_id
|
33
|
+
run %{SAVEPOINT "#{id}"}
|
34
|
+
id
|
35
|
+
end
|
36
|
+
|
37
|
+
def release_savepoint(id)
|
38
|
+
run %{RELEASE SAVEPOINT "#{id}"}
|
39
|
+
end
|
40
|
+
|
41
|
+
def rollback_to_savepoint(id)
|
42
|
+
run %{ROLLBACK TO SAVEPOINT "#{id}"}
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def run(cmd)
|
48
|
+
connection.create_command(cmd).execute_non_query
|
49
|
+
end
|
50
|
+
|
51
|
+
def new_savepoint_id
|
52
|
+
Digest::SHA256.hexdigest("#{HOST}:#{$$}:#{Time.now.to_f}:#{@@counter += 1}")[0..-2]
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,156 @@
|
|
1
|
+
shared_examples_for 'Sql Transactions' do
|
2
|
+
def write(query)
|
3
|
+
command = Hyperion::Sql.connection.create_command(query)
|
4
|
+
command.execute_non_query
|
5
|
+
end
|
6
|
+
|
7
|
+
def create_table(table_name)
|
8
|
+
write("CREATE TABLE #{table_name} (name VARCHAR(20), age INTEGER)")
|
9
|
+
end
|
10
|
+
|
11
|
+
def drop_table(table_name)
|
12
|
+
write("DROP TABLE IF EXISTS #{table_name}")
|
13
|
+
end
|
14
|
+
|
15
|
+
around :each do |example|
|
16
|
+
begin
|
17
|
+
create_table('test')
|
18
|
+
example.run
|
19
|
+
ensure
|
20
|
+
drop_table('test')
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_count
|
25
|
+
Hyperion::API.count_by_kind('test')
|
26
|
+
end
|
27
|
+
|
28
|
+
context 'rollback' do
|
29
|
+
|
30
|
+
it 'rolls back all changes' do
|
31
|
+
Hyperion::Sql.rollback do
|
32
|
+
write("INSERT INTO test (name, age) VALUES ('Myles', 23)")
|
33
|
+
test_count.should == 1
|
34
|
+
end
|
35
|
+
test_count.should == 0
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'rolls back multiple' do
|
39
|
+
Hyperion::Sql.rollback do
|
40
|
+
write("INSERT INTO test (name, age) VALUES ('Myles', 23)")
|
41
|
+
Hyperion::Sql.rollback do
|
42
|
+
write("INSERT INTO test (name, age) VALUES ('Myles', 23)")
|
43
|
+
test_count.should == 2
|
44
|
+
end
|
45
|
+
test_count.should == 1
|
46
|
+
end
|
47
|
+
test_count.should == 0
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'returns the result of the body' do
|
51
|
+
Hyperion::Sql.rollback do
|
52
|
+
:result
|
53
|
+
end.should == :result
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
context 'transaction' do
|
59
|
+
|
60
|
+
it 'commits' do
|
61
|
+
Hyperion::Sql.transaction do
|
62
|
+
write("INSERT INTO test (name, age) VALUES ('Myles', 23)")
|
63
|
+
test_count.should == 1
|
64
|
+
end
|
65
|
+
test_count.should == 1
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'commits multiple' do
|
69
|
+
Hyperion::Sql.transaction do
|
70
|
+
write("INSERT INTO test (name, age) VALUES ('Myles', 23)")
|
71
|
+
end
|
72
|
+
Hyperion::Sql.transaction do
|
73
|
+
write("INSERT INTO test (name, age) VALUES ('Myles', 23)")
|
74
|
+
end
|
75
|
+
test_count.should == 2
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'commits one and then rolls back the next' do
|
79
|
+
Hyperion::Sql.transaction do
|
80
|
+
write("INSERT INTO test (name, age) VALUES ('Myles', 23)")
|
81
|
+
end
|
82
|
+
expect {
|
83
|
+
Hyperion::Sql.transaction do
|
84
|
+
write("INSERT INTO test (name, age) VALUES ('Myles', 23)")
|
85
|
+
raise
|
86
|
+
end
|
87
|
+
}.to raise_error
|
88
|
+
test_count.should == 1
|
89
|
+
end
|
90
|
+
|
91
|
+
it 'rolls back when an exception is thrown' do
|
92
|
+
expect {
|
93
|
+
Hyperion::Sql.transaction do
|
94
|
+
write("INSERT INTO test (name, age) VALUES ('Myles', 23)")
|
95
|
+
test_count.should == 1
|
96
|
+
raise
|
97
|
+
end
|
98
|
+
}.to raise_error
|
99
|
+
test_count.should == 0
|
100
|
+
end
|
101
|
+
|
102
|
+
it 'commits nested transactions' do
|
103
|
+
Hyperion::Sql.transaction do
|
104
|
+
write("INSERT INTO test (name, age) VALUES ('Myles', 23)")
|
105
|
+
Hyperion::Sql.transaction do
|
106
|
+
write("INSERT INTO test (name, age) VALUES ('Myles', 23)")
|
107
|
+
end
|
108
|
+
end
|
109
|
+
test_count.should == 2
|
110
|
+
end
|
111
|
+
|
112
|
+
it 'rolls back nested transactions' do
|
113
|
+
expect {
|
114
|
+
Hyperion::Sql.transaction do
|
115
|
+
write("INSERT INTO test (name, age) VALUES ('Myles', 23)")
|
116
|
+
Hyperion::Sql.transaction do
|
117
|
+
write("INSERT INTO test (name, age) VALUES ('Myles', 23)")
|
118
|
+
end
|
119
|
+
test_count.should == 2
|
120
|
+
raise
|
121
|
+
end
|
122
|
+
}.to raise_error
|
123
|
+
test_count.should == 0
|
124
|
+
end
|
125
|
+
|
126
|
+
it 'returns the result of the transaction' do
|
127
|
+
Hyperion::Sql.transaction do
|
128
|
+
:result
|
129
|
+
end.should == :result
|
130
|
+
end
|
131
|
+
|
132
|
+
end
|
133
|
+
|
134
|
+
it 'can handle outer rollback and inner transaction' do
|
135
|
+
Hyperion::Sql.rollback do
|
136
|
+
Hyperion::Sql.transaction do
|
137
|
+
write("INSERT INTO test (name, age) VALUES ('Myles', 23)")
|
138
|
+
end
|
139
|
+
test_count.should == 1
|
140
|
+
end
|
141
|
+
test_count.should == 0
|
142
|
+
end
|
143
|
+
|
144
|
+
it 'can handle outer transaction and inner rollback' do
|
145
|
+
Hyperion::Sql.transaction do
|
146
|
+
write("INSERT INTO test (name, age) VALUES ('Myles', 23)")
|
147
|
+
test_count.should == 1
|
148
|
+
Hyperion::Sql.rollback do
|
149
|
+
write("INSERT INTO test (name, age) VALUES ('Myles', 23)")
|
150
|
+
test_count.should == 2
|
151
|
+
end
|
152
|
+
test_count.should == 1
|
153
|
+
end
|
154
|
+
test_count.should == 1
|
155
|
+
end
|
156
|
+
end
|
data/lib/hyperion/sql.rb
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'hyperion/sql/transaction'
|
2
|
+
|
3
|
+
module Hyperion
|
4
|
+
module Sql
|
5
|
+
|
6
|
+
def self.with_connection(url)
|
7
|
+
connection = DataObjects::Connection.new(url)
|
8
|
+
Thread.current[:connection] = connection
|
9
|
+
yield(connection)
|
10
|
+
connection.close
|
11
|
+
Thread.current[:connection] = nil
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.connection
|
15
|
+
Thread.current[:connection] || raise('No Connection Established')
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.rollback
|
19
|
+
with_txn do |txn|
|
20
|
+
begin
|
21
|
+
savepoint_id = txn.begin_savepoint
|
22
|
+
yield
|
23
|
+
ensure
|
24
|
+
txn.rollback_to_savepoint(savepoint_id)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.transaction
|
30
|
+
with_txn do |txn|
|
31
|
+
begin
|
32
|
+
savepoint_id = txn.begin_savepoint
|
33
|
+
result = yield
|
34
|
+
txn.release_savepoint(savepoint_id)
|
35
|
+
result
|
36
|
+
rescue Exception => e
|
37
|
+
txn.rollback_to_savepoint(savepoint_id)
|
38
|
+
raise e
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def self.in_transaction?
|
46
|
+
!Thread.current[:transaction].nil?
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.with_txn
|
50
|
+
if Thread.current[:transaction]
|
51
|
+
yield(Thread.current[:transaction])
|
52
|
+
else
|
53
|
+
txn = (Thread.current[:transaction] = Transaction.new(connection))
|
54
|
+
txn.begin
|
55
|
+
result = yield(txn)
|
56
|
+
txn.commit
|
57
|
+
result
|
58
|
+
end
|
59
|
+
rescue Exception => e
|
60
|
+
txn.rollback
|
61
|
+
raise e
|
62
|
+
ensure
|
63
|
+
Thread.current[:transaction] = nil
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
metadata
ADDED
@@ -0,0 +1,88 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: hyperion-sql
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1.alpha2
|
5
|
+
prerelease: 6
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- 8th Light, Inc.
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-09-17 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rspec
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - '='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 2.11.0
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - '='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 2.11.0
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: hyperion-api
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - '='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: 0.0.1.alpha2
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - '='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: 0.0.1.alpha2
|
46
|
+
description: Shared behavior for Sql databases
|
47
|
+
email:
|
48
|
+
- myles@8thlight.com
|
49
|
+
- skim@8thlight.com
|
50
|
+
executables: []
|
51
|
+
extensions: []
|
52
|
+
extra_rdoc_files: []
|
53
|
+
files:
|
54
|
+
- lib/hyperion/sql.rb
|
55
|
+
- lib/hyperion/sql/query_executor.rb
|
56
|
+
- lib/hyperion/sql/sql_query.rb
|
57
|
+
- lib/hyperion/sql/transaction.rb
|
58
|
+
- lib/hyperion/sql/transaction_spec.rb
|
59
|
+
- lib/hyperion/sql/query_builder.rb
|
60
|
+
- lib/hyperion/sql/datastore.rb
|
61
|
+
- spec/hyperion/sql_spec.rb
|
62
|
+
homepage: https://github.com/mylesmegyesi/hyperion-ruby
|
63
|
+
licenses:
|
64
|
+
- Eclipse Public License
|
65
|
+
post_install_message:
|
66
|
+
rdoc_options: []
|
67
|
+
require_paths:
|
68
|
+
- lib
|
69
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
70
|
+
none: false
|
71
|
+
requirements:
|
72
|
+
- - ! '>='
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: 1.8.7
|
75
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
76
|
+
none: false
|
77
|
+
requirements:
|
78
|
+
- - ! '>'
|
79
|
+
- !ruby/object:Gem::Version
|
80
|
+
version: 1.3.1
|
81
|
+
requirements: []
|
82
|
+
rubyforge_project:
|
83
|
+
rubygems_version: 1.8.24
|
84
|
+
signing_key:
|
85
|
+
specification_version: 3
|
86
|
+
summary: Shared behavior for Sql databases
|
87
|
+
test_files:
|
88
|
+
- spec/hyperion/sql_spec.rb
|