db_mod 0.0.5 → 0.0.6

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.
@@ -46,7 +46,8 @@ module DbMod
46
46
  # Defines a module-specific +def_prepared+ function
47
47
  # for a module that has just had {DbMod} included.
48
48
  #
49
- # @param mod [Module]
49
+ # @param mod [Module] module including {DbMod}
50
+ # @see DbMod.included
50
51
  def self.setup(mod)
51
52
  Prepared.define_def_prepared(mod)
52
53
  Prepared.define_prepared_statements(mod)
@@ -63,15 +64,17 @@ module DbMod
63
64
  # method with the same name.
64
65
  #
65
66
  # @param mod [Module] a module with {DbMod} included
67
+ # @raise ArgumentError if there is a problem parsing
68
+ # method parameters from the SQL statement
66
69
  def self.define_def_prepared(mod)
67
- mod.class.instance_eval do
70
+ class << mod
68
71
  define_method(:def_prepared) do |name, sql, &block|
69
72
  sql = sql.dup
70
73
  name = name.to_sym
71
74
 
72
75
  params = Parameters.parse_params! sql
73
76
  prepared_statements[name] = sql
74
- Prepared.define_prepared_method(mod, name, params, &block)
77
+ Prepared.define_prepared_method(self, name, params, &block)
75
78
  end
76
79
  end
77
80
  end
@@ -82,8 +85,9 @@ module DbMod
82
85
  # module or any of its included modules.
83
86
  #
84
87
  # @param mod [Module] module that has {DbMod} included
88
+ # @see DbMod#db_connect
85
89
  def self.define_prepare_all_statements(mod)
86
- mod.class.instance_eval do
90
+ class << mod
87
91
  define_method(:prepare_all_statements) do |conn|
88
92
  inherited_prepared_statements.each do |name, sql|
89
93
  conn.prepare(name.to_s, sql)
@@ -99,6 +103,7 @@ module DbMod
99
103
  # @param statements [Hash] named list of prepared statements
100
104
  # @param klass [Class,Module] ancestor (hopefully a DbMod module)
101
105
  # to collect prepared statements from
106
+ # @see Prepared.define_inherited_prepared_statements
102
107
  def self.merge_statements(statements, klass)
103
108
  return unless klass.respond_to? :prepared_statements
104
109
  return if klass.prepared_statements.nil?
@@ -119,8 +124,9 @@ module DbMod
119
124
  # @param params [Fixnum,Array<Symbol>]
120
125
  # expected parameter count, or a list of argument names.
121
126
  # An empty array produces a no-argument method.
122
- # @yield dsl block may be passed, which will be evaluated using a
123
- # {Configuration::MethodConfiguration} object as scope
127
+ # @param block [Proc] A dsl block may be passed, which will be evaluated
128
+ # using a {Configuration::MethodConfiguration} object as scope
129
+ # @see Configurable.def_configurable
124
130
  def self.define_prepared_method(mod, name, params, &block)
125
131
  if params == []
126
132
  define_no_args_prepared_method(mod, name, &block)
@@ -137,12 +143,18 @@ module DbMod
137
143
  # where the method will be defined
138
144
  # @param name [Symbol] name of the method to be defined
139
145
  # and the prepared query to be called.
146
+ # @param block [Proc] see {Configuration::MethodConfiguration}
140
147
  # @yield dsl method configuration object may be passed
141
148
  def self.define_no_args_prepared_method(mod, name, &block)
142
149
  method = ->(*) { conn.exec_prepared(name.to_s) }
143
150
  Configuration.def_configurable mod, name, method, &block
144
151
  end
145
152
 
153
+ # Define a method on the given module with the given name
154
+ # and parameters that will call the prepared statement with
155
+ # the same name. Additional method configuration settings may
156
+ # be passed via a block.
157
+ #
146
158
  # @param mod [Module] {DbMod} enabled module
147
159
  # where the method will be defined
148
160
  # @param name [Symbol] name of the method to be defined
@@ -150,6 +162,8 @@ module DbMod
150
162
  # @param params [Fixnum,Array<Symbol>]
151
163
  # expected parameter count, or a list of argument names.
152
164
  # An empty array produces a no-argument method.
165
+ # @param block [Proc] see {Configuration::MethodConfiguration}
166
+ # @yield dsl method configuration object may be passed
153
167
  def self.define_prepared_method_with_args(mod, name, params, &block)
