db_mod 0.0.3 → 0.0.4

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.
Files changed (34) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +1 -0
  3. data/CHANGELOG.md +7 -1
  4. data/README.md +53 -5
  5. data/lib/db_mod/exceptions/bad_method_configuration.rb +11 -0
  6. data/lib/db_mod/exceptions/no_results.rb +12 -0
  7. data/lib/db_mod/exceptions/too_many_results.rb +12 -0
  8. data/lib/db_mod/exceptions.rb +4 -1
  9. data/lib/db_mod/statements/configuration/as/csv.rb +39 -0
  10. data/lib/db_mod/statements/configuration/as/json.rb +33 -0
  11. data/lib/db_mod/statements/configuration/as.rb +47 -0
  12. data/lib/db_mod/statements/configuration/configurable_method.rb +78 -0
  13. data/lib/db_mod/statements/configuration/single/column.rb +29 -0
  14. data/lib/db_mod/statements/configuration/single/required_row.rb +35 -0
  15. data/lib/db_mod/statements/configuration/single/required_value.rb +35 -0
  16. data/lib/db_mod/statements/configuration/single/row.rb +33 -0
  17. data/lib/db_mod/statements/configuration/single/value.rb +32 -0
  18. data/lib/db_mod/statements/configuration/single.rb +63 -0
  19. data/lib/db_mod/statements/configuration.rb +48 -0
  20. data/lib/db_mod/statements/parameters.rb +1 -1
  21. data/lib/db_mod/statements/prepared.rb +6 -11
  22. data/lib/db_mod/statements/statement.rb +28 -10
  23. data/lib/db_mod/statements.rb +1 -38
  24. data/lib/db_mod/version.rb +1 -1
  25. data/lib/db_mod.rb +4 -2
  26. data/spec/db_mod/{as → statements/configuration/as}/csv_spec.rb +7 -6
  27. data/spec/db_mod/statements/configuration/as/json_spec.rb +38 -0
  28. data/spec/db_mod/statements/configuration/as_spec.rb +24 -0
  29. data/spec/db_mod/statements/configuration/single_spec.rb +106 -0
  30. metadata +25 -9
  31. data/lib/db_mod/as/csv.rb +0 -37
  32. data/lib/db_mod/as.rb +0 -38
  33. data/lib/db_mod/statements/configurable_method.rb +0 -41
  34. data/spec/db_mod/as_spec.rb +0 -14
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1fe4bb1c7e19c817bad5e788d2cd626ce909ba0d
4
- data.tar.gz: 3bb72d90d2387e7f8e7373f41d390f779b0cc819
3
+ metadata.gz: 9f9ed4ad3824fbe0a2b09444c5aa9b4dde35ee94
4
+ data.tar.gz: 4a70f5c7416895da2fb5cc58608d5aaabeb610ce
5
5
  SHA512:
