db_mod 0.0.1 → 0.0.2
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.
- checksums.yaml +4 -4
- data/README.md +18 -3
- data/db_mod.gemspec +6 -4
- data/lib/db_mod.rb +1 -1
- data/lib/db_mod/statements.rb +6 -0
- data/lib/db_mod/statements/{params.rb → parameters.rb} +67 -21
- data/lib/db_mod/statements/prepared.rb +24 -37
- data/lib/db_mod/statements/statement.rb +98 -1
- data/lib/db_mod/version.rb +1 -1
- data/spec/db_mod/statements/prepared_spec.rb +23 -0
- data/spec/db_mod/statements/statement_spec.rb +130 -0
- metadata +9 -21
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5547dcc1f21c6ca964d25c097abb813e77713161
|
4
|
+
data.tar.gz: 2214d888e4d39fb545e65e2c8539fdeadc48b145
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6e5bd968a9b47e6d55972444b55bb7bf0434938711c71d0c6955b6d6d048c25a7bfb0e78bffec87bc70dbe3c58aec5766513530e63104c24a2a1089ba99a91c0
|
7
|
+
data.tar.gz: 28d48191ba234d83a0dc52dc5c9224923712834d8b8411e4e67d081bc36ab45f062f5476e7e0d17f2eb0222cb89065bdb5f657d84230c765bc2f1208ce26a0dc
|
data/README.md
CHANGED
@@ -8,6 +8,12 @@ The `db_mod` gem is a simple framework that helps you organise your
|
|
8
8
|
database access functions into modular libraries that can be included
|
9
9
|
in your projects to give them selective access to facets of your data.
|
10
10
|
|
11
|
+
For the moment `db_mod` only supports PostgreSQL databases via the
|
12
|
+
`pg` gem. This gem is still in the early stages of development and no
|
13
|
+
guarantees will be made about backwards compatibility until v0.1.0.
|
14
|
+
|
15
|
+
Issues, pull requests, comments and feedback all welcomed.
|
16
|
+
|
11
17
|
## Usage
|
12
18
|
|
13
19
|
### The database connection
|
@@ -53,7 +59,7 @@ get_stuff.each do |thing|
|
|
53
59
|
end
|
54
60
|
```
|
55
61
|
|
56
|
-
####
|
62
|
+
#### Module instances: `DbMod.create`
|
57
63
|
|
58
64
|
Each module also comes with its own `create` function,
|
59
65
|
which instantiates an object exposing all of the module's functions.
|
@@ -69,7 +75,7 @@ db = MyFunctions.create PGconn.connect # ...
|
|
69
75
|
db.get_stuff
|
70
76
|
```
|
71
77
|
|
72
|
-
####
|
78
|
+
#### The connection object: `@conn`
|
73
79
|
|
74
80
|
The connection created by `db_connect` or `create` will be stored
|
75
81
|
in the instance variable `@conn`. This instance variable may be
|
@@ -104,7 +110,9 @@ db = DbAccess.create db: 'mydb'
|
|
104
110
|
db.things_n_stuff
|
105
111
|
```
|
106
112
|
|
107
|
-
###
|
113
|
+
### Declaring SQL statements
|
114
|
+
|
115
|
+
#### Prepared statement methods: `DbMod.def_prepared`
|
108
116
|
|
109
117
|
Modules which include `DbMod` can declare prepared statements
|
110
118
|
using the module function `def_prepared`. These statements will
|
@@ -168,3 +176,10 @@ modules on the given connection.
|
|
168
176
|
db = Db::ComplicatedStuff.create my_conn
|
169
177
|
Db::ComplicatedStuff.prepare_all_statements my_conn
|
170
178
|
```
|
179
|
+
|
180
|
+
#### Saved statement methods: `DbMod.def_statement`
|
181
|
+
|
182
|
+
`def_statement` works in just the same way as `def_prepared`, except that
|
183
|
+
the SQL queries are saved in memory rather than being sent to the database
|
184
|
+
at connection time. This is useful for queries that will only be run once
|
185
|
+
or twice during a program's execution.
|
data/db_mod.gemspec
CHANGED
@@ -5,14 +5,16 @@ Gem::Specification.new do |s|
|
|
5
5
|
s.name = 'db_mod'
|
6
6
|
s.version = DbMod::VERSION
|
7
7
|
s.platform = Gem::Platform::RUBY
|
8
|
-
s.
|
8
|
+
s.author = 'Doug Hammond'
|
9
9
|
s.email = ['d.lakehammond@gmail.com']
|
10
|
-
s.
|
11
|
-
s.
|
10
|
+
s.homepage = 'https://github.com/dslh/db_mod'
|
11
|
+
s.summary = 'Declarative, modular database library framework.'
|
12
|
+
s.description = 'Framework for building modular database libraries.'
|
12
13
|
s.license = 'MIT'
|
13
14
|
|
15
|
+
s.required_ruby_version = '>= 1.9.3'
|
16
|
+
|
14
17
|
s.add_runtime_dependency 'pg'
|
15
|
-
s.add_runtime_dependency 'docile'
|
16
18
|
|
17
19
|
s.add_development_dependency 'simplecov'
|
18
20
|
s.add_development_dependency 'rspec'
|
data/lib/db_mod.rb
CHANGED
data/lib/db_mod/statements.rb
CHANGED
@@ -9,5 +9,11 @@ module DbMod
|
|
9
9
|
# See {DbMod::Statements::Statement} for details on +def_statement+
|
10
10
|
# and {DbMod::Statements::Prepared} for details on +def_prepared+.
|
11
11
|
module Statements
|
12
|
+
# Called when a module includes {DbMod},
|
13
|
+
# defines module-level +def_statement+ and +def_prepared+ dsl methods.
|
14
|
+
def self.setup(mod)
|
15
|
+
DbMod::Statements::Prepared.setup(mod)
|
16
|
+
DbMod::Statements::Statement.setup(mod)
|
17
|
+
end
|
12
18
|
end
|
13
19
|
end
|
@@ -2,36 +2,41 @@ module DbMod
|
|
2
2
|
module Statements
|
3
3
|
# Parsing and validation of query parameters
|
4
4
|
# for prepared SQL statements
|
5
|
-
module
|
5
|
+
module Parameters
|
6
|
+
# Called when a {DbMod} dynamically defined method is called.
|
6
7
|
# Assert that the named arguments given for the prepared statement
|
7
|
-
# with the given name satisfy expectations.
|
8
|
+
# with the given name satisfy expectations. Returns a parameter array
|
9
|
+
# as per {Parameters.parameter_array}.
|
8
10
|
#
|
9
11
|
# @param expected [Array<Symbol>] the parameters expected to be present
|
10
|
-
# @param args [Hash] given
|
12
|
+
# @param args [Array<Hash<Symbol>>] arguments given to the method being
|
13
|
+
# executed. The method should only be called with an options hash that
|
14
|
+
# contains exactly the parameter names given when the method was
|
15
|
+
# defined.
|
11
16
|
# @return [Array] values to be passed to the prepared statement
|
12
17
|
def self.valid_named_args!(expected, args)
|
13
|
-
|
14
|
-
fail ArgumentError, "invalid argument: #{args.inspect}"
|
15
|
-
end
|
18
|
+
wrapped_hash! args
|
16
19
|
|
20
|
+
args = args.first
|
17
21
|
if args.size != expected.size
|
18
22
|
fail ArgumentError, "#{args.size} args given, #{expected.size} needed"
|
19
23
|
end
|
20
24
|
|
21
|
-
expected
|
22
|
-
args[arg] || fail(ArgumentError, "missing arg #{arg}")
|
23
|
-
end
|
25
|
+
parameter_array(expected, args)
|
24
26
|
end
|
25
27
|
|
26
|
-
#
|
27
|
-
|
28
|
-
|
29
|
-
#
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
28
|
+
# Called when a {DbMod} dynamically defined method is called.
|
29
|
+
# Assert that the correct number of arguments has been provided.
|
30
|
+
#
|
31
|
+
# @param count [Fixnum] arity of the method being called.
|
32
|
+
# @param args [Array] list of arguments given.
|
33
|
+
def self.valid_fixed_args!(count, args)
|
34
|
+
unless args.size == count
|
35
|
+
fail ArgumentError, "#{args.size} args given, #{count} expected"
|
36
|
+
end
|
37
|
+
end
|
34
38
|
|
39
|
+
# Called when a {DbMod} dynamically defined method is declared.
|
35
40
|
# Parses parameters, named or numbered, from an SQL
|
36
41
|
# statement. See the {Prepared} module documentation
|
37
42
|
# for more. This method may modify the sql statement
|
@@ -47,15 +52,56 @@ module DbMod
|
|
47
52
|
# @return [Fixnum,Array<Symbol>] description of
|
48
53
|
# prepared statement's parameters
|
49
54
|
def self.parse_params!(sql)
|
50
|
-
|
55
|
+
Parameters.valid_sql_params! sql
|
51
56
|
numbered = sql.scan NUMBERED_PARAM
|
52
57
|
named = sql.scan NAMED_PARAM
|
53
58
|
|
54
59
|
if numbered.any?
|
55
60
|
fail ArgumentError, 'mixed named and numbered params' if named.any?
|
56
|
-
|
61
|
+
Parameters.parse_numbered_params! numbered
|
57
62
|
else
|
58
|
-
|
63
|
+
Parameters.parse_named_params! sql, named
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# Regex matching a numbered parameter
|
68
|
+
NUMBERED_PARAM = /\$\d+/
|
69
|
+
|
70
|
+
# Regex matching a named parameter
|
71
|
+
NAMED_PARAM = /\$[a-z]+(?:_[a-z]+)*/
|
72
|
+
|
73
|
+
# For validation, named or numbered parameter
|
74
|
+
NAMED_OR_NUMBERED = /^\$(?:\d+|[a-z]+(?:_[a-z]+)*)$/
|
75
|
+
|
76
|
+
private
|
77
|
+
|
78
|
+
# Assert that the given parameter list is an array
|
79
|
+
# containing a single hash of named parameter values.
|
80
|
+
#
|
81
|
+
# Raises {ArgumentError} otherwise.
|
82
|
+
#
|
83
|
+
# @param args [Array<Hash<Symbol>>] method arguments being validated
|
84
|
+
def self.wrapped_hash!(args)
|
85
|
+
unless args.size == 1
|
86
|
+
fail ArgumentError, "unexpected arguments: #{args.inspect}"
|
87
|
+
end
|
88
|
+
|
89
|
+
unless args.first.is_a? Hash
|
90
|
+
fail ArgumentError, "invalid argument: #{args.first.inspect}"
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
# Convert the given named parameter hash into
|
95
|
+
# an array containing the parameter values in
|
96
|
+
# the order required to be supplied to the
|
97
|
+
# SQL statement being executed.
|
98
|
+
#
|
99
|
+
# @param expected [Array<Symbol>] the parameters expected to be present
|
100
|
+
# @param args [Hash] given parameters
|
101
|
+
# @return [Array] values to be passed to the prepared statement
|
102
|
+
def self.parameter_array(expected, args)
|
103
|
+
expected.map do |arg|
|
104
|
+
args[arg] || fail(ArgumentError, "missing arg #{arg}")
|
59
105
|
end
|
60
106
|
end
|
61
107
|
|
@@ -63,7 +109,7 @@ module DbMod
|
|
63
109
|
# in the expected format. They must either be
|
64
110
|
# lower_case_a_to_z or digits only.
|
65
111
|
def self.valid_sql_params!(sql)
|
66
|
-
sql.scan(
|
112
|
+
sql.scan(/\$[A-Za-z0-9_]+/) do |param|
|
67
113
|
unless param =~ NAMED_OR_NUMBERED
|
68
114
|
fail ArgumentError, "Invalid parameter #{param}"
|
69
115
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require_relative '
|
1
|
+
require_relative 'parameters'
|
2
2
|
|
3
3
|
module DbMod
|
4
4
|
module Statements
|
@@ -48,8 +48,6 @@ module DbMod
|
|
48
48
|
# my_prepared(1,2)
|
49
49
|
# my_named_prepared(a: 1, b: 2)
|
50
50
|
module Prepared
|
51
|
-
include Params
|
52
|
-
|
53
51
|
# Defines a module-specific +def_prepared+ function
|
54
52
|
# for a module that has just had {DbMod} included.
|
55
53
|
#
|
@@ -63,24 +61,6 @@ module DbMod
|
|
63
61
|
|
64
62
|
private
|
65
63
|
|
66
|
-
# Merge the prepared statements from a module
|
67
|
-
# into a given hash. Fails if there are any
|
68
|
-
# duplicates.
|
69
|
-
#
|
70
|
-
# @param statements [Hash] named list of prepared statements
|
71
|
-
# @param klass [Class,Module] ancestor (hopefully a DbMod module)
|
72
|
-
# to collect prepared statements from
|
73
|
-
def self.merge_statements(statements, klass)
|
74
|
-
return unless klass.respond_to? :prepared_statements
|
75
|
-
return if klass.prepared_statements.nil?
|
76
|
-
|
77
|
-
klass.prepared_statements.each do |name, sql|
|
78
|
-
fail DbMod::Exceptions::DuplicateStatementName if statements.key? name
|
79
|
-
|
80
|
-
statements[name] = sql
|
81
|
-
end
|
82
|
-
end
|
83
|
-
|
84
64
|
# Add a +def_prepared+ method definition to a module.
|
85
65
|
# This method allows modules to declare named SQL statements
|
86
66
|
# that will be prepared when the database connection is
|
@@ -94,7 +74,7 @@ module DbMod
|
|
94
74
|
sql = sql.dup
|
95
75
|
name = name.to_sym
|
96
76
|
|
97
|
-
params =
|
77
|
+
params = Parameters.parse_params! sql
|
98
78
|
prepared_statements[name] = sql
|
99
79
|
Prepared.define_prepared_method(mod, name, params)
|
100
80
|
end
|
@@ -117,18 +97,34 @@ module DbMod
|
|
117
97
|
end
|
118
98
|
end
|
119
99
|
|
120
|
-
#
|
100
|
+
# Merge the prepared statements from a module
|
101
|
+
# into a given hash. Fails if there are any
|
102
|
+
# duplicates.
|
103
|
+
#
|
104
|
+
# @param statements [Hash] named list of prepared statements
|
105
|
+
# @param klass [Class,Module] ancestor (hopefully a DbMod module)
|
106
|
+
# to collect prepared statements from
|
107
|
+
def self.merge_statements(statements, klass)
|
108
|
+
return unless klass.respond_to? :prepared_statements
|
109
|
+
return if klass.prepared_statements.nil?
|
110
|
+
|
111
|
+
klass.prepared_statements.each do |name, sql|
|
112
|
+
fail DbMod::Exceptions::DuplicateStatementName if statements.key? name
|
113
|
+
|
114
|
+
statements[name] = sql
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
# Define a method in the given module with the given name
|
121
119
|
# and parameters, that will call the prepared statement
|
122
120
|
# with the same name.
|
123
121
|
#
|
124
|
-
# @param mod [Module] module declaring the
|
122
|
+
# @param mod [Module] module declaring the method
|
125
123
|
# @param name [Symbol] method name
|
126
124
|
# @param params [Fixnum,Array<Symbol>]
|
127
125
|
# expected parameter count, or a list of argument names.
|
128
126
|
# An empty array produces a no-argument method.
|
129
127
|
def self.define_prepared_method(mod, name, params)
|
130
|
-
mod.expected_prepared_statement_parameters[name] = params
|
131
|
-
|
132
128
|
if params.is_a?(Array)
|
133
129
|
if params.empty?
|
134
130
|
define_no_args_prepared_method(mod, name)
|
@@ -165,10 +161,7 @@ module DbMod
|
|
165
161
|
# @param params [Array<Symbol>] list of parameter names
|
166
162
|
def self.define_named_args_prepared_method(mod, name, params)
|
167
163
|
method = lambda do |*args|
|
168
|
-
|
169
|
-
fail ArgumentError, "unexpected arguments: #{args.inspect}"
|
170
|
-
end
|
171
|
-
args = Params.valid_named_args! params, args.first
|
164
|
+
args = Parameters.valid_named_args! params, args
|
172
165
|
conn.exec_prepared(name.to_s, args)
|
173
166
|
end
|
174
167
|
|
@@ -188,9 +181,7 @@ module DbMod
|
|
188
181
|
# requires
|
189
182
|
def self.define_fixed_args_prepared_method(mod, name, count)
|
190
183
|
method = lambda do |*args|
|
191
|
-
|
192
|
-
fail ArgumentError, "#{args.size} args given, #{count} expected"
|
193
|
-
end
|
184
|
+
Parameters.valid_fixed_args!(count, args)
|
194
185
|
|
195
186
|
conn.exec_prepared(name.to_s, args)
|
196
187
|
end
|
@@ -208,10 +199,6 @@ module DbMod
|
|
208
199
|
define_method(:prepared_statements) do
|
209
200
|
@prepared_statements ||= {}
|
210
201
|
end
|
211
|
-
|
212
|
-
define_method(:expected_prepared_statement_parameters) do
|
213
|
-
@expected_prepared_statement_parameters ||= {}
|
214
|
-
end
|
215
202
|
end
|
216
203
|
end
|
217
204
|
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require_relative 'parameters'
|
2
|
+
|
1
3
|
module DbMod
|
2
4
|
module Statements
|
3
5
|
# Provides the +def_statement+ function which allows
|
@@ -23,7 +25,102 @@ module DbMod
|
|
23
25
|
# can then be passed parameters that will be used to fill
|
24
26
|
# in the statement before execution.
|
25
27
|
module Statement
|
26
|
-
#
|
28
|
+
# Defines a module-specific +def_statement+ function
|
29
|
+
# for a module that has just had {DbMod} included.
|
30
|
+
#
|
31
|
+
# @param mod [Module]
|
32
|
+
def self.setup(mod)
|
33
|
+
Statement.define_def_statement(mod)
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
# Add a +def_statement+ method definition to a module.
|
39
|
+
# This method allows modules to declare SQL statements
|
40
|
+
# that can be accessed via an instance method with
|
41
|
+
# arbitrary name.
|
42
|
+
#
|
43
|
+
# @param mod [Module] a module with {DbMod} included
|
44
|
+
def self.define_def_statement(mod)
|
45
|
+
mod.class.instance_eval do
|
46
|
+
define_method(:def_statement) do |name, sql|
|
47
|
+
sql = sql.dup
|
48
|
+
name = name.to_sym
|
49
|
+
|
50
|
+
params = Parameters.parse_params! sql
|
51
|
+
Statement.define_statement_method(mod, name, params, sql)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# Define a method in the given module with the given name
|
57
|
+
# and parameters, that will call the given sql statement
|
58
|
+
# and return the results.
|
59
|
+
#
|
60
|
+
# @param mod [Module] module declaring the method
|
61
|
+
# @param name [Symbol] method name
|
62
|
+
# @param params [Fixnum,Array<Symbol>]
|
63
|
+
# expected parameter count, or a list of argument names.
|
64
|
+
# An empty array produces a no-argument method.
|
65
|
+
def self.define_statement_method(mod, name, params, sql)
|
66
|
+
if params.is_a?(Array)
|
67
|
+
if params.empty?
|
68
|
+
define_no_args_statement_method(mod, name, sql)
|
69
|
+
else
|
70
|
+
define_named_args_statement_method(mod, name, params, sql)
|
71
|
+
end
|
72
|
+
else
|
73
|
+
define_fixed_args_statement_method(mod, name, params, sql)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# Define a no-argument method with the given name
|
78
|
+
# that will execute the given sql statement and return
|
79
|
+
# the result.
|
80
|
+
#
|
81
|
+
# @param mod [Module] {DbMod} enabled module
|
82
|
+
# where the method will be defined
|
83
|
+
# @param name [Symbol] name of the method to be defined
|
84
|
+
# @param sql [String] parameterless SQL statement to execute
|
85
|
+
def self.define_no_args_statement_method(mod, name, sql)
|
86
|
+
mod.instance_eval do
|
87
|
+
define_method name, ->() { query(sql) }
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
# Define a method with the given name, that accepts the
|
92
|
+
# given set of named parameters that will be used to execute
|
93
|
+
# the given SQL query.
|
94
|
+
#
|
95
|
+
# @param mod [Module] {DbMod} enabled module
|
96
|
+
# @param name [Symbol] name of the method to be defined
|
97
|
+
# @param params [Array<Symbol>] parameter names and order
|
98
|
+
def self.define_named_args_statement_method(mod, name, params, sql)
|
99
|
+
method = lambda do |*args|
|
100
|
+
args = Parameters.valid_named_args! params, args
|
101
|
+
conn.exec_params(sql, args)
|
102
|
+
end
|
103
|
+
|
104
|
+
mod.instance_eval { define_method(name, method) }
|
105
|
+
end
|
106
|
+
|
107
|
+
# Define a method with the given name that accepts a fixed number
|
108
|
+
# of arguments, that will be used to execute the given SQL query.
|
109
|
+
#
|
110
|
+
# @param mod [Module] {DbMod} enabled module where the method
|
111
|
+
# will be defined
|
112
|
+
# @param name [Symbol] name of the method to be defined
|
113
|
+
# @param count [Fixnum] arity of the defined method,
|
114
|
+
# the number of parameters that the SQL statement requires
|
115
|
+
def self.define_fixed_args_statement_method(mod, name, count, sql)
|
116
|
+
method = lambda do |*args|
|
117
|
+
Parameters.valid_fixed_args!(count, args)
|
118
|
+
|
119
|
+
conn.exec_params(sql, args)
|
120
|
+
end
|
121
|
+
|
122
|
+
mod.instance_eval { define_method(name, method) }
|
123
|
+
end
|
27
124
|
end
|
28
125
|
end
|
29
126
|
end
|
data/lib/db_mod/version.rb
CHANGED
@@ -108,6 +108,29 @@ describe DbMod::Statements::Prepared do
|
|
108
108
|
end.not_to raise_exception
|
109
109
|
end
|
110
110
|
|
111
|
+
it 'handles complicated parameter usage' do
|
112
|
+
mod = Module.new do
|
113
|
+
include DbMod
|
114
|
+
|
115
|
+
def_prepared(
|
116
|
+
:params_test,
|
117
|
+
'INSERT INTO foo (a,b,c,d) VALUES ($a-1,$b::integer,$c*2,$b)'
|
118
|
+
)
|
119
|
+
end
|
120
|
+
|
121
|
+
expect(@conn).to receive(:prepare).with(
|
122
|
+
'params_test',
|
123
|
+
'INSERT INTO foo (a,b,c,d) VALUES ($1-1,$2::integer,$3*2,$2)'
|
124
|
+
)
|
125
|
+
db = mod.create(db: 'testdb')
|
126
|
+
|
127
|
+
expect(@conn).to receive(:exec_prepared).with(
|
128
|
+
'params_test',
|
129
|
+
[1, 2, 3]
|
130
|
+
)
|
131
|
+
db.params_test(a: 1, b: 2, c: 3)
|
132
|
+
end
|
133
|
+
|
111
134
|
it 'does not allow invalid parameters' do
|
112
135
|
%w(CAPITALS numb3rs_and_l3tt3rs).each do |param|
|
113
136
|
expect do
|
@@ -0,0 +1,130 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe DbMod::Statements::Statement do
|
4
|
+
subject do
|
5
|
+
Module.new do
|
6
|
+
include DbMod
|
7
|
+
|
8
|
+
def_statement(
|
9
|
+
:one,
|
10
|
+
'SELECT * FROM foo WHERE a = $1 AND b = $2 AND c = $1'
|
11
|
+
)
|
12
|
+
|
13
|
+
def_statement(
|
14
|
+
:two,
|
15
|
+
'SELECT * FROM foo WHERE a = $a AND b = $b AND c = $a'
|
16
|
+
)
|
17
|
+
end.create db: 'testdb'
|
18
|
+
end
|
19
|
+
|
20
|
+
before do
|
21
|
+
@conn = instance_double 'PGconn'
|
22
|
+
allow(PGconn).to receive(:connect).and_return(@conn)
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'executes statements with numbered params' do
|
26
|
+
expect(@conn).to receive(:exec_params).with(
|
27
|
+
'SELECT * FROM foo WHERE a = $1 AND b = $2 AND c = $1',
|
28
|
+
[1, 'two']
|
29
|
+
)
|
30
|
+
subject.one(1, 'two')
|
31
|
+
|
32
|
+
expect { subject.one 'not enough args' }.to raise_exception ArgumentError
|
33
|
+
|
34
|
+
expect do
|
35
|
+
subject.one('too', 'many', 'args')
|
36
|
+
end.to raise_exception ArgumentError
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'executes statements with named params' do
|
40
|
+
expect(@conn).to receive(:exec_params).with(
|
41
|
+
'SELECT * FROM foo WHERE a = $1 AND b = $2 AND c = $1',
|
42
|
+
[1, 'two']
|
43
|
+
)
|
44
|
+
subject.two(a: 1, b: 'two')
|
45
|
+
|
46
|
+
expect do
|
47
|
+
subject.two bad: 'arg', b: 1, a: 1
|
48
|
+
end.to raise_exception ArgumentError
|
49
|
+
expect { subject.two b: 'a missing' }.to raise_exception ArgumentError
|
50
|
+
expect { subject.two 1, 2 }.to raise_exception ArgumentError
|
51
|
+
expect { subject.two 1 }.to raise_exception ArgumentError
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'allows statements with no parameters' do
|
55
|
+
expect do
|
56
|
+
mod = Module.new do
|
57
|
+
include DbMod
|
58
|
+
|
59
|
+
def_statement :no_params, 'SELECT 1'
|
60
|
+
end
|
61
|
+
|
62
|
+
expect(@conn).to receive(:query).with('SELECT 1')
|
63
|
+
db = mod.create db: 'testdb'
|
64
|
+
|
65
|
+
expect { db.no_params }.not_to raise_exception
|
66
|
+
expect { db.no_params(1) }.to raise_exception ArgumentError
|
67
|
+
end.not_to raise_exception
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'does not allow mixed parameter types' do
|
71
|
+
expect do
|
72
|
+
Module.new do
|
73
|
+
include DbMod
|
74
|
+
|
75
|
+
def_statement :numbers_and_names, <<-SQL
|
76
|
+
SELECT *
|
77
|
+
FROM foo
|
78
|
+
WHERE this = $1 AND wont = $work
|
79
|
+
SQL
|
80
|
+
end
|
81
|
+
end.to raise_exception ArgumentError
|
82
|
+
end
|
83
|
+
|
84
|
+
it 'handles complicated parameter usage' do
|
85
|
+
db = Module.new do
|
86
|
+
include DbMod
|
87
|
+
|
88
|
+
def_statement(
|
89
|
+
:params_test,
|
90
|
+
'INSERT INTO foo (a,b,c,d) VALUES ($a-1,$b::integer,$c*2,$b)'
|
91
|
+
)
|
92
|
+
end.create(db: 'testdb')
|
93
|
+
|
94
|
+
expect(@conn).to receive(:exec_params).with(
|
95
|
+
'INSERT INTO foo (a,b,c,d) VALUES ($1-1,$2::integer,$3*2,$2)',
|
96
|
+
[1, 2, 3]
|
97
|
+
)
|
98
|
+
db.params_test(a: 1, b: 2, c: 3)
|
99
|
+
end
|
100
|
+
|
101
|
+
it 'does not allow invalid parameters' do
|
102
|
+
%w(CAPITALS numb3s_and_l3tt3rs).each do |param|
|
103
|
+
expect do
|
104
|
+
Module.new do
|
105
|
+
include DbMod
|
106
|
+
|
107
|
+
def_statement :bad_params, %(
|
108
|
+
SELECT * FROM foo WHERE bad = $#{param}
|
109
|
+
)
|
110
|
+
end
|
111
|
+
end.to raise_exception ArgumentError
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
it 'validates numbered arguments' do
|
116
|
+
[
|
117
|
+
'a = $1 and b = $2 and c = $2 and c = $4',
|
118
|
+
'a = $2 and b = $2 and c = $3',
|
119
|
+
'a = $1 and b = $2 and c = $4'
|
120
|
+
].each do |params|
|
121
|
+
expect do
|
122
|
+
Module.new do
|
123
|
+
include DbMod
|
124
|
+
|
125
|
+
def_statement :bad_params, "SELECT * FROM foo WHERE #{params}"
|
126
|
+
end
|
127
|
+
end.to raise_exception ArgumentError
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: db_mod
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Doug Hammond
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-10-
|
11
|
+
date: 2015-10-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: pg
|
@@ -24,20 +24,6 @@ dependencies:
|
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0'
|
27
|
-
- !ruby/object:Gem::Dependency
|
28
|
-
name: docile
|
29
|
-
requirement: !ruby/object:Gem::Requirement
|
30
|
-
requirements:
|
31
|
-
- - ">="
|
32
|
-
- !ruby/object:Gem::Version
|
33
|
-
version: '0'
|
34
|
-
type: :runtime
|
35
|
-
prerelease: false
|
36
|
-
version_requirements: !ruby/object:Gem::Requirement
|
37
|
-
requirements:
|
38
|
-
- - ">="
|
39
|
-
- !ruby/object:Gem::Version
|
40
|
-
version: '0'
|
41
27
|
- !ruby/object:Gem::Dependency
|
42
28
|
name: simplecov
|
43
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -108,7 +94,7 @@ dependencies:
|
|
108
94
|
- - ">="
|
109
95
|
- !ruby/object:Gem::Version
|
110
96
|
version: '0'
|
111
|
-
description:
|
97
|
+
description: Framework for building modular database libraries.
|
112
98
|
email:
|
113
99
|
- d.lakehammond@gmail.com
|
114
100
|
executables: []
|
@@ -134,17 +120,18 @@ files:
|
|
134
120
|
- lib/db_mod/exceptions/connection_not_set.rb
|
135
121
|
- lib/db_mod/exceptions/duplicate_statement_name.rb
|
136
122
|
- lib/db_mod/statements.rb
|
137
|
-
- lib/db_mod/statements/
|
123
|
+
- lib/db_mod/statements/parameters.rb
|
138
124
|
- lib/db_mod/statements/prepared.rb
|
139
125
|
- lib/db_mod/statements/statement.rb
|
140
126
|
- lib/db_mod/transaction.rb
|
141
127
|
- lib/db_mod/version.rb
|
142
128
|
- spec/db_mod/create_spec.rb
|
143
129
|
- spec/db_mod/statements/prepared_spec.rb
|
130
|
+
- spec/db_mod/statements/statement_spec.rb
|
144
131
|
- spec/db_mod/transaction_spec.rb
|
145
132
|
- spec/db_mod_spec.rb
|
146
133
|
- spec/spec_helper.rb
|
147
|
-
homepage:
|
134
|
+
homepage: https://github.com/dslh/db_mod
|
148
135
|
licenses:
|
149
136
|
- MIT
|
150
137
|
metadata: {}
|
@@ -156,7 +143,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
156
143
|
requirements:
|
157
144
|
- - ">="
|
158
145
|
- !ruby/object:Gem::Version
|
159
|
-
version:
|
146
|
+
version: 1.9.3
|
160
147
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
161
148
|
requirements:
|
162
149
|
- - ">="
|
@@ -167,10 +154,11 @@ rubyforge_project:
|
|
167
154
|
rubygems_version: 2.4.8
|
168
155
|
signing_key:
|
169
156
|
specification_version: 4
|
170
|
-
summary:
|
157
|
+
summary: Declarative, modular database library framework.
|
171
158
|
test_files:
|
172
159
|
- spec/db_mod/create_spec.rb
|
173
160
|
- spec/db_mod/statements/prepared_spec.rb
|
161
|
+
- spec/db_mod/statements/statement_spec.rb
|
174
162
|
- spec/db_mod/transaction_spec.rb
|
175
163
|
- spec/db_mod_spec.rb
|
176
164
|
- spec/spec_helper.rb
|