154
168
  method = ->(*args) { conn.exec_prepared(name.to_s, args) }
155
169
  Configuration.def_configurable(mod, name, method, params, &block)
@@ -160,8 +174,9 @@ module DbMod
160
174
  # {DbMod#db_connect} is called.
161
175
  #
162
176
  # @param mod [Module]
177
+ # @see Prepared.define_inherited_prepared_statements
163
178
  def self.define_prepared_statements(mod)
164
- mod.class.instance_eval do
179
+ class << mod
165
180
  define_method(:prepared_statements) do
166
181
  @prepared_statements ||= {}
167
182
  end
@@ -172,8 +187,12 @@ module DbMod
172
187
  # of named prepared statements declared on this module and all
173
188
  # included modules will be added to the connection when
174
189
  # {DbMod#db_connect} is called.
190
+ #
191
+ # @param mod [Module] where +inherited_prepared_statements+
192
+ # should be defined
193
+ # @see Prepared.define_prepare_all_statements
175
194
  def self.define_inherited_prepared_statements(mod)
176
- mod.class.instance_eval do
195
+ class << mod
177
196
  define_method(:inherited_prepared_statements) do
178
197
  inherited = {}
179
198
  ancestors.each do |klass|
@@ -46,7 +46,8 @@ module DbMod
46
46
  # Defines a module-specific +def_statement+ function
47
47
  # for a module that has just had {DbMod} included.
48
48
  #
49
- # @param mod [Module]
49
+ # @param mod [Module] module with {DbMod} included
50
+ # @see DbMod.included
50
51
  def self.setup(mod)
51
52
  Statement.define_def_statement(mod)
52
53
  end
@@ -59,14 +60,16 @@ module DbMod
59
60
  # arbitrary name.
60
61
  #
61
62
  # @param mod [Module] a module with {DbMod} included
63
+ # @raise ArgumentError if there is a problem parsing
64
+ # method parameters from the SQL statement
62
65
  def self.define_def_statement(mod)
63
- mod.class.instance_eval do
66
+ class << mod
64
67
  define_method(:def_statement) do |name, sql, &block|
65
68
  sql = sql.dup
66
69
  name = name.to_sym
67
70
 
68
71
  params = Parameters.parse_params! sql
69
- Statement.define_statement_method(mod, name, params, sql, &block)
72
+ Statement.define_statement_method(self, name, params, sql, &block)
70
73
  end
71
74
  end
72
75
  end
@@ -80,8 +83,10 @@ module DbMod
80
83
  # @param params [Fixnum,Array<Symbol>]
81
84
  # expected parameter count, or a list of argument names.
82
85
  # An empty array produces a no-argument method.
83
- # @yield dsl block may be passed, which will be evaluated using a
84
- # {Configuration::MethodConfiguration} object as scope
86
+ # @param sql [String] sql statement to execute
87
+ # @param block [Proc] A dsl block may be passed, which will be evaluated
88
+ # using a {Configuration::MethodConfiguration} object as scope
89
+ # @see Configuration.def_configurable
85
90
  def self.define_statement_method(mod, name, params, sql, &block)
86
91
  if params == []
87
92
  Configuration.def_configurable(mod, name, ->(*) { query sql }, &block)
@@ -32,6 +32,9 @@ module DbMod
32
32
 
33
33
  # Start the database transaction, or fail if
34
34
  # one is already open.
35
+ #
36
+ # @raise [Exceptions::AlreadyInTransaction]
37
+ # @see #transaction
35
38
  def start_transaction!
36
39
  fail DbMod::Exceptions::AlreadyInTransaction if @in_transaction
37
40
  @in_transaction = true
@@ -40,6 +43,8 @@ module DbMod
40
43
  end
41
44
 
42
45
  # End the database transaction
46
+ #
47
+ # @see #transaction
43
48
  def end_transaction!
44
49
  @in_transaction = false
45
50
  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.5'
4
+ VERSION = '0.0.6'
5
5
  end
@@ -136,6 +136,67 @@ describe DbMod::Statements::Configuration::Defaults do
136
136
  end
137
137
  end
138
138
 
