db_mod 0.0.4 → 0.0.5
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/CHANGELOG.md +22 -0
- data/README.md +46 -14
- data/lib/db_mod/exceptions/no_results.rb +2 -1
- data/lib/db_mod/exceptions/too_many_results.rb +2 -1
- data/lib/db_mod/statements/configuration.rb +95 -24
- data/lib/db_mod/statements/configuration/as.rb +9 -16
- data/lib/db_mod/statements/configuration/as/csv.rb +4 -7
- data/lib/db_mod/statements/configuration/as/json.rb +8 -10
- data/lib/db_mod/statements/configuration/defaults.rb +140 -0
- data/lib/db_mod/statements/configuration/method_configuration.rb +109 -0
- data/lib/db_mod/statements/configuration/single.rb +16 -22
- data/lib/db_mod/statements/configuration/single/column.rb +1 -1
- data/lib/db_mod/statements/configuration/single/required_row.rb +1 -1
- data/lib/db_mod/statements/configuration/single/required_value.rb +1 -1
- data/lib/db_mod/statements/configuration/single/row.rb +1 -1
- data/lib/db_mod/statements/configuration/single/value.rb +1 -1
- data/lib/db_mod/statements/parameters.rb +3 -1
- data/lib/db_mod/statements/prepared.rb +18 -46
- data/lib/db_mod/statements/statement.rb +9 -56
- data/lib/db_mod/version.rb +1 -1
- data/spec/db_mod/statements/configuration/as/csv_spec.rb +2 -2
- data/spec/db_mod/statements/configuration/as/json_spec.rb +29 -2
- data/spec/db_mod/statements/configuration/as_spec.rb +2 -2
- data/spec/db_mod/statements/configuration/defaults_spec.rb +203 -0
- data/spec/db_mod/statements/configuration/single_spec.rb +7 -7
- metadata +6 -3
- data/lib/db_mod/statements/configuration/configurable_method.rb +0 -78
@@ -0,0 +1,109 @@
|
|
1
|
+
require_relative 'as'
|
2
|
+
require_relative 'defaults'
|
3
|
+
require_relative 'single'
|
4
|
+
|
5
|
+
module DbMod
|
6
|
+
module Statements
|
7
|
+
module Configuration
|
8
|
+
# Collects settings given at time of definition for statement
|
9
|
+
# and prepared methods. If a block is passed to +def_statement+
|
10
|
+
# or +def_prepared+ it will be evaluated using an instance of
|
11
|
+
# this class, allowing methods such as {#as} or {#single} to
|
12
|
+
# be used to shape the behaviour of the defined method.
|
13
|
+
class MethodConfiguration
|
14
|
+
# Creates a new configuration object to be used as the scope for
|
15
|
+
# blocks passed to +def_statement+ and +def_prepared+ declarations.
|
16
|
+
#
|
17
|
+
# @yield executes the block using +self+ as scope
|
18
|
+
def initialize(&block)
|
19
|
+
@settings = {}
|
20
|
+
instance_exec(&block) if block_given?
|
21
|
+
end
|
22
|
+
|
23
|
+
# Extend the method by converting results into a given
|
24
|
+
# format, using one of the coercion methods defined
|
25
|
+
# under {DbMod::Statements::Configuration::As}.
|
26
|
+
#
|
27
|
+
# @param type [:csv,:json] output format for the method
|
28
|
+
# @return [self]
|
29
|
+
def as(type)
|
30
|
+
one_of! type, Configuration::As::COERCERS
|
31
|
+
set_once! :as, type
|
32
|
+
|
33
|
+
self
|
34
|
+
end
|
35
|
+
|
36
|
+
# Extend the method by extracting a singular part of
|
37
|
+
# the result set, for queries expected to only return
|
38
|
+
# one row, one column, or one row with a single value.
|
39
|
+
# See {DbMod::Statements::Configuration::Single} for
|
40
|
+
# more details.
|
41
|
+
#
|
42
|
+
# @param type [Symbol] see {Configuration::Single::COERCERS}
|
43
|
+
# @return [self]
|
44
|
+
def single(type)
|
45
|
+
one_of! type, Configuration::Single::COERCERS
|
46
|
+
set_once! :single, type
|
47
|
+
|
48
|
+
self
|
49
|
+
end
|
50
|
+
|
51
|
+
# Declares default values for method parameters.
|
52
|
+
# For methods with named parameters, a hash of argument
|
53
|
+
# names and default values should be provided.
|
54
|
+
# For methods with indexed parameters, an array of 1..n
|
55
|
+
# default values should be provided, where n is the
|
56
|
+
# method's arity. In this case default values will be
|
57
|
+
# applied to the right-hand side of the argument list,
|
58
|
+
# as with normal parameter default rules.
|
59
|
+
#
|
60
|
+
# @param defaults [Hash<Symbol,value>,Array<value>]
|
61
|
+
# default parameter values
|
62
|
+
def defaults(*defaults)
|
63
|
+
if defaults.size == 1 && defaults.first.is_a?(Hash)
|
64
|
+
defaults = defaults.first
|
65
|
+
elsif defaults.last.is_a? Hash
|
66
|
+
fail ArgumentError, 'mixed default declaration not allowed'
|
67
|
+
end
|
68
|
+
|
69
|
+
set_once! :defaults, defaults
|
70
|
+
|
71
|
+
self
|
72
|
+
end
|
73
|
+
|
74
|
+
# Return all given settings in a hash.
|
75
|
+
# @return [Hash]
|
76
|
+
def to_hash
|
77
|
+
@settings
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
|
82
|
+
# Guard method which asserts that a configuration method
|
83
|
+
# may not be called more than once, or else raises
|
84
|
+
# {DbMod::Exceptions::BadMethodConfiguration}.
|
85
|
+
#
|
86
|
+
# @param setting [Symbol] setting name
|
87
|
+
# @param value [Object] setting value
|
88
|
+
def set_once!(setting, value)
|
89
|
+
if @settings.key? setting
|
90
|
+
fail Exceptions::BadMethodConfiguration, "#{setting} already called"
|
91
|
+
end
|
92
|
+
|
93
|
+
@settings[setting] = value
|
94
|
+
end
|
95
|
+
|
96
|
+
# Guard method which asserts that a configuration setting
|
97
|
+
# is one of the allowed values in the given hash.
|
98
|
+
#
|
99
|
+
# @param value [key] configuration setting
|
100
|
+
# @param allowed [Hash] set of allowed configuration settings
|
101
|
+
def one_of!(value, allowed)
|
102
|
+
return if allowed.key? value
|
103
|
+
|
104
|
+
fail ArgumentError, "#{value} not in #{allowed.keys.join ', '}"
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
@@ -10,17 +10,17 @@ module DbMod
|
|
10
10
|
# Provides convenience extensions for statement and
|
11
11
|
# prepared methods that return only a single result,
|
12
12
|
# row, or column. The normal way to access this functionality
|
13
|
-
# is via {
|
13
|
+
# is via {MethodConfiguration#single}, which is available
|
14
14
|
# when defining a statement method or prepared method:
|
15
15
|
#
|
16
|
-
# def_statement(:a, 'SELECT name FROM a WHERE id=$1')
|
17
|
-
# def_prepared(:b, 'SELECT id FROM b WHERE
|
18
|
-
# def_prepared(:c, 'SELECT * FROM c WHERE id = $id')
|
16
|
+
# def_statement(:a, 'SELECT name FROM a WHERE id=$1') { single(:value) }
|
17
|
+
# def_prepared(:b, 'SELECT id FROM b WHERE x > $y') { single(:column) }
|
18
|
+
# def_prepared(:c, 'SELECT * FROM c WHERE id = $id') { single(:row) }
|
19
19
|
#
|
20
20
|
# def do_stuff
|
21
|
-
# a
|
22
|
-
# b # => ['1','2','3',...]
|
23
|
-
# c # => Hash
|
21
|
+
# a(1) # => "foo"
|
22
|
+
# b(y: 2) # => ['1','2','3',...]
|
23
|
+
# c id: 3 # => Hash
|
24
24
|
# end
|
25
25
|
#
|
26
26
|
# +.single(:row)+ and +.single(:value)+ will return the first
|
@@ -30,10 +30,7 @@ module DbMod
|
|
30
30
|
# instead of returning +nil+, use +.single(:row!)+ or
|
31
31
|
# +.single(:value!)+.
|
32
32
|
module Single
|
33
|
-
#
|
34
|
-
Configuration = DbMod::Statements::Configuration
|
35
|
-
|
36
|
-
# List of allowed parameters for {#single},
|
33
|
+
# List of allowed parameters for {MethodConfiguration#single},
|
37
34
|
# and the methods used to process them.
|
38
35
|
COERCERS = {
|
39
36
|
value: Single::Value,
|
@@ -43,19 +40,16 @@ module DbMod
|
|
43
40
|
column: Single::Column
|
44
41
|
}
|
45
42
|
|
46
|
-
# Extend
|
47
|
-
#
|
48
|
-
# See above for more details.
|
43
|
+
# Extend the given method definition with additional
|
44
|
+
# result coercion.
|
49
45
|
#
|
50
|
-
# @param
|
51
|
-
# @param
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
fail ArgumentError, "#{type} not in #{COERCERS.keys.join ', '}"
|
56
|
-
end
|
46
|
+
# @param definition [Proc] base method definition
|
47
|
+
# @param config [MethodConfiguration] method configuration
|
48
|
+
def self.extend(definition, config)
|
49
|
+
type = config[:single]
|
50
|
+
return definition if type.nil?
|
57
51
|
|
58
|
-
Configuration.
|
52
|
+
Configuration.attach_result_processor definition, COERCERS[type]
|
59
53
|
end
|
60
54
|
end
|
61
55
|
end
|
@@ -13,7 +13,7 @@ module DbMod
|
|
13
13
|
# end
|
14
14
|
module Column
|
15
15
|
# Enables this module to be passed to
|
16
|
-
# {DbMod::Statements::Configuration.
|
16
|
+
# {DbMod::Statements::Configuration.attach_result_processor} as the
|
17
17
|
# +wrapper+ function, where it will return an array of the first
|
18
18
|
# value from every row in the result set.
|
19
19
|
#
|
@@ -14,7 +14,7 @@ module DbMod
|
|
14
14
|
# end
|
15
15
|
module RequiredRow
|
16
16
|
# Enables this module to be passed to
|
17
|
-
# {DbMod::Statements::Configuration.
|
17
|
+
# {DbMod::Statements::Configuration.attach_result_processor} as the
|
18
18
|
# +wrapper+ function, where it will return the first row of the
|
19
19
|
# result set, or raise an exception if exactly one row is not
|
20
20
|
# returned.
|
@@ -14,7 +14,7 @@ module DbMod
|
|
14
14
|
# end
|
15
15
|
module RequiredValue
|
16
16
|
# Enables this module to be passed to
|
17
|
-
# {DbMod::Statements::Configuration.
|
17
|
+
# {DbMod::Statements::Configuration.attach_result_processor} as the
|
18
18
|
# +wrapper+ function, where it will return the first column of the
|
19
19
|
# first row of the result set, or fail if anything other than
|
20
20
|
# exactly one row is returned.
|
@@ -14,7 +14,7 @@ module DbMod
|
|
14
14
|
# end
|
15
15
|
module Row
|
16
16
|
# Enables this module to be passed to
|
17
|
-
# {DbMod::Statements::Configuration.
|
17
|
+
# {DbMod::Statements::Configuration.attach_result_processor} as the
|
18
18
|
# +wrapper+ function, where it will return the first row of the
|
19
19
|
# result set, or +nil+ if the result set is empty.
|
20
20
|
#
|
@@ -13,7 +13,7 @@ module DbMod
|
|
13
13
|
# end
|
14
14
|
module Value
|
15
15
|
# Enables this module to be passed to
|
16
|
-
# {DbMod::Statements::Configuration.
|
16
|
+
# {DbMod::Statements::Configuration.attach_result_processor} as the
|
17
17
|
# +wrapper+ function, where it will return the first column of the
|
18
18
|
# first row of the result set, or +nil+ if no results are returned.
|
19
19
|
#
|
@@ -101,7 +101,9 @@ module DbMod
|
|
101
101
|
# @return [Array] values to be passed to the prepared statement
|
102
102
|
def self.parameter_array(expected, args)
|
103
103
|
expected.map do |arg|
|
104
|
-
|
104
|
+
fail(ArgumentError, "missing arg #{arg}") unless args.key? arg
|
105
|
+
|
106
|
+
args[arg]
|
105
107
|
end
|
106
108
|
end
|
107
109
|
|
@@ -65,13 +65,13 @@ module DbMod
|
|
65
65
|
# @param mod [Module] a module with {DbMod} included
|
66
66
|
def self.define_def_prepared(mod)
|
67
67
|
mod.class.instance_eval do
|
68
|
-
define_method(:def_prepared) do |name, sql|
|
68
|
+
define_method(:def_prepared) do |name, sql, &block|
|
69
69
|
sql = sql.dup
|
70
70
|
name = name.to_sym
|
71
71
|
|
72
72
|
params = Parameters.parse_params! sql
|
73
73
|
prepared_statements[name] = sql
|
74
|
-
Prepared.define_prepared_method(mod, name, params)
|
74
|
+
Prepared.define_prepared_method(mod, name, params, &block)
|
75
75
|
end
|
76
76
|
end
|
77
77
|
end
|
@@ -119,15 +119,13 @@ module DbMod
|
|
119
119
|
# @param params [Fixnum,Array<Symbol>]
|
120
120
|
# expected parameter count, or a list of argument names.
|
121
121
|
# An empty array produces a no-argument method.
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
define_named_args_prepared_method(mod, name, params)
|
128
|
-
end
|
122
|
+
# @yield dsl block may be passed, which will be evaluated using a
|
123
|
+
# {Configuration::MethodConfiguration} object as scope
|
124
|
+
def self.define_prepared_method(mod, name, params, &block)
|
125
|
+
if params == []
|
126
|
+
define_no_args_prepared_method(mod, name, &block)
|
129
127
|
else
|
130
|
-
|
128
|
+
define_prepared_method_with_args(mod, name, params, &block)
|
131
129
|
end
|
132
130
|
end
|
133
131
|
|
@@ -139,48 +137,22 @@ module DbMod
|
|
139
137
|
# where the method will be defined
|
140
138
|
# @param name [Symbol] name of the method to be defined
|
141
139
|
# and the prepared query to be called.
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
# Define a method with the given name that accepts the
|
148
|
-
# given set of named parameters, that will call the prepared
|
149
|
-
# statement with the same name.
|
150
|
-
#
|
151
|
-
# @param mod [Module] {DbMod} enabled module
|
152
|
-
# where the method will be defined
|
153
|
-
# @param name [Symbol] name of the method to be defined
|
154
|
-
# and the prepared query to be called.
|
155
|
-
# @param params [Array<Symbol>] list of parameter names
|
156
|
-
def self.define_named_args_prepared_method(mod, name, params)
|
157
|
-
method = lambda do |*args|
|
158
|
-
args = Parameters.valid_named_args! params, args
|
159
|
-
conn.exec_prepared(name.to_s, args)
|
160
|
-
end
|
161
|
-
|
162
|
-
Configuration.def_configurable mod, name, method
|
140
|
+
# @yield dsl method configuration object may be passed
|
141
|
+
def self.define_no_args_prepared_method(mod, name, &block)
|
142
|
+
method = ->(*) { conn.exec_prepared(name.to_s) }
|
143
|
+
Configuration.def_configurable mod, name, method, &block
|
163
144
|
end
|
164
145
|
|
165
|
-
# Define a method with the given name that accepts a fixed
|
166
|
-
# number of arguments, that will call the prepared statement
|
167
|
-
# with the same name.
|
168
|
-
#
|
169
146
|
# @param mod [Module] {DbMod} enabled module
|
170
147
|
# where the method will be defined
|
171
148
|
# @param name [Symbol] name of the method to be defined
|
172
149
|
# and the prepared query to be called.
|
173
|
-
# @param
|
174
|
-
#
|
175
|
-
#
|
176
|
-
def self.
|
177
|
-
method =
|
178
|
-
|
179
|
-
|
180
|
-
conn.exec_prepared(name.to_s, args)
|
181
|
-
end
|
182
|
-
|
183
|
-
Configuration.def_configurable(mod, name, method)
|
150
|
+
# @param params [Fixnum,Array<Symbol>]
|
151
|
+
# expected parameter count, or a list of argument names.
|
152
|
+
# An empty array produces a no-argument method.
|
153
|
+
def self.define_prepared_method_with_args(mod, name, params, &block)
|
154
|
+
method = ->(*args) { conn.exec_prepared(name.to_s, args) }
|
155
|
+
Configuration.def_configurable(mod, name, method, params, &block)
|
184
156
|
end
|
185
157
|
|
186
158
|
# Adds +prepared_statements+ to a module. This list of named
|
@@ -61,12 +61,12 @@ module DbMod
|
|
61
61
|
# @param mod [Module] a module with {DbMod} included
|
62
62
|
def self.define_def_statement(mod)
|
63
63
|
mod.class.instance_eval do
|
64
|
-
define_method(:def_statement) do |name, sql|
|
64
|
+
define_method(:def_statement) do |name, sql, &block|
|
65
65
|
sql = sql.dup
|
66
66
|
name = name.to_sym
|
67
67
|
|
68
68
|
params = Parameters.parse_params! sql
|
69
|
-
Statement.define_statement_method(mod, name, params, sql)
|
69
|
+
Statement.define_statement_method(mod, name, params, sql, &block)
|
70
70
|
end
|
71
71
|
end
|
72
72
|
end
|
@@ -80,62 +80,15 @@ module DbMod
|
|
80
80
|
# @param params [Fixnum,Array<Symbol>]
|
81
81
|
# expected parameter count, or a list of argument names.
|
82
82
|
# An empty array produces a no-argument method.
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
define_named_args_statement_method(mod, name, params, sql)
|
89
|
-
end
|
83
|
+
# @yield dsl block may be passed, which will be evaluated using a
|
84
|
+
# {Configuration::MethodConfiguration} object as scope
|
85
|
+
def self.define_statement_method(mod, name, params, sql, &block)
|
86
|
+
if params == []
|
87
|
+
Configuration.def_configurable(mod, name, ->(*) { query sql }, &block)
|
90
88
|
else
|
91
|
-
|
92
|
-
|
93
|
-
end
|
94
|
-
|
95
|
-
# Define a no-argument method with the given name
|
96
|
-
# that will execute the given sql statement and return
|
97
|
-
# the result.
|
98
|
-
#
|
99
|
-
# @param mod [Module] {DbMod} enabled module
|
100
|
-
# where the method will be defined
|
101
|
-
# @param name [Symbol] name of the method to be defined
|
102
|
-
# @param sql [String] parameterless SQL statement to execute
|
103
|
-
def self.define_no_args_statement_method(mod, name, sql)
|
104
|
-
Configuration.def_configurable mod, name, ->() { query(sql) }
|
105
|
-
end
|
106
|
-
|
107
|
-
# Define a method with the given name, that accepts the
|
108
|
-
# given set of named parameters that will be used to execute
|
109
|
-
# the given SQL query.
|
110
|
-
#
|
111
|
-
# @param mod [Module] {DbMod} enabled module
|
112
|
-
# @param name [Symbol] name of the method to be defined
|
113
|
-
# @param params [Array<Symbol>] parameter names and order
|
114
|
-
def self.define_named_args_statement_method(mod, name, params, sql)
|
115
|
-
method = lambda do |*args|
|
116
|
-
args = Parameters.valid_named_args! params, args
|
117
|
-
conn.exec_params(sql, args)
|
89
|
+
method = ->(*args) { conn.exec_params(sql, args) }
|
90
|
+
Configuration.def_configurable(mod, name, method, params, &block)
|
118
91
|
end
|
119
|
-
|
120
|
-
Configuration.def_configurable mod, name, method
|
121
|
-
end
|
122
|
-
|
123
|
-
# Define a method with the given name that accepts a fixed number
|
124
|
-
# of arguments, that will be used to execute the given SQL query.
|
125
|
-
#
|
126
|
-
# @param mod [Module] {DbMod} enabled module where the method
|
127
|
-
# will be defined
|
128
|
-
# @param name [Symbol] name of the method to be defined
|
129
|
-
# @param count [Fixnum] arity of the defined method,
|
130
|
-
# the number of parameters that the SQL statement requires
|
131
|
-
def self.define_fixed_args_statement_method(mod, name, count, sql)
|
132
|
-
method = lambda do |*args|
|
133
|
-
Parameters.valid_fixed_args!(count, args)
|
134
|
-
|
135
|
-
conn.exec_params(sql, args)
|
136
|
-
end
|
137
|
-
|
138
|
-
Configuration.def_configurable mod, name, method
|
139
92
|
end
|
140
93
|
end
|
141
94
|
end
|
data/lib/db_mod/version.rb
CHANGED
@@ -6,8 +6,8 @@ describe DbMod::Statements::Configuration::As::Csv do
|
|
6
6
|
Module.new do
|
7
7
|
include DbMod
|
8
8
|
|
9
|
-
def_statement(:statement, 'SELECT a, b FROM foo')
|
10
|
-
def_prepared(:prepared, 'SELECT a, b FROM bar')
|
9
|
+
def_statement(:statement, 'SELECT a, b FROM foo') { as(:csv) }
|
10
|
+
def_prepared(:prepared, 'SELECT a, b FROM bar') { as(:csv) }
|
11
11
|
end.create(db: 'testdb')
|
12
12
|
end
|
13
13
|
|
@@ -6,8 +6,20 @@ describe DbMod::Statements::Configuration::As::Json do
|
|
6
6
|
Module.new do
|
7
7
|
include DbMod
|
8
8
|
|
9
|
-
def_statement(:statement, 'SELECT a, b FROM foo')
|
10
|
-
def_prepared(:prepared, 'SELECT a, b FROM bar')
|
9
|
+
def_statement(:statement, 'SELECT a, b FROM foo') { as(:json) }
|
10
|
+
def_prepared(:prepared, 'SELECT a, b FROM bar') { as(:json) }
|
11
|
+
|
12
|
+
def_statement(
|
13
|
+
:single,
|
14
|
+
'SELECT * FROM foo WHERE a = $1'
|
15
|
+
) { single(:row).as(:json) }
|
16
|
+
|
17
|
+
def_statement(
|
18
|
+
:single!,
|
19
|
+
'SELECT * FROM foo WHERE a = $1'
|
20
|
+
) { single(:row!).as(:json) }
|
21
|
+
|
22
|
+
def_statement(:col, 'SELECT a FROM foo') { single(:column).as(:json) }
|
11
23
|
end.create(db: 'testdb')
|
12
24
|
end
|
13
25
|
|
@@ -35,4 +47,19 @@ describe DbMod::Statements::Configuration::As::Json do
|
|
35
47
|
end
|
36
48
|
end
|
37
49
|
end
|
50
|
+
|
51
|
+
it 'can be chained with single(:row)' do
|
52
|
+
result = [{ 'a' => '1', 'b' => '2' }]
|
53
|
+
expected = '{"a":"1","b":"2"}'
|
54
|
+
|
55
|
+
expect(@conn).to receive(:exec_params).exactly(2).times.and_return(result)
|
56
|
+
expect(subject.single(1)).to eq(expected)
|
57
|
+
expect(subject.single!(2)).to eq(expected)
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'can be chained with single(:column)' do
|
61
|
+
result = [{ 'a' => '1' }, { 'a' => '2' }, { 'a' => '3' }]
|
62
|
+
expect(@conn).to receive(:query).and_return(result)
|
63
|
+
expect(subject.col).to eq('["1","2","3"]')
|
64
|
+
end
|
38
65
|
end
|