tusks 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 408ceaca49432fc3be46b178ad26f733c48d81c9
4
+ data.tar.gz: 5b7b79891270d0c9e0e473607052fe6b62c985f7
5
+ SHA512:
6
+ metadata.gz: d0f6cf8e4813555f8264d1e2e68e3a3a6ca14fb2d918141747099cf46fd794bd695a799d2046e7632873bf55d08c82bf01f4cc994c0270da8a5a3e0ff1a750b3
7
+ data.tar.gz: 4e7a281c47cee1924d1bddfc1bde4bc55461acd07dbe4bd7bc2fd109d01d8522b2a164c244fc2c3b3ca0329e9b518f32dfdb251422e73d4cb3464879b7e68400
data/.gitignore ADDED
@@ -0,0 +1 @@
1
+ *.gem
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/LICENSE.md ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2014 Craig McCown
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 all
13
+ 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 THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,4 @@
1
+ tusks
2
+ =====
3
+
4
+ Easily call PostgreSQL functions from Ruby.
@@ -0,0 +1,96 @@
1
+ module Tusks
2
+ class Connection
3
+ def initialize(options = {})
4
+ @options = options
5
+ @in_transaction = false
6
+ end
7
+
8
+ def connected?
9
+ !(@conn.nil? || @conn.finished?)
10
+ end
11
+
12
+ def in_transaction?
13
+ @in_transaction
14
+ end
15
+
16
+ def begin_transaction
17
+ connect
18
+ @conn.exec("BEGIN TRANSACTION")
19
+ @in_transaction = true
20
+ end
21
+
22
+ def commit_transaction
23
+ if in_transaction?
24
+ @conn.exec("COMMIT TRANSACTION")
25
+ disconnect
26
+ end
27
+ end
28
+
29
+ def rollback_transaction
30
+ if in_transaction?
31
+ @conn.exec("ROLLBACK TRANSACTION")
32
+ disconnect
33
+ end
34
+ end
35
+
36
+ def functions(&block)
37
+ instance_eval &block
38
+ end
39
+
40
+ private
41
+
42
+ def connect
43
+ disconnect if connected?
44
+ @conn = PG.connect(@options)
45
+ end
46
+
47
+ def disconnect
48
+ @conn.close if connected?
49
+ @in_transaction = false
50
+ end
51
+
52
+ def execute_function(sproc_name, *args)
53
+ connect if !connected?
54
+ @conn.exec(build_sql_string(sproc_name, *args))
55
+ disconnect
56
+ end
57
+
58
+ def build_sql_string(sproc_name, *args)
59
+ arg_str = ''
60
+ args.each do |arg|
61
+ raise UnsupportedTypeError.new 'Unsupported type: ' + arg.class.to_s unless arg.respond_to? :to_pg_s
62
+ arg_str = (arg_str.empty? && arg.to_pg_s) || [arg_str, arg.to_pg_s].join(',')
63
+ end
64
+ "SELECT * FROM #{sproc_name}(#{arg_str})"
65
+ end
66
+
67
+ def no_results(*sproc_names)
68
+ sproc_names.each do |sproc_name|
69
+ define_singleton_method(sproc_name) do |*args|
70
+ execute_function(sproc_name, *args)
71
+ nil
72
+ end
73
+ end
74
+ end
75
+
76
+ def one_result(*sproc_names)
77
+ sproc_names.each do |sproc_name|
78
+ define_singleton_method(sproc_name) do |*args|
79
+ result = execute_function(sproc_name, *args)
80
+ result[0]
81
+ end
82
+ end
83
+ end
84
+
85
+ def many_results(*sproc_names)
86
+ sproc_names.each do |sproc_name|
87
+ define_singleton_method(sproc_name) do |*args|
88
+ results = execute_function(sproc_name, *args)
89
+ result_array = []
90
+ results.each { |row| result_array << row }
91
+ result_array
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,3 @@
1
+ require_relative 'tusks_error'
2
+ require_relative 'nested_array_error'
3
+ require_relative 'unsupported_type_error'
@@ -0,0 +1,3 @@
1
+ module Tusks
2
+ class NestedArrayError < TusksError; end
3
+ end
@@ -0,0 +1,3 @@
1
+ module Tusks
2
+ class TusksError < StandardError; end
3
+ end
@@ -0,0 +1,3 @@
1
+ module Tusks
2
+ class UnsupportedTypeError < TusksError; end
3
+ end
@@ -0,0 +1,16 @@
1
+ module Tusks
2
+ class ::Array
3
+ def to_pg_s
4
+ array_string = 'ARRAY['
5
+
6
+ self.each do |element|
7
+ raise UnsupportedTypeError.new 'Unsupported type: ' + element.class.to_s unless element.respond_to? :to_pg_s
8
+ raise NestedArrayError.new 'Nested arrays not yet supported' if element.is_a? Array
9
+
10
+ array_string << element.to_pg_s << ','
11
+ end
12
+
13
+ array_string.chomp(',') << ']'
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,7 @@
1
+ module Tusks
2
+ class ::Float
3
+ def to_pg_s
4
+ self.to_s
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ module Tusks
2
+ class ::Hash
3
+ def to_pg_s
4
+ self.to_json
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,6 @@
1
+ require_relative 'array'
2
+ require_relative 'float'
3
+ require_relative 'hash'
4
+ require_relative 'integer'
5
+ require_relative 'nil'
6
+ require_relative 'string'
@@ -0,0 +1,7 @@
1
+ module Tusks
2
+ class ::Integer
3
+ def to_pg_s
4
+ self.to_s
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ module Tusks
2
+ class ::NilClass
3
+ def to_pg_s
4
+ 'NULL'
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ module Tusks
2
+ class ::String
3
+ def to_pg_s
4
+ "'" + self.gsub("'") {"''"} + "'"
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,3 @@
1
+ module Tusks
2
+ VERSION = '0.0.0'
3
+ end
data/lib/tusks.rb ADDED
@@ -0,0 +1,3 @@
1
+ require 'tusks/types/init'
2
+ require 'tusks/error/init'
3
+ require 'tusks/connection'
@@ -0,0 +1 @@
1
+ require 'tusks'
@@ -0,0 +1,173 @@
1
+ require 'spec_helper'
2
+
3
+ module Tusks
4
+ describe Connection do
5
+ before :each do
6
+ @connection = Connection.new({'key1' => 'value 1', 'key2' => 'value 2'})
7
+ end
8
+
9
+ context 'after defining a function interface' do
10
+ before :each do
11
+ @connection.functions do
12
+ no_results :none
13
+ one_result :one
14
+ many_results :many
15
+ end
16
+ end
17
+
18
+ it 'should respond to the configured methods' do
19
+ expect(@connection).to respond_to :none
20
+ expect(@connection).to respond_to :one
21
+ expect(@connection).to respond_to :many
22
+ end
23
+
24
+ it 'should only define methods on the connection instance that is configured' do
25
+ expect { Connection.new.none }.to raise_error NoMethodError
26
+ expect { Connection.new.one }.to raise_error NoMethodError
27
+ expect { Connection.new.many }.to raise_error NoMethodError
28
+ end
29
+
30
+ context 'a method defined by #no_results' do
31
+ before :each do
32
+ @connection.stub(:execute_function)
33
+ end
34
+
35
+ it 'should return nil' do
36
+ expect(@connection.none).to be_nil
37
+ end
38
+
39
+ it 'should execute the function with the correct name and arguments' do
40
+ expect(@connection).to receive(:execute_function).with(:none, 'arg 1', 'arg 2')
41
+ @connection.none('arg 1', 'arg 2')
42
+ end
43
+ end
44
+
45
+ context 'a method defined by #one_result' do
46
+ it 'should execute the function with the correct name and arguments' do
47
+ @connection.stub(:execute_function).and_return([])
48
+ expect(@connection).to receive(:execute_function).with(:one, 'arg 1', 'arg 2')
49
+ @connection.one('arg 1', 'arg 2')
50
+ end
51
+
52
+ context 'that has results' do
53
+ before :each do
54
+ @connection.stub(:execute_function).and_return([{
55
+ 'key 1' => 'value 1',
56
+ 'key 2' => 'value 2'
57
+ }])
58
+ end
59
+
60
+ it 'should return the first result returned by the connection' do
61
+ expect(@connection.one).to eql({
62
+ 'key 1' => 'value 1',
63
+ 'key 2' => 'value 2'
64
+ })
65
+ end
66
+ end
67
+
68
+ context 'that has no results' do
69
+ before :each do
70
+ @connection.stub(:execute_function).and_return([])
71
+ end
72
+
73
+ it 'should return nil' do
74
+ expect(@connection.one).to be_nil
75
+ end
76
+ end
77
+ end
78
+
79
+ context 'a method defined by #many_results' do
80
+ it 'should execute the function with the correct name and arguments' do
81
+ @connection.stub(:execute_function).and_return([])
82
+ @connection.should receive(:execute_function).with(:many, 'arg 1', 'arg 2')
83
+ @connection.many('arg 1', 'arg 2')
84
+ end
85
+
86
+ context 'that has results' do
87
+ before :each do
88
+ @connection.stub(:execute_function).and_return([
89
+ {
90
+ 'key 1' => 'value 1',
91
+ 'key 2' => 'value 2'
92
+ },
93
+ {
94
+ 'key 3' => 'value 3',
95
+ 'key 4' => 'value 4'
96
+ }
97
+ ])
98
+ end
99
+
100
+ it 'should return all results returned by the connection' do
101
+ expect(@connection.many).to eql([
102
+ {
103
+ 'key 1' => 'value 1',
104
+ 'key 2' => 'value 2'
105
+ },
106
+ {
107
+ 'key 3' => 'value 3',
108
+ 'key 4' => 'value 4'
109
+ }
110
+ ])
111
+ end
112
+ end
113
+
114
+ context 'that has no results' do
115
+ context 'when passed no records' do
116
+ before :each do
117
+ @connection.stub(:execute_function).and_return []
118
+ end
119
+
120
+ it 'should return an empty array' do
121
+ expect(@connection.many).to eql []
122
+ end
123
+ end
124
+ end
125
+ end
126
+ end
127
+
128
+ context '#build_sql_string' do
129
+ it 'should raise an error if an unsupported type is passed in' do
130
+ expect { @connection.instance_eval { build_sql_string('function_name', Class.new, 'arg2') }}.to raise_error Tusks::UnsupportedTypeError
131
+ end
132
+
133
+ context 'with arguments' do
134
+ it 'should build the proper SQL string' do
135
+ arg1 = double('arg1')
136
+ arg2 = double('arg2')
137
+ arg1.stub(:to_pg_s).and_return('arg1#to_pg_s')
138
+ arg2.stub(:to_pg_s).and_return('arg2#to_pg_s')
139
+
140
+ expect(@connection.instance_eval {
141
+ build_sql_string(
142
+ 'function_name',
143
+ arg1,
144
+ arg2
145
+ )}).to eql 'SELECT * FROM function_name(arg1#to_pg_s,arg2#to_pg_s)'
146
+ end
147
+ end
148
+
149
+ context 'without arguments' do
150
+ it 'should build the proper SQL string' do
151
+ @connection.instance_eval { build_sql_string('function_name') }.should eql 'SELECT * FROM function_name()'
152
+ end
153
+ end
154
+ end
155
+
156
+ context 'when first created' do
157
+ it 'should set the passed in options as the connection options' do
158
+ @connection.instance_variable_get(:@options).should eql ({
159
+ 'key1' => 'value 1',
160
+ 'key2' => 'value 2'
161
+ })
162
+ end
163
+
164
+ it 'should not claim to be in a transaction' do
165
+ expect(@connection.in_transaction?).to be false
166
+ end
167
+
168
+ it 'should not claim to be connected' do
169
+ expect(@connection.connected?).to be false
170
+ end
171
+ end
172
+ end
173
+ end
@@ -0,0 +1,58 @@
1
+ require 'spec_helper'
2
+
3
+ module Tusks
4
+ describe Array do
5
+ before :each do
6
+ @arr = []
7
+ end
8
+
9
+ context '#to_pg_s' do
10
+ context 'holding zero elements' do
11
+ it 'should serialize to an empty Postgres array constructor' do
12
+ expect(@arr.to_pg_s).to eql 'ARRAY[]'
13
+ end
14
+ end
15
+
16
+ context 'holding one element' do
17
+ before :each do
18
+ @arr << 'element'
19
+ end
20
+
21
+ it 'should serialize to a Postgres array constructor holding a serialized version of that element' do
22
+ expect(@arr.to_pg_s).to eql 'ARRAY[\'element\']'
23
+ end
24
+ end
25
+
26
+ context 'holding many elements' do
27
+ before :each do
28
+ @arr << 'element1'
29
+ @arr << 'element2'
30
+ end
31
+
32
+ it 'should serialize to a Postgres array constructor holding serialized versions of those elements' do
33
+ expect(@arr.to_pg_s).to eql 'ARRAY[\'element1\',\'element2\']'
34
+ end
35
+ end
36
+
37
+ context 'holding unsupported types' do
38
+ before :each do
39
+ @arr << double
40
+ end
41
+
42
+ it 'should raise a Tusks::UnsupportedTypeError' do
43
+ expect { @arr.to_pg_s }.to raise_error Tusks::UnsupportedTypeError
44
+ end
45
+ end
46
+
47
+ context 'holding nested arrays' do
48
+ before :each do
49
+ @arr << ['asdf']
50
+ end
51
+
52
+ it 'should raise a Tusks::NestedArrayError' do
53
+ expect { @arr.to_pg_s }.to raise_error Tusks::NestedArrayError
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,17 @@
1
+ require 'spec_helper'
2
+
3
+ module Tusks
4
+ describe Float do
5
+ before :each do
6
+ @float1 = 1.23
7
+ @float2 = 3.21
8
+ end
9
+
10
+ context '#to_pg_s' do
11
+ it 'should serialize to a string representation of its value' do
12
+ expect(@float1.to_pg_s).to eql '1.23'
13
+ expect(@float2.to_pg_s).to eql '3.21'
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,16 @@
1
+ require 'spec_helper'
2
+
3
+ module Tusks
4
+ describe Hash do
5
+ context '#to_pg_s' do
6
+ before :each do
7
+ @hash = {}
8
+ end
9
+
10
+ it 'should serialize itself to a json string using #to_json' do
11
+ expect(@hash).to receive :to_json
12
+ @hash.to_pg_s
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,17 @@
1
+ require 'spec_helper'
2
+
3
+ module Tusks
4
+ describe Integer do
5
+ before :each do
6
+ @int1 = 999
7
+ @int2 = 100
8
+ end
9
+
10
+ context '#to_pg_s' do
11
+ it 'should serialize to a string representation of its value' do
12
+ expect(@int1.to_pg_s).to eql '999'
13
+ expect(@int2.to_pg_s).to eql '100'
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,15 @@
1
+ require 'spec_helper'
2
+
3
+ module Tusks
4
+ describe NilClass do
5
+ before :each do
6
+ @nil = nil
7
+ end
8
+
9
+ context '#to_pg_s' do
10
+ it 'should serialize to NULL' do
11
+ expect(@nil.to_pg_s).to eql 'NULL'
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,37 @@
1
+ require 'spec_helper'
2
+
3
+ module Tusks
4
+ describe String do
5
+ context '#to_pg_s' do
6
+ context 'of zero length' do
7
+ before :each do
8
+ @str = ''
9
+ end
10
+
11
+ it 'should serialize to two escaped single quotes, side-by-side' do
12
+ expect(@str.to_pg_s).to eql '\'\''
13
+ end
14
+ end
15
+
16
+ context 'of non-zero length' do
17
+ before :each do
18
+ @str = 'this is a string'
19
+ end
20
+
21
+ it 'should surround itself by escaped single quotes' do
22
+ expect(@str.to_pg_s).to eql '\'this is a string\''
23
+ end
24
+ end
25
+
26
+ context 'containting single quotes' do
27
+ before :each do
28
+ @str = '"It\'s a test", he said'
29
+ end
30
+
31
+ it 'should escape them by prepending them with another single quote' do
32
+ expect(@str.to_pg_s).to eql '\'"It\'\'s a test", he said\''
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
data/tusks.gemspec ADDED
@@ -0,0 +1,25 @@
1
+ lib = File.expand_path('../lib/', __FILE__)
2
+ $:.unshift lib unless $:.include?(lib)
3
+ require 'tusks/version'
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = 'tusks'
7
+ s.version = Tusks::VERSION
8
+ s.license = 'MIT'
9
+ s.platform = Gem::Platform::RUBY
10
+ s.author = 'Craig McCown'
11
+ s.email = 'craigrmccown@gmail.com'
12
+ s.homepage = 'https://github.com/craigrmccown/tusks'
13
+ s.summary = 'Easily call PostgreSQL functions from Ruby.'
14
+ s.description = 'Tusks is built for Ruby application developers who want to access their Postgres database exclusively using functions. It abstracts away ugly serialization functionality, enables easy transaction management, and provides a declarative way to define a clean interface to your Postgres functions.'
15
+ s.post_install_message = 'Thanks for using Tusks! Log and track issues at: https://github.com/craigrmccown/tusks/issues'
16
+
17
+ s.required_ruby_version = '2.1.0'
18
+ s.required_rubygems_version = '2.2.2'
19
+
20
+ s.files = `git ls-files -z`.split("\x0")
21
+ s.test_files = Dir.glob('spec/**/*')
22
+ s.require_paths = ['lib']
23
+
24
+ s.add_development_dependency 'rspec', '~> 3.0', '>= 3.0.0'
25
+ end
metadata ADDED
@@ -0,0 +1,101 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: tusks
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Craig McCown
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-06-16 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rspec
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '3.0'
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 3.0.0
23
+ type: :development
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - "~>"
28
+ - !ruby/object:Gem::Version
29
+ version: '3.0'
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: 3.0.0
33
+ description: Tusks is built for Ruby application developers who want to access their
34
+ Postgres database exclusively using functions. It abstracts away ugly serialization
35
+ functionality, enables easy transaction management, and provides a declarative way
36
+ to define a clean interface to your Postgres functions.
37
+ email: craigrmccown@gmail.com
38
+ executables: []
39
+ extensions: []
40
+ extra_rdoc_files: []
41
+ files:
42
+ - ".gitignore"
43
+ - ".rspec"
44
+ - LICENSE.md
45
+ - README.md
46
+ - lib/tusks.rb
47
+ - lib/tusks/connection.rb
48
+ - lib/tusks/error/init.rb
49
+ - lib/tusks/error/nested_array_error.rb
50
+ - lib/tusks/error/tusks_error.rb
51
+ - lib/tusks/error/unsupported_type_error.rb
52
+ - lib/tusks/types/array.rb
53
+ - lib/tusks/types/float.rb
54
+ - lib/tusks/types/hash.rb
55
+ - lib/tusks/types/init.rb
56
+ - lib/tusks/types/integer.rb
57
+ - lib/tusks/types/nil.rb
58
+ - lib/tusks/types/string.rb
59
+ - lib/tusks/version.rb
60
+ - spec/spec_helper.rb
61
+ - spec/tusks/connection_spec.rb
62
+ - spec/tusks/types/array_spec.rb
63
+ - spec/tusks/types/float_spec.rb
64
+ - spec/tusks/types/hash_spec.rb
65
+ - spec/tusks/types/integer_spec.rb
66
+ - spec/tusks/types/nil_spec.rb
67
+ - spec/tusks/types/string_spec.rb
68
+ - tusks.gemspec
69
+ homepage: https://github.com/craigrmccown/tusks
70
+ licenses:
71
+ - MIT
72
+ metadata: {}
73
+ post_install_message: 'Thanks for using Tusks! Log and track issues at: https://github.com/craigrmccown/tusks/issues'
74
+ rdoc_options: []
75
+ require_paths:
76
+ - lib
77
+ required_ruby_version: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - '='
80
+ - !ruby/object:Gem::Version
81
+ version: 2.1.0
82
+ required_rubygems_version: !ruby/object:Gem::Requirement
83
+ requirements:
84
+ - - '='
85
+ - !ruby/object:Gem::Version
86
+ version: 2.2.2
87
+ requirements: []
88
+ rubyforge_project:
89
+ rubygems_version: 2.2.2
90
+ signing_key:
91
+ specification_version: 4
92
+ summary: Easily call PostgreSQL functions from Ruby.
93
+ test_files:
94
+ - spec/spec_helper.rb
95
+ - spec/tusks/connection_spec.rb
96
+ - spec/tusks/types/array_spec.rb
97
+ - spec/tusks/types/float_spec.rb
98
+ - spec/tusks/types/hash_spec.rb
99
+ - spec/tusks/types/integer_spec.rb
100
+ - spec/tusks/types/nil_spec.rb
101
+ - spec/tusks/types/string_spec.rb