139
+ context 'procs as defaults' do
140
+ MINS_FOR = {
141
+ 1 => 10,
142
+ 2 => 20
143
+ }
144
+
145
+ subject do
146
+ Module.new do
147
+ include DbMod
148
+
149
+ def min_for(args)
150
+ MINS_FOR[args[:id]]
151
+ end
152
+
153
+ def_prepared(:a, %(
154
+ SELECT *
155
+ FROM foo
156
+ WHERE id = $1
157
+ AND x > $2
158
+ )) { defaults ->(args) { MINS_FOR[args.first] } }
159
+
160
+ def_prepared(:b, %(
161
+ SELECT *
162
+ FROM foo
163
+ WHERE id = $id
164
+ AND x > $min
165
+ )) { defaults min: ->(args) { min_for(args) } }
166
+ end.create db: 'testdb'
167
+ end
168
+
169
+ it 'works with indexed parameters' do
170
+ expect(@conn).to receive(:exec_prepared).with('a', [1, 10])
171
+ subject.a(1)
172
+
173
+ expect(@conn).to receive(:exec_prepared).with('a', [2, 20])
174
+ subject.a(2)
175
+
176
+ expect(@conn).to receive(:exec_prepared).with('a', [1, 11])
177
+ subject.a(1, 11)
178
+
179
+ expect(@conn).to receive(:exec_prepared).with('a', [3, nil])
180
+ subject.a(3)
181
+ end
182
+
183
+ it 'works with named parameters' do
184
+ expect(@conn).to receive(:exec_prepared).with('b', [1, 10])
185
+ subject.b(id: 1)
186
+
187
+ expect(@conn).to receive(:exec_prepared).with('b', [2, 20])
188
+ subject.b(id: 2)
189
+
190
+ expect(@conn).to receive(:exec_prepared).with('b', [1, 11])
191
+ subject.b(id: 1, min: 11)
192
+
193
+ expect(@conn).to receive(:exec_prepared).with('b', [3, nil])
194
+ subject.b(id: 3)
195
+
196
+ expect { subject.b }.to raise_exception ArgumentError
197
+ end
198
+ end
199
+
139
200
  context 'definition-time validation' do
140
201
  it 'must use named or indexed parameters appropriately' do
141
202
  expect do
