db_mod 0.0.5 → 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -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