db_mod 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5547dcc1f21c6ca964d25c097abb813e77713161
4
- data.tar.gz: 2214d888e4d39fb545e65e2c8539fdeadc48b145
3
+ metadata.gz: 1fe4bb1c7e19c817bad5e788d2cd626ce909ba0d
4
+ data.tar.gz: 3bb72d90d2387e7f8e7373f41d390f779b0cc819
5
5
  SHA512:
6
- metadata.gz: 6e5bd968a9b47e6d55972444b55bb7bf0434938711c71d0c6955b6d6d048c25a7bfb0e78bffec87bc70dbe3c58aec5766513530e63104c24a2a1089ba99a91c0
7
- data.tar.gz: 28d48191ba234d83a0dc52dc5c9224923712834d8b8411e4e67d081bc36ab45f062f5476e7e0d17f2eb0222cb89065bdb5f657d84230c765bc2f1208ce26a0dc
6
+ metadata.gz: b9318f1de5b1fe4c8189c7bc1d03cb42533316175411bb33440824e0051dd33da5c4c64595658e95d001b8995fca4fa4e4c67d24570e229e48418d4cfd346f5e
7
+ data.tar.gz: fe369f08a3c480d4c5d3322c35b575ecccb5cbada2b7db33f5b05a3fd692ad5d17cbc9580fabf2356e936b7886d28d1cc7d322e605ad3f6f3f2b04ad5523fa1e
@@ -0,0 +1,14 @@
1
+ 0.0.3 (2015-10-13)
2
+ ==================
3
+
4
+ * Configurable method framework. Adds `def_prepared/statement.as(:csv)` - [@dslh](https://github.com/dslh)
5
+
6
+ 0.0.2 (2015-10-12)
7
+ ==================
8
+
9
+ * Adds `def_statement` to compliment `def_prepared` - [@dslh](https://github.com/dslh).
10
+
11
+ 0.0.1 (2015-10-11)
12
+ ==================
13
+
14
+ * Initial release - [@dslh](https://github.com/dslh).
data/README.md CHANGED
@@ -183,3 +183,23 @@ Db::ComplicatedStuff.prepare_all_statements my_conn
183
183
  the SQL queries are saved in memory rather than being sent to the database
184
184
  at connection time. This is useful for queries that will only be run once
185
185
  or twice during a program's execution.
186
+
187
+ #### Configuring defined statements
188
+
189
+ `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.
193
+
194
+ ```ruby
195
+ module CsvReports
196
+ include DbMod
197
+
198
+ def_prepared(:foo, 'SELECT a, b FROM foo WHERE bar_id = $id').as(:csv)
199
+ end
200
+
201
+ include CsvReports
202
+ db_connect db: 'testdb'
203
+
204
+ foo(id: 1) # => "a,b\n1,2\n3,4\n..."
205
+ ```
@@ -0,0 +1,38 @@
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
@@ -0,0 +1,37 @@
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
@@ -1,3 +1,4 @@
1
+ require_relative 'statements/configurable_method'
1
2
  require_relative 'statements/statement'
2
3
  require_relative 'statements/prepared'
3
4
 
@@ -15,5 +16,42 @@ module DbMod
15
16
  DbMod::Statements::Prepared.setup(mod)
16
17
  DbMod::Statements::Statement.setup(mod)
17
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
18
56
  end
19
57
  end
@@ -0,0 +1,41 @@
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
@@ -145,9 +145,8 @@ module DbMod
145
145
  # @param name [Symbol] name of the method to be defined
146
146
  # and the prepared query to be called.
147
147
  def self.define_no_args_prepared_method(mod, name)
148
- mod.instance_eval do
149
- define_method name, ->() { conn.exec_prepared(name.to_s) }
150
- end
148
+ method = ->() { conn.exec_prepared(name.to_s) }
149
+ Statements.configurable_method mod, name, method
151
150
  end
152
151
 
153
152
  # Define a method with the given name that accepts the
@@ -165,7 +164,7 @@ module DbMod
165
164
  conn.exec_prepared(name.to_s, args)
166
165
  end
167
166
 
168
- mod.instance_eval { define_method(name, method) }
167
+ Statements.configurable_method mod, name, method
169
168
  end
170
169
 
171
170
  # Define a method with the given name that accepts a fixed
@@ -186,7 +185,7 @@ module DbMod
186
185
  conn.exec_prepared(name.to_s, args)
187
186
  end
188
187
 
189
- mod.instance_eval { define_method(name, method) }
188
+ Statements.configurable_method(mod, name, method)
190
189
  end
191
190
 
192
191
  # Adds +prepared_statements+ to a module. This list of named
@@ -83,9 +83,7 @@ module DbMod
83
83
  # @param name [Symbol] name of the method to be defined
84
84
  # @param sql [String] parameterless SQL statement to execute
85
85
  def self.define_no_args_statement_method(mod, name, sql)
86
- mod.instance_eval do
87
- define_method name, ->() { query(sql) }
88
- end
86
+ Statements.configurable_method mod, name, ->() { query(sql) }
89
87
  end
90
88
 
91
89
  # Define a method with the given name, that accepts the
@@ -101,7 +99,7 @@ module DbMod
101
99
  conn.exec_params(sql, args)
102
100
  end
103
101
 
104
- mod.instance_eval { define_method(name, method) }
102
+ Statements.configurable_method mod, name, method
105
103
  end
106
104
 
107
105
  # Define a method with the given name that accepts a fixed number
@@ -119,7 +117,7 @@ module DbMod
119
117
  conn.exec_params(sql, args)
120
118
  end
121
119
 
122
- mod.instance_eval { define_method(name, method) }
120
+ Statements.configurable_method mod, name, method
123
121
  end
124
122
  end
125
123
  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.2'
4
+ VERSION = '0.0.3'
5
5
  end
@@ -0,0 +1,36 @@
1
+ require 'spec_helper'
2
+ require 'csv'
3
+
4
+ describe DbMod::As::Csv do
5
+ subject do
6
+ Module.new do
7
+ include DbMod
8
+
9
+ def_statement(:statement, 'SELECT a, b FROM foo').as(:csv)
10
+ def_prepared(:prepared, 'SELECT c, d FROM bar').as(:csv)
11
+ end
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 csv' do
26
+ expect(@conn).to receive(exec_type).and_return([
27
+ { 'a' => '1', 'b' => '2' },
28
+ { 'a' => '3', 'b' => '4' }
29
+ ])
30
+
31
+ csv = subject.create(db: 'testdb').send(method_type)
32
+ expect(csv).to eq("a,b\n1,2\n3,4\n")
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,14 @@
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
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.2
4
+ version: 0.0.3
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-12 00:00:00.000000000 Z
11
+ date: 2015-10-13 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
+ - CHANGELOG.md
108
109
  - CODE_OF_CONDUCT.md
109
110
  - Gemfile
110
111
  - Guardfile
@@ -113,6 +114,8 @@ files:
113
114
  - Rakefile
114
115
  - db_mod.gemspec
115
116
  - lib/db_mod.rb
117
+ - lib/db_mod/as.rb
118
+ - lib/db_mod/as/csv.rb
116
119
  - lib/db_mod/create.rb
117
120
  - lib/db_mod/exceptions.rb
118
121
  - lib/db_mod/exceptions/already_in_transaction.rb
@@ -120,11 +123,14 @@ files:
120
123
  - lib/db_mod/exceptions/connection_not_set.rb
121
124
  - lib/db_mod/exceptions/duplicate_statement_name.rb
122
125
  - lib/db_mod/statements.rb
126
+ - lib/db_mod/statements/configurable_method.rb
123
127
  - lib/db_mod/statements/parameters.rb
124
128
  - lib/db_mod/statements/prepared.rb
125
129
  - lib/db_mod/statements/statement.rb
126
130
  - lib/db_mod/transaction.rb
127
131
  - lib/db_mod/version.rb
132
+ - spec/db_mod/as/csv_spec.rb
133
+ - spec/db_mod/as_spec.rb
128
134
  - spec/db_mod/create_spec.rb
129
135
  - spec/db_mod/statements/prepared_spec.rb
130
136
  - spec/db_mod/statements/statement_spec.rb
@@ -156,6 +162,8 @@ signing_key:
156
162
  specification_version: 4
157
163
  summary: Declarative, modular database library framework.
158
164
  test_files:
165
+ - spec/db_mod/as/csv_spec.rb
166
+ - spec/db_mod/as_spec.rb
159
167
  - spec/db_mod/create_spec.rb
160
168
  - spec/db_mod/statements/prepared_spec.rb
161
169
  - spec/db_mod/statements/statement_spec.rb