@@ -0,0 +1,41 @@
1
+ require 'spec_helper'
2
+
3
+ describe DbMod::Statements::Configuration::MethodConfiguration do
4
+ it 'can be constructed with a block' do
5
+ config = subject.class.new { single(:row!).as(:json) }
6
+
7
+ expect(config.to_hash).to eq(single: :row!, as: :json)
8
+ end
9
+
10
+ it 'can be constructed with named args' do
11
+ config = subject.class.new as: :json
12
+
13
+ expect(config.to_hash).to eq(as: :json)
14
+ end
15
+
16
+ it 'can be constructed from another config object' do
17
+ base = subject.class.new { single(:row!).as(:json) }
18
+ config = subject.class.new base
19
+
20
+ expect(config.to_hash).to eq(single: :row!, as: :json)
21
+ end
22
+
23
+ it 'can be constructed from a lambda proc' do
24
+ base = ->() { as(:csv) }
25
+ config = subject.class.new base
26
+
27
+ expect(config.to_hash).to eq(as: :csv)
28
+ end
29
+
30
+ it 'can be assembled from multiple sources' do
31
+ base = { as: :json, single: :row, defaults: [1, 2, 3] }
32
+ csv = ->() { as(:csv) }
33
+
34
+ config = subject.class.new(base, csv) { defaults 3, 4, 5 }
35
+ expect(config.to_hash).to eq(single: :row, as: :csv, defaults: [3, 4, 5])
36
+ end
37
+
38
+ it 'validates arguments' do
39
+ expect { subject.class.new 1, 2, 3 }.to raise_exception ArgumentError
40
+ end
41
+ end
@@ -0,0 +1,69 @@
1
+ require 'spec_helper'
2
+ require 'csv'
3
+
4
+ describe DbMod::Statements::Configuration::Returning do
5
+ before do
6
+ @conn = instance_double 'PGconn'
7
+ allow(@conn).to receive(:prepare)
8
+ allow(PGconn).to receive(:connect).and_return(@conn)
9
+
10
+ allow(@conn).to receive(:exec_prepared).with('a').and_return([
11
+ { 'name' => 'cow', 'sound' => 'moo' },
12
+ { 'name' => 'dog', 'sound' => 'woof' }
13
+ ])
14
+ end
15
+
16
+ subject do
17
+ Module.new do
18
+ include DbMod
19
+
20
+ def_prepared(:a, 'SELECT name, sound FROM animals') do
21
+ returning do |animals|
22
+ animals.map do |animal|
23
+ "the #{animal['name']} goes #{animal['sound']}"
24
+ end.join ' and '
25
+ end
26
+ end
27
+
28
+ def send_email(*); end
29
+
30
+ def_prepared(:b, 'SELECT address FROM email WHERE id = $1') do
31
+ single(:value)
32
+
33
+ returning { |email| send_email(email, a) }
34
+ end
35
+
36
+ def_prepared(:c, 'SELECT * FROM bar') do
37
+ as(:csv)
38
+
39
+ returning { |json| send_email('som@body', json) }
40
+ end
41
+ end.create db: 'test'
42
+ end
43
+
44
+ it 'performs arbitrary result transformations' do
45
+ expect(subject.a).to eq(
46
+ 'the cow goes moo and the dog goes woof'
47
+ )
48
+ end
49
+
50
+ it 'has access to module instance scope' do
51
+ allow(@conn).to receive(:exec_prepared).with('b', [1]).and_return([
52
+ { 'address' => 'ex@mple' }
53
+ ])
54
+ expect(subject).to receive(:send_email).with(
55
+ 'ex@mple',
56
+ 'the cow goes moo and the dog goes woof'
57
+ )
58
+ subject.b(1)
59
+ end
60
+
61
+ it 'works with as' do
62
+ allow(@conn).to receive(:exec_prepared).and_return([
63
+ { 'a' => '1', 'b' => '2' },
64
+ { 'a' => '3', 'b' => '4' }
65
+ ])
66
+ expect(subject).to receive(:send_email).with('som@body', "a,b\n1,2\n3,4\n")
67
+ subject.c
68
+ end
69
+ end
@@ -0,0 +1,63 @@
1
+ require 'spec_helper'
2
+ require 'json'
3
+ require 'csv'
4
+
5
+ describe DbMod::Statements::DefaultMethodSettings do
6
+ subject do
7
+ Module.new do
8
+ include DbMod
9
+
10
+ default_method_settings do
11
+ single(:row).as(:json).returning(&:inspect)
12
+ end
13
+
14
+ def_prepared(:a, 'SELECT * FROM a WHERE id = $1')
15
+ def_prepared(:b, 'SELECT * FROM b WHERE id = $1')
16
+ def_prepared(:c, 'SELECT * FROM c WHERE id = $1') { single(:column) }
17
+ end
18
+ end
19
+
20
+ before do
21
+ @conn = instance_double('PGconn')
22
+ allow(@conn).to receive(:prepare)
23
+ allow(PGconn).to receive(:connect).and_return(@conn)
24
+
25
+ allow(@conn).to receive(:exec_prepared) do |name, args|
26
+ [{ 'id' => args.first, name => (args.first * 2) }]
27
+ end
28
+ end
29
+
30
+ it 'applies settings to all defined methods' do
31
+ db = subject.create db: 'x'
32
+
33
+ expect(db.a('1')).to eq('"{\"id\":\"1\",\"a\":\"11\"}"')
34
+ expect(db.b('2')).to eq('"{\"id\":\"2\",\"b\":\"22\"}"')
35
+ end
36
+
37
+ it 'allows overrides to be provided for settings' do
38
+ db = subject.create db: 'x'
39
+
40
+ allow(@conn).to receive(:exec_prepared).and_return([
41
+ { 'x' => '1' },
42
+ { 'x' => '2' }
43
+ ])
44
+ expect(db.c('3')).to eq('"[\"1\",\"2\"]"')
45
+ end
46
+
47
+ it 'does not cascade to other modules' do
48
+ parent = subject
49
+ db = Module.new do
50
+ include parent
51
+
52
+ def_prepared(:d, 'SELECT * FROM d WHERE id = $1')
53
+ def_statement(:e, 'SELECT id FROM e WHERE x = $y') do
54
+ single(:value).returning { |id| a(id) }
55
+ end
56
+ end.create db: 'x'
57
+
58
+ expect(db.d('4')).to eq([{ 'id' => '4', 'd' => '44' }])
59
+
60
+ allow(@conn).to receive(:exec_params).and_return([{ 'id' => '5' }])
61
+ expect(db.e(y: 'y')).to eq('"{\"id\":\"5\",\"a\":\"55\"}"')
62
+ end
63
+ end
@@ -1,5 +1,11 @@
1
- require 'simplecov'
2
- SimpleCov.start
1
+ # Send coverage report to codeclimate when a repo token is given
2
+ if ENV['CODECLIMATE_REPO_TOKEN']
3
+ require 'codeclimate-test-reporter'
4
+ CodeClimate::TestReporter.start
5
+ else
6
+ require 'simplecov'
7
+ SimpleCov.start
8
+ end
3
9
 