6
- metadata.gz: b9318f1de5b1fe4c8189c7bc1d03cb42533316175411bb33440824e0051dd33da5c4c64595658e95d001b8995fca4fa4e4c67d24570e229e48418d4cfd346f5e
7
- data.tar.gz: fe369f08a3c480d4c5d3322c35b575ecccb5cbada2b7db33f5b05a3fd692ad5d17cbc9580fabf2356e936b7886d28d1cc7d322e605ad3f6f3f2b04ad5523fa1e
6
+ metadata.gz: 22b0da036c7e04ee7f70d21c79a5dcb70db35895485ee8b1a9bae150a258be8a5c529023c0b71c1f6e74c80ddb911d8f0915d4ef341db88286c48bfaab8bf182
7
+ data.tar.gz: 23a8967663138b1b10d83d994b7b397dd00b8c536663d1a58b1227f6434a47334d7f1d77fcb58a86048d5bfcdbf30e8ab6800eb818d9f37116083cce35b2c0e1
data/.yardopts ADDED
@@ -0,0 +1 @@
1
+ --private --protected lib/**/*.rb - *.md LICENSE
data/CHANGELOG.md CHANGED
@@ -1,7 +1,13 @@
1
+ 0.0.4 (2015-10-15)
2
+ ==================
3
+
4
+ * Adds `def_prepared/statement.single(:value/:row/:column)` - [@dslh](https://github.com/dslh).
5
+ * Adds `.as(:json)` to go with `.as(:csv)` - [@dslh](https://github.com/dslh).
6
+
1
7
  0.0.3 (2015-10-13)
2
8
  ==================
3
9
 
4
- * Configurable method framework. Adds `def_prepared/statement.as(:csv)` - [@dslh](https://github.com/dslh)
10
+ * Configurable method framework. Adds `def_prepared/statement.as(:csv)` - [@dslh](https://github.com/dslh).
5
11
 
6
12
  0.0.2 (2015-10-12)
7
13
  ==================
data/README.md CHANGED
@@ -2,6 +2,13 @@
2
2
 
3
3
  Database enabled modules for ruby.
4
4
 
5
+ [![GitHub version](https://badge.fury.io/gh/dslh%2Fdb_mod.svg)](https://github.com/dslh/db_mod)
6
+ [![Gem Version](https://badge.fury.io/rb/db_mod.svg)](https://rubygems.org/gems/db_mod)
7
+ [![Travis CI](https://img.shields.io/travis/dslh/db_mod/master.svg)](https://travis-ci.org/dslh/db_mod)
8
+ ![Gem downloads](https://img.shields.io/gem/dt/db_mod.svg)
9
+
10
+ [Rubydoc.info documentation](http://www.rubydoc.info/gems/db_mod)
11
+
5
12
  ## Description
6
13
 
7
14
  The `db_mod` gem is a simple framework that helps you organise your
@@ -187,19 +194,60 @@ or twice during a program's execution.
187
194
  #### Configuring defined statements
188
195
 
189
196
  `db_mod` contains a simple framework for extending these statement methods
190
- and prepared methods with additional argument and result processing. For
191
- now only `.as(:csv)` is supported, which will cause the method to format
192
- the SQL result set as a CSV string.
197
+ and prepared methods with additional result processing. A simple
198
+ chained-method syntax is used
199
+
200
+ ##### JSON and CSV formatting
201
+
202
+ Statement and prepared methods can be configured on declaration by using
203
+ `.as(:csv)` and `.as(:json)`, which will convert the result set to a string
204
+ formatted as either a CSV document or an array of JSON objects, respectively.
193
205
 
194
206
  ```ruby
195
- module CsvReports
207
+ module Reports
196
208
  include DbMod
197
209
 
198
210
  def_prepared(:foo, 'SELECT a, b FROM foo WHERE bar_id = $id').as(:csv)
211
+ def_statement(:bar, 'SElECT c, d FROM bar WHERE foo_id = $1').as(:json)
199
212
  end
200
213
 
201
- include CsvReports
214
+ include Reports
202
215
  db_connect db: 'testdb'
203
216
 
204
217
  foo(id: 1) # => "a,b\n1,2\n3,4\n..."
218
+ bar(2) # => '[{"c":"5","d":"6"},...]'
219
+ ```
220
+
221
+ ##### Queries returning one row, column or value
222
+
223
+ To save a lot of repetetive unboxing of query results, methods that return
224
+ only one row, or rows with only one column, or only one row with a single
225
+ value, can be marked as such using the `.single` extension.
226
+
227
+ ```ruby
228
+ module Getters
229
+ include DbMod
230
+
231
+ def_prepared(:user, 'SELECT * FROM user WHERE id = $1').single(:row)
232
+ def_prepared(:name, 'SELECT name FROM user WHERE id = $1').single(:value)
233
+ def_statement(:ids, 'SELECT id FROM user').single(:column)
234
+ end
235
+
236
+ # ...
237
+
238
+ user(1) # => { "id" => "1", "name" => "username" }
239
+ name(1) # => "username"
240
+ ids # => ['1', '2', '3', ...]
241
+ ```
242
+
243
+ When no results are returned, `:column` returns `[]` while `:row` and
244
+ `:value` will return `nil`. To raise an exception instead of returning
245
+ `nil`, use `:row!` and `:value!` instead.
246
+
247
+ ```ruby
248
+ def_statement(:a, 'SELECT 1 WHERE true = false').single(:value)
249
+ def_statement(:b, 'SELECT 1 WHERE true = false').single(:value!)
250
+
251
+ a # => nil
252
+ b # => fail
205
253
  ```
@@ -0,0 +1,11 @@
1
+ require_relative 'base'
2
+
3
+ module DbMod
4
+ module Exceptions
5
+ # Raised when an attempt is made to configure
6
+ # a dynamically defined statement or prepared method
7
+ # in an invalid way.
8
+ class BadMethodConfiguration < Base
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,12 @@
1
+ require_relative 'base'
2
+
3
+ module DbMod
4
+ module Exceptions
5
+ # Raised by a statement or prepared method that
6
+ # has been configured using {ConfigurableMethod#single},
7
+ # when a result set expected to contain at least one
8
+ # result does not.
9
+ class NoResults < Base
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ require_relative 'base'
2
+
3
+ module DbMod
4
+ module Exceptions
5
+ # Raised by a statement or prepared method that
6
+ # has been configured using {ConfigurableMethod#single},
7
+ # when a result set expected to contain not more than
8
+ # one result, in fact, does.
9
+ class TooManyResults < Base
10
+ end
11
+ end
12
+ end
@@ -1,6 +1,9 @@
1
- require_relative 'exceptions/connection_not_set'
2
1
  require_relative 'exceptions/already_in_transaction'
2
+ require_relative 'exceptions/bad_method_configuration'
3
+ require_relative 'exceptions/connection_not_set'
3
4
  require_relative 'exceptions/duplicate_statement_name'
5
+ require_relative 'exceptions/no_results'
6
+ require_relative 'exceptions/too_many_results'
4
7
 
5
8
  module DbMod
6
9
  # Non-standard errors raised by db_mod
@@ -0,0 +1,39 @@
1
+ module DbMod
2
+ module Statements
3
+ module Configuration
4
+ module As
5
+ # Coercer which converts an SQL result set
6
+ # into a string formatted as a CSV document.
7
+ # May be enabled for a prepared method or
8
+ # statement method using +.as(:csv)+:
9
+ #
10
+ # def_statement(:a, 'SELECT a, b FROM foo').as(:csv)
11
+ # def_prepared(:b, 'SELECT b, c FROM bar').as(:csv)
12
+ #
13
+ # def do_stuff
14
+ # a # => "a,b\r\n1,2\r\n3,4\r\n..."
15
+ # end
16
+ module Csv
17
+ # Enables this module to be passed to
18
+ # {DbMod::Statements::Configuration.process_method_results} as the
19
+ # +wrapper+ function, in which case it will retrieve the results
20
+ # and format them as a CSV document using the column names
21
+ # from the result set.
22
+ #
23
+ # @param results [Object] SQL result set
24
+ # @return [String] a CSV formatted document
25
+ def self.call(results)
26
+ headers = nil
27
+ CSV.generate do |csv|
28
+ results.each do |row|
29
+ csv << (headers = row.keys) unless headers
30
+
31
+ csv << headers.map { |col| row[col] }
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,33 @@
1
+ module DbMod
2
+ module Statements
3
+ module Configuration
4
+ module As
5
+ # Coercer which converts an SQL result set
6
+ # into a string formatted as a JSON array.
7
+ # May be enabled for a prepared method or
8
+ # statement method using +.as(:json)+:
9
+ #
10
+ # def_statement(:a, 'SELECT a, b FROM foo').as(:json)
11
+ # def_prepared(:b, 'SELECT b, c FROM bar').as(:json)
12
+ #
13
+ # def do_stuff
14
+ # a # => '[{"a":"x","b":"y"},...]'
15
+ # end
16
+ module Json
17
+ # Enables this module to be passed to
18
+ # {DbMod::Statements::Configuration.process_method_results} as the
19
+ # +wrapper+ function, in which case it will retrieve the results
20
+ # and format them as a JSON string using the column names
21
+ # from the result set for the keys of each object.
22
+ #
23
+ # @param results [Object] SQL result set
24
+ # @return [String] a JSON formatted string
25
+ def self.call(results)
26
+ # .map turns the result object into an array
27
+ results.map { |x| x }.to_json
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,47 @@
1
+ require_relative 'as/csv'
2
+ require_relative 'as/json'
3
+
4
+ module DbMod
5
+ module Statements
6
+ module Configuration
7
+ # Contains coercers and other functions that allow
8
+ # module instance methods returning an SQL result set
9
+ # to be extended with additional result coercion and
10
+ # formatting. The normal way to access this functionality
11
+ # is via {ConfigurableMethod#as},
12
+ # which is available when defining a statement method
13
+ # or prepared method:
14
+ #
15
+ # def_statement(:a, 'SELECT a, b, c FROM foo').as(:csv)
16
+ # def_prepared(:b, 'SELECT d, e, f FROM bar').as(:csv)
17
+ module As
18
+ # For extend_method
19
+ Configuration = DbMod::Statements::Configuration
20
+
21
+ # List of available result coercion methods.
22
+ # Only keys defined here are allowed as arguments
23
+ # to {DbMod::Statements::Configuration::ConfigurableMethod#as}.
24
+ COERCERS = {
25
+ csv: As::Csv,
26
+ json: As::Json
27
+ }
28
+
29
+ # Extend a method so that the SQL result set it
30
+ # returns will be coerced to the given type.
31
+ # See {COERCERS} for a list of defined coercion
32
+ # methods.
33
+ #
34
+ # @param mod [Module] module where the method has been defined
35
+ # @param name [Symbol] method name
36
+ # @param type [Symbol] type to which result set should be coerced
37
+ def self.extend_method(mod, name, type)
38
+ unless COERCERS.key? type
39
+ fail ArgumentError, "#{type} not in #{COERCERS.keys.join ', '}"
40
+ end
41
+
42
+ Configuration.process_method_results(mod, name, COERCERS[type])
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,78 @@
1
+ require_relative 'as'
2
+ require_relative 'single'
3
+
4
+ module DbMod
5
+ module Statements
6
+ module Configuration
7
+ # Encapsulates a method that has just been defined
8
+ # via the dsl exposed in {DbMod::Statements} so that
9
+ # it can be extended with additional processing such
10
+ # as result coercion.
11
+ #
12
+ # The pattern here is something similar to rack's middleware.
13
+ # Calling any of the extension methods below will replace
14
+ # the original method defined by +def_prepared+ or +def_statement+
15
+ # with a wrapper function that may perform processing on given
16
+ # arguments, pass them to the original function, then perform
17
+ # additional processing on the result.
18
+ class ConfigurableMethod
19
+ # Encapsulate a method that has been newly defined
20
+ # by a {DbMod} dsl function, for additional configuration.
21
+ #
22
+ # @param mod [Module] the {DbMod} enabled module
23
+ # where the method was defined
24
+ # @param name [Symbol] the method name
25
+ def initialize(mod, name)
26
+ @mod = mod
27
+ @name = name
28
+ @already_called = {}
29
+ end
30
+
31
+ # Extend the method by converting results into a given
32
+ # format, using one of the coercion methods defined
33
+ # under {DbMod::Statements::Configuration::As}.
34
+ #
35
+ # @param type [:csv,:json] output format for the method
36
+ # @return [self]
37
+ def as(type)
38
+ called! :as
39
+
40
+ Configuration::As.extend_method(@mod, @name, type)
41
+
42
+ self
43
+ end
44
+
45
+ # Extend the method by extracting a singular part of
46
+ # the result set, for queries expected to only return
47
+ # one row, one column, or one row with a single value.
48
+ # See {DbMod::Statements::Configuration::Single} for
49
+ # more details.
50
+ #
51
+ # @param type [Symbol] see {SINGLE_TYPES}
52
+ # @return [self]
53
+ def single(type)
54
+ called! :single
55
+
56
+ Configuration::Single.extend_method(@mod, @name, type)
57
+
58
+ self
59
+ end
60
+
61
+ private
62
+
63
+ # Guard method which asserts that a configuration method
64
+ # may not be called more than once, or else raises
65
+ # {DbMod::Exceptions::BadMethodConfiguration}.
66
+ #
67
+ # @param method [Symbol] method being called
68
+ def called!(method)
69
+ if @already_called[method]
70
+ fail Exceptions::BadMethodConfiguration, "#{method} already called"
71
+ end
72
+
73
+ @already_called[method] = true
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,29 @@
1
+ module DbMod
2
+ module Statements
3
+ module Configuration
4
+ module Single
5
+ # Wrapper for a statement or prepared method that returns
6
+ # an array of the values from the first value of every row
7
+ # returned by the SQL statement.
8
+ #
9
+ # def_statement(:a, 'SELECT a FROM b').single(:column)
10
+ #
11
+ # def do_stuff
12
+ # a # => ['a', 'b', 'c']
13
+ # end
14
+ module Column
15
+ # Enables this module to be passed to
16
+ # {DbMod::Statements::Configuration.process_method_results} as the
17
+ # +wrapper+ function, where it will return an array of the first
18
+ # value from every row in the result set.
19
+ #
20
+ # @param results [Object] SQL result set
21
+ # @return [Array<String>] an array of values from the first column
22
+ def self.call(results)
23
+ results.map { |row| row[row.keys.first] }
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,35 @@
1
+ module DbMod
2
+ module Statements
3
+ module Configuration
4
+ module Single
5
+ # Wrapper for a statement or prepared method that returns
6
+ # only the first row of the result set as a hash, to save
7
+ # manual unboxing. Raises an error unless exactly one row
8
+ # is returned.
9
+ #
10
+ # def_statement(:a, 'SELECT a, b FROM foo').single(:row)
11
+ #
12
+ # def do_stuff
13
+ # a # => { 'a' => '1', 'b' => '2'
14
+ # end
15
+ module RequiredRow
16
+ # Enables this module to be passed to
17
+ # {DbMod::Statements::Configuration.process_method_results} as the
18
+ # +wrapper+ function, where it will return the first row of the
19
+ # result set, or raise an exception if exactly one row is not
20
+ # returned.
21
+ #
22
+ # @param results [Object] SQL result set
23
+ # @return [Hash<String,String>]
24
+ # the first row of the SQL result set returned by the query
25
+ def self.call(results)
26
+ fail DbMod::Exceptions::NoResults unless results.any?
27
+ fail DbMod::Exceptions::TooManyResults if results.count > 1
28
+
29
+ results[0]
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,35 @@
1
+ module DbMod
2
+ module Statements
3
+ module Configuration
4
+ module Single
5
+ # Wrapper for a statement or prepared method that returns
6
+ # the first column of the first returned row. Strictly enforces
7
+ # that exactly one row should be returned by the SQL result, and
8
+ # will fail if zero or more than one row is returned.
9
+ #
10
+ # def_statement(:a, 'SELECT 1').single(:value!)
11
+ #
12
+ # def do_stuff
13
+ # a # => '1'
14
+ # end
15
+ module RequiredValue
16
+ # Enables this module to be passed to
17
+ # {DbMod::Statements::Configuration.process_method_results} as the
18
+ # +wrapper+ function, where it will return the first column of the
19
+ # first row of the result set, or fail if anything other than
20
+ # exactly one row is returned.
21
+ #
22
+ # @param results [Object] SQL result set
23
+ # @return [String] the first column of the first returned row
24
+ def self.call(results)
25
+ fail DbMod::Exceptions::NoResults unless results.any?
26
+ fail DbMod::Exceptions::TooManyResults if results.count > 1
27
+
28
+ row = results[0]
29
+ row[row.keys.first]
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,33 @@
1
+ module DbMod
2
+ module Statements
3
+ module Configuration
4
+ module Single
5
+ # Wrapper for a statement or prepared method that returns
6
+ # only the first row of the result set as a hash, to save
7
+ # manual unboxing. Returns +nil+ if the query returns no
8
+ # results.
9
+ #
10
+ # def_statement(:a, 'SELECT a, b FROM foo').single(:row)
11
+ #
12
+ # def do_stuff
13
+ # a # => { 'a' => '1', 'b' => '2'
14
+ # end
15
+ module Row
16
+ # Enables this module to be passed to
17
+ # {DbMod::Statements::Configuration.process_method_results} as the
18
+ # +wrapper+ function, where it will return the first row of the
19
+ # result set, or +nil+ if the result set is empty.
20
+ #
21
+ # @param results [Object] SQL result set
22
+ # @return [Hash<String,String>,nil]
23
+ # the first row of the SQL result set returned by the query
24
+ def self.call(results)
25
+ return nil unless results.any?
26
+
27
+ results[0]
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,32 @@
1
+ module DbMod
2
+ module Statements
3
+ module Configuration
4
+ module Single
5
+ # Wrapper for a statement or prepared method that
6
+ # returns the first column of the first returned row,
7
+ # or +nil+ if no rows are returned by the query.
8
+ #
9
+ # def_statement(:a, 'SELECT 1').single(:value)
10
+ #
11
+ # def do_stuff
12
+ # a # => '1'
13
+ # end
14
+ module Value
15
+ # Enables this module to be passed to
16
+ # {DbMod::Statements::Configuration.process_method_results} as the
17
+ # +wrapper+ function, where it will return the first column of the
18
+ # first row of the result set, or +nil+ if no results are returned.
19
+ #
20
+ # @param results [Object] SQL result set
21
+ # @return [String,nil] the first column of the first returned row
22
+ def self.call(results)
23
+ return nil unless results.any?
24
+
25
+ row = results[0]
26
+ row[row.keys.first]
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,63 @@
1
+ require_relative 'single/value'
2
+ require_relative 'single/required_value'
3
+ require_relative 'single/row'
4
+ require_relative 'single/required_row'
5
+ require_relative 'single/column'
6
+
7
+ module DbMod
8
+ module Statements
9
+ module Configuration
10
+ # Provides convenience extensions for statement and
11
+ # prepared methods that return only a single result,
12
+ # row, or column. The normal way to access this functionality
13
+ # is via {ConfigurableMethod#single}, which is available
14
+ # when defining a statement method or prepared method:
15
+ #
16
+ # def_statement(:a, 'SELECT name FROM a WHERE id=$1').single(:value)
17
+ # def_prepared(:b, 'SELECT id FROM b WHERE value > $min').single(:column)
18
+ # def_prepared(:c, 'SELECT * FROM c WHERE id = $id').single(:row)
19
+ #
20
+ # def do_stuff
21
+ # a # => "foo"
22
+ # b # => ['1','2','3',...]
23
+ # c # => Hash
24
+ # end
25
+ #
26
+ # +.single(:row)+ and +.single(:value)+ will return the first
27
+ # row or the first value of the first row respectively, or +nil+
28
+ # if no results are found. To generate a
29
+ # {DbMod::Exceptions::NoResults} failure
30
+ # instead of returning +nil+, use +.single(:row!)+ or
31
+ # +.single(:value!)+.
32
+ module Single
33
+ # For process_method_results
34
+ Configuration = DbMod::Statements::Configuration
35
+
36
+ # List of allowed parameters for {#single},
37
+ # and the methods used to process them.
38
+ COERCERS = {
39
+ value: Single::Value,
40
+ value!: Single::RequiredValue,
41
+ row: Single::Row,
42
+ row!: Single::RequiredRow,
43
+ column: Single::Column
44
+ }
45
+
46
+ # Extend a method so that only some singular part of
47
+ # the SQL result set is returned.
48
+ # See above for more details.
49
+ #
50
+ # @param mod [Module] module where the method has been defined
51
+ # @param name [Symbol] method name
52
+ # @param type [Symbol] one of {SINGLE_TYPES}
53
+ def self.extend_method(mod, name, type)
54
+ unless COERCERS.key? type
55
+ fail ArgumentError, "#{type} not in #{COERCERS.keys.join ', '}"
56
+ end
57
+
58
+ Configuration.process_method_results(mod, name, COERCERS[type])
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,48 @@
1
+ module DbMod
2
+ module Statements
3
+ # Provides additional functionality to statement and
4
+ # prepared methods, allowing additional processing of
5
+ # arguments and results using the dsl extensions
6
+ # exposed via {ConfigurableMethod}.
7
+ module Configuration
8
+ # Used by submodules to when defining a method as declared by
9
+ # +def_statement+ or +def_prepared+. Wraps the defined method
10
+ # so that it may be extended with additional argument and
11
+ # result processing.
12
+ #
13
+ # @param mod [Module] the module where the method has been declared
14
+ # @param name [Symbol] the name of the module that has been defined
15
+ # @param definition [Proc] method definition
16
+ # @return [DbMod::Statements::ConfigurableMethod] dsl object for
17
+ # further extending the method
18
+ def self.def_configurable(mod, name, definition)
19
+ mod.instance_eval { define_method(name, definition) }
20
+
21
+ ConfigurableMethod.new(mod, name)
22
+ end
23
+
24
+ # Used by {ConfigurableMethod} (and associated code) to wrap a defined
25
+ # statement method or prepared method with additional result processing.
26
+ # A method should be provided, which accepts an SQL result set and
27
+ # returns some transformation of the results. The original method
28
+ # declaration will be replaced, so that the original method definition
29
+ # is called and the results are passed through this given method.
30
+ #
31
+ # @param mod [Module] the module where the method has been defined
32
+ # @param name [Symbol] the method name
33
+ # @param wrapper [#call]
34
+ # a function that processes the SQL results in some way
35
+ def self.process_method_results(mod, name, wrapper)
36
+ mod.instance_eval do
37
+ wrapped = instance_method(name)
38
+
39
+ define_method(name, lambda do |*args|
40
+ wrapper.call wrapped.bind(self).call(*args)
41
+ end)
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
47
+
48
+ require_relative 'configuration/configurable_method'
@@ -78,7 +78,7 @@ module DbMod
78
78
  # Assert that the given parameter list is an array
79
79
  # containing a single hash of named parameter values.
80
80
  #
81
- # Raises {ArgumentError} otherwise.
81
+ # Raises +ArgumentError+ otherwise.
82
82
  #
83
83
  # @param args [Array<Hash<Symbol>>] method arguments being validated
84
84
  def self.wrapped_hash!(args)
@@ -10,23 +10,18 @@ module DbMod
10
10
  # For statements that are not prepared ahead of execution,
11
11
  # see +def_statement+ in {DbMod::Statements::Statement}.
12
12
  #
13
- # def_prepared
14
- # ------------
15
- #
16
13
  # +def_prepared+ accepts two parameters:
17
- # * `name` [Symbol]: The name that will be given to
14
+ # * *name* [Symbol]: The name that will be given to
18
15
  # the prepared statement. A method will also be defined
19
16
  # on the module with the same name which will call the
20
17
  # statement and return the result.
21
- # * `sql` [String]: The SQL statement to be prepared.
18
+ # * *sql* [String]: The SQL statement to be prepared.
22
19
  # Parameters may be declared using the $ symbol followed
23
- # by a number ($1, $2, $3) or a name ($one, $two, $under_scores).
20
+ # by a number +($1, $2, $3)+ or a name +($one, $two, $a_b)+.
24
21
  # The two styles may not be mixed in the same statement.
25
22
  # The defined function can then be passed parameters
26
23
  # that will be used when the statement is executed.
27
24
  #
28
- # ### example
29
- #
30
25
  # module MyModule
31
26
  # include DbMod
32
27
  #
@@ -146,7 +141,7 @@ module DbMod
146
141
  # and the prepared query to be called.
147
142
  def self.define_no_args_prepared_method(mod, name)
148
143
  method = ->() { conn.exec_prepared(name.to_s) }
149
- Statements.configurable_method mod, name, method
144
+ Configuration.def_configurable mod, name, method
150
145
  end
151
146
 
152
147
  # Define a method with the given name that accepts the
@@ -164,7 +159,7 @@ module DbMod
164
159
  conn.exec_prepared(name.to_s, args)
165
160
  end
166
161
 
167
- Statements.configurable_method mod, name, method
162
+ Configuration.def_configurable mod, name, method
168
163
  end
169
164
 
170
165
  # Define a method with the given name that accepts a fixed
@@ -185,7 +180,7 @@ module DbMod
185
180
  conn.exec_prepared(name.to_s, args)
186
181
  end
187
182
 
188
- Statements.configurable_method(mod, name, method)
183
+ Configuration.def_configurable(mod, name, method)
189
184
  end
190
185
 
191
186
  # Adds +prepared_statements+ to a module. This list of named
@@ -10,20 +10,38 @@ module DbMod
10
10
  # To declare prepared statements, see +def_prepared+
11
11
  # in {DbMod::Statements::Prepared}.
12
12
  #
13
- # def_statement
14
- # -------------
15
- #
16
13
  # +def_statement+ accepts two parameters:
17
- # * `name` [Symbol]: The name that will be given to the
14
+ # * *name* [Symbol]: The name that will be given to the
18
15
  # method that can be used to execute the SQL statement
19
16
  # and return the result.
20
- # * `sql` [String]: The SQL statement that shoul be executed
17
+ # * *sql* [String]: The SQL statement that shoul be executed
21
18
  # when the method is called. Parameters may be declared
22
- # using the $ symbol followed by a number ($1, $2, $3) or
23
- # a name ($one, $two, $under_scores). The two styles may
19
+ # using the $ symbol followed by a number +($1, $2, $3)+ or
20
+ # a name +($one, $two, $under_scores)+. The two styles may
24
21
  # not be mixed in the same statement. The defined function
25
22
  # can then be passed parameters that will be used to fill
26
23
  # in the statement before execution.
24
+ #
25
+ # module MyModule
26
+ # include DbMod
27
+ #
28
+ # def_prepared :my_prepared, <<-SQL
29
+ # SELECT *
30
+ # FROM stuff
31
+ # WHERE a = $1 AND b = $2
32
+ # SQL
33
+ #
34
+ # def_prepared :my_named_prepared, <<-SQL
35
+ # SELECT *
36
+ # FROM stuff
37
+ # WHERE a = $a AND b = $b
38
+ # SQL
39
+ # end
40
+ #
41
+ # include MyModule
42
+ # db_connect db: 'mydb'
43
+ # my_prepared(1,2)
44
+ # my_named_prepared(a: 1, b: 2)
27
45
  module Statement
28
46
  # Defines a module-specific +def_statement+ function
29
47
  # for a module that has just had {DbMod} included.
@@ -83,7 +101,7 @@ module DbMod
83
101
  # @param name [Symbol] name of the method to be defined
84
102
  # @param sql [String] parameterless SQL statement to execute
85
103
  def self.define_no_args_statement_method(mod, name, sql)
86
- Statements.configurable_method mod, name, ->() { query(sql) }
104
+ Configuration.def_configurable mod, name, ->() { query(sql) }
87
105
  end
88
106
 
89
107
  # Define a method with the given name, that accepts the
@@ -99,7 +117,7 @@ module DbMod
99
117
  conn.exec_params(sql, args)
100
118
  end
101
119
 
102
- Statements.configurable_method mod, name, method
120
+ Configuration.def_configurable mod, name, method
103
121
  end
104
122
 
105
123
  # Define a method with the given name that accepts a fixed number
@@ -117,7 +135,7 @@ module DbMod
117
135
  conn.exec_params(sql, args)
118
136
  end
119
137
 
120
- Statements.configurable_method mod, name, method
138
+ Configuration.def_configurable mod, name, method
121
139
  end
122
140
  end
123
141
  end
@@ -1,4 +1,4 @@
1
- require_relative 'statements/configurable_method'
1
+ require_relative 'statements/configuration'
2
2
  require_relative 'statements/statement'
3
3
  require_relative 'statements/prepared'
4
4
 
@@ -16,42 +16,5 @@ module DbMod
16
16
  DbMod::Statements::Prepared.setup(mod)
17
17
  DbMod::Statements::Statement.setup(mod)
18
18
  end
19
-
20
- # Used by submodules to when defining a method as declared by
21
- # +def_statement+ or +def_prepared+. Wraps the defined method
22
- # so that it may be extended with additional argument and
23
- # result processing.
24
- #
25
- # @param mod [Module] the module where the method has been declared
26
- # @param name [Symbol] the name of the module that has been defined
27
- # @param definition [Proc] method definition
28
- # @return [DbMod::Statements::ConfigurableMethod] dsl object for
29
- # further extending the method
30
- def self.configurable_method(mod, name, definition)
31
- mod.instance_eval { define_method(name, definition) }
32
-
33
- ConfigurableMethod.new(mod, name)
34
- end
35
-
36
- # Used by {ConfigurableMethod} (and associated code) to wrap a defined
37
- # statement method or prepared method with additional parameter or result
38
- # processing. A wrapper method definition should be provided, which will
39
- # be called in place of the original method. It will be called with the
40
- # original method proc as a first argument followed by the original
41
- # method arguments (before +DbMod+ has made any attempt to validate them!).
42
- # It is expected to yield to the original proc at some point, although it
43
- # is allowed to do whatever it wants with the results before returning them.
44
- #
45
- # @param mod [Module] the module where the method has been defined
46
- # @param name [Symbol] the method name
47
- # @param wrapper [Proc] a function which will be used to wrap the
48
- # original method definition
49
- def self.extend_method(mod, name, wrapper)
50
- mod.instance_eval do
51
- wrapped = instance_method(name)
52
-
53
- define_method name, ->(*args) { wrapper.call wrapped.bind(self), *args }
54
- end
55
- end
56
19
  end
57
20
  end
@@ -1,5 +1,5 @@
1
1
  # Version information
2
2
  module DbMod
3
3
  # The current version of db_mod.
4
- VERSION = '0.0.3'
4
+ VERSION = '0.0.4'
5
5
  end
data/lib/db_mod.rb CHANGED
@@ -1,8 +1,10 @@
1
1
  require 'pg'
2
- require_relative 'db_mod/exceptions'
3
- require_relative 'db_mod/transaction'
2
+
4
3
  require_relative 'db_mod/create'
4
+ require_relative 'db_mod/exceptions'
5
5
  require_relative 'db_mod/statements'
6
+ require_relative 'db_mod/transaction'
7
+ require_relative 'db_mod/version'
6
8
 
7
9
  # This is the foundation module for enabling db_mod
8
10
  # support in an application. Including this module
@@ -1,14 +1,14 @@
1
1
  require 'spec_helper'
2
2
  require 'csv'
3
3
 
4
- describe DbMod::As::Csv do
4
+ describe DbMod::Statements::Configuration::As::Csv do
5
5
  subject do
6
6
  Module.new do
7
7
  include DbMod
8
8
 
9
9
  def_statement(:statement, 'SELECT a, b FROM foo').as(:csv)
10
- def_prepared(:prepared, 'SELECT c, d FROM bar').as(:csv)
11
- end
10
+ def_prepared(:prepared, 'SELECT a, b FROM bar').as(:csv)
11
+ end.create(db: 'testdb')
12
12
  end
13
13
 
14
14
  before do
@@ -25,11 +25,12 @@ describe DbMod::As::Csv do
25
25
  it 'coerces results to csv' do
26
26
  expect(@conn).to receive(exec_type).and_return([
27
27
  { 'a' => '1', 'b' => '2' },
28
- { 'a' => '3', 'b' => '4' }
28
+ { 'a' => '3', 'b' => '4' },
29
+ { 'a' => nil, 'b' => '5' }
29
30
  ])
30
31
 
31
- csv = subject.create(db: 'testdb').send(method_type)
32
- expect(csv).to eq("a,b\n1,2\n3,4\n")
32
+ csv = subject.send(method_type)
33
+ expect(csv).to eq("a,b\n1,2\n3,4\n,5\n")
33
34
  end
34
35
  end
35
36
  end
@@ -0,0 +1,38 @@
1
+ require 'spec_helper'
2
+ require 'json'
3
+
4
+ describe DbMod::Statements::Configuration::As::Json do
5
+ subject do
6
+ Module.new do
7
+ include DbMod
8
+
9
+ def_statement(:statement, 'SELECT a, b FROM foo').as(:json)
10
+ def_prepared(:prepared, 'SELECT a, b FROM bar').as(:json)
11
+ end.create(db: 'testdb')
12
+ end
13
+
14
+ before do
15
+ @conn = instance_double 'PGconn'
16
+ allow(@conn).to receive(:prepare)
17
+ allow(PGconn).to receive(:connect).and_return @conn
18
+ end
19
+
20
+ {
21
+ statement: :query,
22
+ prepared: :exec_prepared
23
+ }.each do |method_type, exec_type|
24
+ context "#{method_type} methods" do
25
+ it 'coerces results to json' do
26
+ expect(@conn).to receive(exec_type).and_return([
27
+ { 'a' => '1', 'b' => 'foo' },
28
+ { 'a' => '2', 'b' => nil }
29
+ ])
30
+
31
+ json = subject.send(method_type)
32
+ expect(json).to eq(
33
+ '[{"a":"1","b":"foo"},{"a":"2","b":null}]'
34
+ )
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,24 @@
1
+ require 'spec_helper'
2
+
3
+ # See submodules for more
4
+ describe DbMod::Statements::Configuration::As do
5
+ it 'disallows unknown coercions' do
6
+ expect do
7
+ Module.new do
8
+ include DbMod
9
+
10
+ def_statement(:foo, 'SELECT 1').as(:lolwut)
11
+ end
12
+ end.to raise_exception ArgumentError
13
+ end
14
+
15
+ it 'disallows multiple coercions' do
16
+ expect do
17
+ Module.new do
18
+ include DbMod
19
+
20
+ def_statement(:foo, 'SELECT 1').as(:json).as(:csv)
21
+ end
22
+ end.to raise_exception DbMod::Exceptions::BadMethodConfiguration
23
+ end
24
+ end
@@ -0,0 +1,106 @@
1
+ require 'spec_helper'
2
+
3
+ describe DbMod::Statements::Configuration::Single do
4
+ subject do
5
+ Module.new do
6
+ include DbMod
7
+
8
+ def_statement(:v, 'SELECT a FROM foo').single(:value)
9
+ def_prepared(:v!, 'SELECT c FROM d WHERE e = $f').single(:value!)
10
+
11
+ def_prepared(:r, 'SELECT a, b FROM foo WHERE c = $1').single(:row)
12
+ def_statement(:r!, 'SELECT a, b FROM foo').single(:row!)
13
+
14
+ def_statement(:c, 'SELECT a FROM foo WHERE c > $min').single(:column)
15
+ end.create(db: 'testdb')
16
+ end
17
+
18
+ before do
19
+ @conn = instance_double 'PGconn'
20
+ allow(@conn).to receive(:prepare)
21
+ allow(PGconn).to receive(:connect).and_return(@conn)
22
+ end
23
+
24
+ context ':value, :value!' do
25
+ it 'extracts single values' do
26
+ expect(@conn).to receive(:query).and_return([{ 'a' => '1' }])
27
+ expect(subject.v).to eq('1')
28
+
29
+ expect(@conn).to receive(:exec_prepared).and_return([{ 'c' => '2' }])
30
+ expect(subject.v! f: 1).to eq('2')
31
+ end
32
+
33
+ it 'can assert a single result was returned' do
34
+ expect(@conn).to receive(:exec_prepared).and_return([])
35
+ expect { subject.v! f: 2 }.to raise_exception DbMod::Exceptions::NoResults
36
+
37
+ expect(@conn).to receive(:exec_prepared).and_return([
38
+ { 'c' => '3' },
39
+ { 'c' => '4' }
40
+ ])
41
+ expect { subject.v! f: 3 }.to raise_exception(
42
+ DbMod::Exceptions::TooManyResults
43
+ )
44
+ end
45
+ end
46
+
47
+ context ':row, :row!' do
48
+ it 'extracts single rows' do
49
+ result = [{ 'a' => '1', 'b' => '2' }]
50
+ expected = { 'a' => '1', 'b' => '2' }
51
+
52
+ expect(@conn).to receive(:exec_prepared).and_return(result)
53
+ expect(subject.r(1)).to eq(expected)
54
+
55
+ expect(@conn).to receive(:query).and_return(result)
56
+ expect(subject.r!).to eq(expected)
57
+ end
58
+
59
+ it 'can assert that a single result was returned' do
60
+ expect(@conn).to receive(:query).and_return([])
61
+ expect { subject.r! }.to raise_exception DbMod::Exceptions::NoResults
62
+
63
+ expect(@conn).to receive(:query).and_return([
64
+ { 'a' => '3', 'b' => '4' },
65
+ { 'a' => '5', 'b' => '6' }
66
+ ])
67
+ expect { subject.r! }.to raise_exception(
68
+ DbMod::Exceptions::TooManyResults
69
+ )
70
+ end
71
+ end
72
+
73
+ context ':column' do
74
+ it 'returns the column as an array' do
75
+ expect(@conn).to receive(:exec_params).and_return([
76
+ { 'a' => '1' },
77
+ { 'a' => '2' },
78
+ { 'a' => '3' }
79
+ ])
80
+ expect(subject.c min: 1).to eq(%w(1 2 3))
81
+
82
+ expect(@conn).to receive(:exec_params).and_return([])
83
+ expect(subject.c min: 2).to eq([])
84
+ end
85
+ end
86
+
87
+ it 'rejects unknown types' do
88
+ expect do
89
+ Module.new do
90
+ include DbMod
91
+
92
+ def_statement(:a, 'SELECT 1').single(:lolwut)
93
+ end
94
+ end.to raise_exception ArgumentError
95
+ end
96
+
97
+ it 'cannot be called twice' do
98
+ expect do
99
+ Module.new do
100
+ include DbMod
101
+
102
+ def_statement(:a, 'SELECT 1').single(:row).single(:value)
103
+ end
104
+ end.to raise_exception DbMod::Exceptions::BadMethodConfiguration
105
+ end
106
+ 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.3
4
+ version: 0.0.4
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-13 00:00:00.000000000 Z
11
+ date: 2015-10-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: pg
@@ -105,6 +105,7 @@ files:
105
105
  - ".rspec"
106
106
  - ".rubocop.yml"
107
107
  - ".travis.yml"
108
+ - ".yardopts"
108
109
  - CHANGELOG.md
109
110
  - CODE_OF_CONDUCT.md
110
111
  - Gemfile
@@ -114,24 +115,37 @@ files:
114
115
  - Rakefile
115
116
  - db_mod.gemspec
116
117
  - lib/db_mod.rb
117
- - lib/db_mod/as.rb
118
- - lib/db_mod/as/csv.rb
119
118
  - lib/db_mod/create.rb
120
119
  - lib/db_mod/exceptions.rb
121
120
  - lib/db_mod/exceptions/already_in_transaction.rb
121
+ - lib/db_mod/exceptions/bad_method_configuration.rb
122
122
  - lib/db_mod/exceptions/base.rb
123
123
  - lib/db_mod/exceptions/connection_not_set.rb
124
124
  - lib/db_mod/exceptions/duplicate_statement_name.rb
125
+ - lib/db_mod/exceptions/no_results.rb
126
+ - lib/db_mod/exceptions/too_many_results.rb
125
127
  - lib/db_mod/statements.rb
126
- - lib/db_mod/statements/configurable_method.rb
128
+ - lib/db_mod/statements/configuration.rb
129
+ - lib/db_mod/statements/configuration/as.rb
130
+ - lib/db_mod/statements/configuration/as/csv.rb
131
+ - lib/db_mod/statements/configuration/as/json.rb
132
+ - lib/db_mod/statements/configuration/configurable_method.rb
133
+ - lib/db_mod/statements/configuration/single.rb
134
+ - lib/db_mod/statements/configuration/single/column.rb
135
+ - lib/db_mod/statements/configuration/single/required_row.rb
136
+ - lib/db_mod/statements/configuration/single/required_value.rb
137
+ - lib/db_mod/statements/configuration/single/row.rb
138
+ - lib/db_mod/statements/configuration/single/value.rb
127
139
  - lib/db_mod/statements/parameters.rb
128
140
  - lib/db_mod/statements/prepared.rb
129
141
  - lib/db_mod/statements/statement.rb
130
142
  - lib/db_mod/transaction.rb
131
143
  - lib/db_mod/version.rb
132
- - spec/db_mod/as/csv_spec.rb
133
- - spec/db_mod/as_spec.rb
134
144
  - spec/db_mod/create_spec.rb
145
+ - spec/db_mod/statements/configuration/as/csv_spec.rb
146
+ - spec/db_mod/statements/configuration/as/json_spec.rb
147
+ - spec/db_mod/statements/configuration/as_spec.rb
148
+ - spec/db_mod/statements/configuration/single_spec.rb
135
149
  - spec/db_mod/statements/prepared_spec.rb
136
150
  - spec/db_mod/statements/statement_spec.rb
137
151
  - spec/db_mod/transaction_spec.rb
@@ -162,9 +176,11 @@ signing_key:
162
176
  specification_version: 4
163
177
  summary: Declarative, modular database library framework.
164
178
  test_files:
165
- - spec/db_mod/as/csv_spec.rb
166
- - spec/db_mod/as_spec.rb
167
179
  - spec/db_mod/create_spec.rb
180
+ - spec/db_mod/statements/configuration/as/csv_spec.rb
181
+ - spec/db_mod/statements/configuration/as/json_spec.rb
182
+ - spec/db_mod/statements/configuration/as_spec.rb
183
+ - spec/db_mod/statements/configuration/single_spec.rb
168
184
  - spec/db_mod/statements/prepared_spec.rb
169
185
  - spec/db_mod/statements/statement_spec.rb
170
186
  - spec/db_mod/transaction_spec.rb
data/lib/db_mod/as/csv.rb DELETED
@@ -1,37 +0,0 @@
1
- module DbMod
2
- module As
3
- # Coercer which converts an SQL result set
4
- # into a string formatted as a CSV document.
5
- # May be enabled for a prepared method or
6
- # statement method using +.as(:csv)+:
7
- #
8
- # def_statement(:a, 'SELECT a, b FROM foo').as(:csv)
9
- # def_prepared(:b, 'SELECT b, c FROM bar').as(:csv)
10
- #
11
- # def do_stuff
12
- # a # => "a,b\r\n1,2\r\n3,4\r\n..."
13
- # end
14
- module Csv
15
- # Enables this module to be passed to {DbMod::Statements.extend_method}
16
- # as the +wrapper+ function, in which case it will retrieve the results
17
- # and format them as a CSV document using the column names from the
18
- # result set.
19
- #
20
- # @param wrapped_method [Method] the method that has been wrapped
21
- # @param args [*] arguments expected to be passed to the wrapped method
22
- # @return [String] a CSV formatted document
23
- def self.call(wrapped_method, *args)
24
- results = wrapped_method.call(*args)
25
-
26
- headers = nil
27
- CSV.generate do |csv|
28
- results.each do |row|
29
- csv << (headers = row.keys) unless headers
30
-
31
- csv << headers.map { |col| row[col] }
32
- end
33
- end
34
- end
35
- end
36
- end
37
- end
data/lib/db_mod/as.rb DELETED
@@ -1,38 +0,0 @@
1
- require_relative 'as/csv'
2
-
3
- module DbMod
4
- # Contains coercers and other functions that allow
5
- # module instance methods returning an SQL result set
6
- # to be extended with additional result coercion and
7
- # formatting. The normal way to access this functionality
8
- # is via {DbMod::Statements::ConfigurableMethod#as},
9
- # which is available when defining a statement method
10
- # or prepared method:
11
- #
12
- # def_statement(:a, 'SELECT a, b, c FROM foo').as(:csv)
13
- # def_prepared(:b, 'SELECT d, e, f FROM bar').as(:csv)
14
- module As
15
- # List of available result coercion methods.
16
- # Only keys defined here are allowed as arguments
17
- # to {DbMod::Statements::ConfigurableMethod#as}.
18
- COERCERS = {
19
- csv: DbMod::As::Csv
20
- }
21
-
22
- # Extend a method so that the SQL result set it
23
- # returns will be coerced to the given type.
24
- # See {COERCERS} for a list of defined coercion
25
- # methods.
26
- #
27
- # @param mod [Module] module where the method has been defined
28
- # @param name [Symbol] method name
29
- # @param type [Symbol] type to which result set should be coerced
30
- def self.extend_method(mod, name, type)
31
- unless COERCERS.key? type
32
- fail ArgumentError, "#{type} not in #{COERCERS.keys.join ', '}"
33
- end
34
-
35
- Statements.extend_method(mod, name, COERCERS[type])
36
- end
37
- end
38
- end
@@ -1,41 +0,0 @@
1
- require 'db_mod/as'
2
-
3
- module DbMod
4
- module Statements
5
- # Encapsulates a method that has just been defined
6
- # via the dsl exposed in {DbMod::Statements} so that
7
- # it can be extended with additional processing such
8
- # as result coercion.
9
- #
10
- # The pattern here is something similar to rack's middleware.
11
- # Calling any of the extension methods below will replace
12
- # the original method defined by +def_prepared+ or +def_statement+
13
- # with a wrapper function that may perform processing on given
14
- # arguments, pass them to the original function, then perform
15
- # additional processing on the result.
16
- class ConfigurableMethod
17
- # Encapsulate a method that has been newly defined
18
- # by a {DbMod} dsl function, for additional configuration.
19
- #
20
- # @param mod [Module] the {DbMod} enabled module
21
- # where the method was defined
22
- # @param name [Symbol] the method name
23
- def initialize(mod, name)
24
- @mod = mod
25
- @name = name
26
- end
27
-
28
- # Extend the method by converting results into a given
29
- # format, using one of the coercion methods defined
30
- # under {DbMod::As}.
31
- #
32
- # @param type [Symbol] for now, only :csv is accepted
33
- # @return [self]
34
- def as(type)
35
- DbMod::As.extend_method(@mod, @name, type)
36
-
37
- self
38
- end
39
- end
40
- end
41
- end
@@ -1,14 +0,0 @@
1
- require 'spec_helper'
2
-
3
- # See submodules for more
4
- describe DbMod::As do
5
- it 'disallows unknown coercions' do
6
- expect do
7
- Module.new do
8
- include DbMod
9
-
10
- def_statement(:foo, 'SELECT 1').as(:lolwut)
11
- end
12
- end.to raise_exception ArgumentError
13
- end
14
- end