4
10
  $LOAD_PATH.unshift(File.dirname(__FILE__))
5
11
  $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
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.5
4
+ version: 0.0.6
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-19 00:00:00.000000000 Z
11
+ date: 2015-10-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: pg
@@ -25,7 +25,63 @@ dependencies:
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
27
  - !ruby/object:Gem::Dependency
28
- name: simplecov
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: codeclimate-test-reporter
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: inch
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rake
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: redcarpet
29
85
  requirement: !ruby/object:Gem::Requirement
30
86
  requirements:
31
87
  - - ">="
@@ -67,7 +123,7 @@ dependencies:
67
123
  - !ruby/object:Gem::Version
68
124
  version: '0'
69
125
  - !ruby/object:Gem::Dependency
70
- name: yard
126
+ name: rubocop
71
127
  requirement: !ruby/object:Gem::Requirement
72
128
  requirements:
73
129
  - - ">="
@@ -81,7 +137,21 @@ dependencies:
81
137
  - !ruby/object:Gem::Version
82
138
  version: '0'
83
139
  - !ruby/object:Gem::Dependency
84
- name: bundler
140
+ name: simplecov
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
153
+ - !ruby/object:Gem::Dependency
154
+ name: yard
85
155
  requirement: !ruby/object:Gem::Requirement
86
156
  requirements:
87
157
  - - ">="
@@ -131,12 +201,14 @@ files:
131
201
  - lib/db_mod/statements/configuration/as/json.rb
132
202
  - lib/db_mod/statements/configuration/defaults.rb
133
203
  - lib/db_mod/statements/configuration/method_configuration.rb
204
+ - lib/db_mod/statements/configuration/returning.rb
134
205
  - lib/db_mod/statements/configuration/single.rb
135
206
  - lib/db_mod/statements/configuration/single/column.rb
136
207
  - lib/db_mod/statements/configuration/single/required_row.rb
137
208
  - lib/db_mod/statements/configuration/single/required_value.rb
138
209
  - lib/db_mod/statements/configuration/single/row.rb
139
210
  - lib/db_mod/statements/configuration/single/value.rb
211
+ - lib/db_mod/statements/default_method_settings.rb
140
212
  - lib/db_mod/statements/parameters.rb
141
213
  - lib/db_mod/statements/prepared.rb
142
214
  - lib/db_mod/statements/statement.rb
@@ -147,7 +219,10 @@ files:
147
219
  - spec/db_mod/statements/configuration/as/json_spec.rb
148
220
  - spec/db_mod/statements/configuration/as_spec.rb
149
221
  - spec/db_mod/statements/configuration/defaults_spec.rb
222
+ - spec/db_mod/statements/configuration/method_configuration_spec.rb
223
+ - spec/db_mod/statements/configuration/returning_spec.rb
150
224
  - spec/db_mod/statements/configuration/single_spec.rb
225
+ - spec/db_mod/statements/default_method_settings_spec.rb
151
226
  - spec/db_mod/statements/prepared_spec.rb
152
227
  - spec/db_mod/statements/statement_spec.rb
153
228
  - spec/db_mod/transaction_spec.rb
@@ -183,7 +258,10 @@ test_files:
183
258
  - spec/db_mod/statements/configuration/as/json_spec.rb
184
259
  - spec/db_mod/statements/configuration/as_spec.rb
185
260
  - spec/db_mod/statements/configuration/defaults_spec.rb
261
+ - spec/db_mod/statements/configuration/method_configuration_spec.rb
262
+ - spec/db_mod/statements/configuration/returning_spec.rb
186
263
  - spec/db_mod/statements/configuration/single_spec.rb
264
+ - spec/db_mod/statements/default_method_settings_spec.rb
187
265
  - spec/db_mod/statements/prepared_spec.rb
188
266
  - spec/db_mod/statements/statement_spec.rb
189
267
  - spec/db_mod/transaction_spec.rb