db2_query 0.2.2 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a54ab3ca53e8078aee466400a89ec9b18c5c5ed65450d6d35cdf7a8552e0e21c
4
- data.tar.gz: 72ad54eb98b13a471ba8e022e57cb686a31cf7a2e1d833a4d54d833f3d900105
3
+ metadata.gz: 42c29fbb4c23d3cc0198ea49b0cbb9e980150a610dde50b19e422c8dd9519c99
4
+ data.tar.gz: 7ff6974cb986bc5df133229f275d4be9bac9a647e7936c35e1f3103967848b95
5
5
  SHA512:
6
- metadata.gz: a037c568900f33c3eefdc2692fd0d8d470dc4167ff87f3b1c0fa11902204fb38523b4bc257cf6cac069590a9706f1c94f506fc13fd35aa7388495fc9e28ea84a
7
- data.tar.gz: 9f1815d05567d1f972ff1f7d0ff5324a2d11781ae0000b13b552c1c21b4da260de7e7e0d8a633fb48a5ce5636ceea7f90e1fe0a509352a7ff99779788ef0b7d7
6
+ metadata.gz: 79661005fa24b32c0b03e119549377f5fea9dde025aa91dc5809ad52ad4f212f9b20a278736ea6a077aefb411d4bbc0513bb0e3acb629675d9735202206ebfd4
7
+ data.tar.gz: 6f58ef2ceb59765e54310d2ea7c1cf1132da62f6102e7187121691b8018670b6a9abe194d41d86cfd9042f35cb5e5746444dcf10c9adced05b85e2335c37f5e6
data/README.md CHANGED
@@ -45,7 +45,6 @@ At `db2query_database.yml` we can use two type of connection:
45
45
  ```yml
46
46
  development:
47
47
  primary: # Connection String Example
48
- adapter: db2_query
49
48
  conn_string:
50
49
  driver: DB2
51
50
  database: SAMPLE
@@ -56,8 +55,7 @@ development:
56
55
  protocol: IPC
57
56
  uid: <%= ENV["DB2EC_UID"] %>
58
57
  pwd: <%= ENV["DB2EC_PWD"] %>
59
- secondary:
60
- adapter: db2_query # DSN Example
58
+ secondary: # DSN Example
61
59
  dsn: iseries
62
60
  uid: <%= ENV["ISERIES_UID"] %>
63
61
  pwd: <%= ENV["ISERIES_PWD"] %>
@@ -92,7 +90,26 @@ class User < DB2Query::Base
92
90
  SELECT * FROM LIBTEST.USERS WHERE id = ?
93
91
  SQL
94
92
  end
93
+
94
+ class User < DB2query::Base
95
+ query :id_greater_than, -> id {
96
+ exec_query({}, "SELECT * FROM LIBTEST.USERS WHERE id > ?", [id])
97
+ }
98
+
99
+ query :insert_record, -> *args {
100
+ execute(
101
+ "INSERT INTO users (id, first_name, last_name, email) VALUES (?, ?, ?, ?)", args
102
+ )
103
+ }
104
+ end
95
105
  ```
106
+
107
+ The query method must have 2 inputs:
108
+ 1. Method name
109
+ 2. Body (can be an SQL statement or lamda).
110
+
111
+ The lambda is used to facilitate us in using `built-in methods` as shown at two query methods above.
112
+
96
113
  Or use a normal sql method (don't forget the `_sql` suffix)
97
114
  ```ruby
98
115
  class User < DB2Query::Base
@@ -144,11 +161,20 @@ SQL Load (3.28ms) SELECT * FROM LIBTEST.USERS WHERE id = ? [["id", 10000]]
144
161
  => #<DB2Query::Result @records=[#<Record id: 10000, first_name: "Dr.Strange", last_name: "Stephen", email: "strange@marvel.universe.com">]>
145
162
  ```
146
163
 
147
- ### Available methods
164
+ ### Available Result Ogject methods
148
165
  `DB2Query::Result` inherit all `ActiveRecord::Result` methods with additional custom methods:
149
166
  1. `records` to convert query result into array of Record objects.
150
167
  2. `to_h` to convert query result into hash with symbolized keys.
151
168
 
169
+ ### Built-in methods
170
+ These built-in methods are delegated to `DB2Query::Connection` methods
171
+ 1. `query_rows(sql)`
172
+ 2. `query_value(sql)`
173
+ 3. `query_values(sql)`
174
+ 4. `execute(sql)`
175
+ 5. `exec_query(formatters, sql, args = [])`
176
+ They behave just likely `ActiveRecords` connection's public methods.
177
+
152
178
  ### ActiveRecord Combination
153
179
 
154
180
  Create an abstract class that inherit from `ActiveRecord::Base`
@@ -5,6 +5,7 @@ require "erb"
5
5
  require "active_record"
6
6
  require "active_support"
7
7
  require "db2_query/config"
8
+ require "db2_query/error"
8
9
  require "db2_query/connection_handling"
9
10
 
10
11
  module DB2Query
@@ -12,6 +13,7 @@ module DB2Query
12
13
 
13
14
  autoload :Version
14
15
  autoload :Base
16
+ autoload :Bind
15
17
  autoload :Core
16
18
  autoload :DatabaseStatements
17
19
  autoload :Connection
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DB2Query
4
+ class Bind < Struct.new(:name, :value, :index)
5
+ end
6
+ end
@@ -6,6 +6,7 @@ module DB2Query
6
6
 
7
7
  include DB2Query::DatabaseStatements
8
8
  include ActiveSupport::Callbacks
9
+
9
10
  define_callbacks :checkout, :checkin
10
11
 
11
12
  set_callback :checkin, :after, :enable_lazy_transactions!
@@ -81,7 +82,7 @@ module DB2Query
81
82
  msg << "it is already in use by a different thread: #{@owner}. " \
82
83
  "Current thread: #{Thread.current}."
83
84
  end
84
- raise ActiveRecordError, msg
85
+ raise DB2Query::Error, msg
85
86
  end
86
87
 
87
88
  @owner = Thread.current
@@ -124,14 +125,14 @@ module DB2Query
124
125
  when RuntimeError
125
126
  exception
126
127
  else
127
- ActiveRecord::StatementInvalid.new(message, sql: sql, binds: binds)
128
+ DB2Query::StatementInvalid.new(message, sql: sql, binds: binds)
128
129
  end
129
130
  end
130
131
 
131
132
  def expire
132
133
  if in_use?
133
134
  if @owner != Thread.current
134
- raise ActiveRecordError, "Cannot expire connection, " \
135
+ raise DB2Query::Error, "Cannot expire connection, " \
135
136
  "it is owned by a different thread: #{@owner}. " \
136
137
  "Current thread: #{Thread.current}."
137
138
  end
@@ -139,7 +140,7 @@ module DB2Query
139
140
  @idle_since = Concurrent.monotonic_time
140
141
  @owner = nil
141
142
  else
142
- raise ActiveRecordError, "Cannot expire connection, it is not currently leased."
143
+ raise DB2Query::Error, "Cannot expire connection, it is not currently leased."
143
144
  end
144
145
  end
145
146
 
@@ -151,11 +152,11 @@ module DB2Query
151
152
  @owner = Thread.current
152
153
  end
153
154
  else
154
- raise ActiveRecordError, "Cannot steal connection, it is not currently leased."
155
+ raise DB2Query::Error, "Cannot steal connection, it is not currently leased."
155
156
  end
156
157
  end
157
158
 
158
- def seconds_idle # :nodoc:
159
+ def seconds_idle
159
160
  return 0 if in_use?
160
161
  Concurrent.monotonic_time - @idle_since
161
162
  end
@@ -17,7 +17,7 @@ module DB2Query
17
17
  end
18
18
  end
19
19
 
20
- class ConnectionSpecification #:nodoc:
20
+ class ConnectionSpecification
21
21
  attr_reader :name, :config
22
22
 
23
23
  def initialize(name, config)
@@ -70,12 +70,12 @@ module DB2Query
70
70
  RAILS_ENV = -> { (Rails.env if defined?(Rails.env)) || ENV["RAILS_ENV"].presence || ENV["RACK_ENV"].presence }
71
71
  DEFAULT_ENV = -> { RAILS_ENV.call || "default_env" }
72
72
 
73
- def lookup_connection_handler(handler_key) # :nodoc:
73
+ def lookup_connection_handler(handler_key)
74
74
  handler_key = DB2Query::Base.reading_role
75
75
  connection_handlers[handler_key] ||= DB2Query::ConnectionHandler.new
76
76
  end
77
77
 
78
- def resolve_config_for_connection(config_or_env) # :nodoc:
78
+ def resolve_config_for_connection(config_or_env)
79
79
  raise "Anonymous class is not allowed." unless name
80
80
 
81
81
  config_or_env ||= DEFAULT_ENV.call.to_sym
@@ -101,7 +101,7 @@ module DB2Query
101
101
  end
102
102
 
103
103
  private
104
- def swap_connection_handler(handler, &blk) # :nodoc:
104
+ def swap_connection_handler(handler, &blk)
105
105
  old_handler, DB2Query::Base.connection_handler = DB2Query::Base.connection_handler, handler
106
106
  return_value = yield
107
107
  return_value
@@ -40,19 +40,24 @@ module DB2Query
40
40
  formatters.store(attr_name, format)
41
41
  end
42
42
 
43
- def query(name, sql_statement)
43
+ def query(name, body)
44
44
  if defined_method_name?(name)
45
- raise ArgumentError, "You tried to define a scope named \"#{name}\" " \
45
+ raise DB2Query::Error, "You tried to define a scope named \"#{name}\" " \
46
46
  "on the model \"#{self.name}\", but DB2Query already defined " \
47
47
  "a class method with the same name."
48
48
  end
49
49
 
50
- unless sql_statement.strip.match?(/^select/i)
51
- raise NotImplementedError
52
- end
53
-
54
- self.class.define_method(name) do |*args|
55
- connection.exec_query(sql_statement, formatters, args)
50
+ if body.respond_to?(:call)
51
+ singleton_class.define_method(name) do |*args|
52
+ body.call(*args)
53
+ end
54
+ elsif body.is_a?(String)
55
+ sql = body
56
+ singleton_class.define_method(name) do |*args|
57
+ connection.exec_query(formatters, sql, args)
58
+ end
59
+ else
60
+ raise DB2Query::Error, "The query body needs to be callable or is a sql string"
56
61
  end
57
62
  end
58
63
 
@@ -73,12 +78,14 @@ module DB2Query
73
78
  sql_statement = allocate.method(sql_method).call
74
79
 
75
80
  unless sql_statement.is_a? String
76
- raise ArgumentError, "Query methods must return a SQL statement string!"
81
+ raise DB2Query::Error, "Query methods must return a SQL statement string!"
77
82
  end
78
83
 
79
84
  query(method_name, sql_statement)
80
85
 
81
86
  method(method_name).call(*args)
87
+ elsif connection.respond_to?(method_name)
88
+ connection.send(method_name, *args)
82
89
  else
83
90
  super
84
91
  end
@@ -13,25 +13,21 @@ module DB2Query
13
13
  query(sql)
14
14
  end
15
15
 
16
- def query_value(sql, name = nil)
16
+ def query_value(sql)
17
17
  single_value_from_rows(query(sql))
18
18
  end
19
19
 
20
- def query_values(sql, name = nil)
20
+ def query_values(sql)
21
21
  query(sql).map(&:first)
22
22
  end
23
23
 
24
24
  def execute(sql, args = [])
25
- if args.empty?
26
- @connection.do(sql)
27
- else
28
- @connection.do(sql, *args)
29
- end
25
+ @connection.do(sql, *args)
30
26
  end
31
27
 
32
- def exec_query(sql, formatter = {}, args = [], name = "SQL")
28
+ def exec_query(formatters, sql, args = [])
33
29
  binds, args = extract_binds_from_sql(sql, args)
34
- log(sql, name, binds, args) do
30
+ log(sql, "SQL", binds, args) do
35
31
  begin
36
32
  if args.empty?
37
33
  stmt = @connection.run(sql)
@@ -43,7 +39,7 @@ module DB2Query
43
39
  ensure
44
40
  stmt.drop unless stmt.nil?
45
41
  end
46
- DB2Query::Result.new(columns, rows, formatter)
42
+ DB2Query::Result.new(columns, rows, formatters)
47
43
  end
48
44
  end
49
45
 
@@ -54,36 +50,36 @@ module DB2Query
54
50
  end
55
51
 
56
52
  def key_finder_regex(k)
57
- /#{k} =\\? | #{k}=\\? | #{k}= \\? /i
53
+ /#{k} .\\? | #{k}.\\? | #{k}. \\? /i
58
54
  end
59
55
 
60
56
  def extract_binds_from_sql(sql, args)
61
57
  question_mark_positions = sql.enum_for(:scan, /\?/i).map { Regexp.last_match.begin(0) }
62
- args = args.first.is_a?(Hash) ? args.first : args.is_a?(Array) ? args : [args]
58
+ args = args.first.is_a?(Hash) ? args.first : args
63
59
  given, expected = args.length, question_mark_positions.length
64
60
 
65
61
  if given != expected
66
- raise ArgumentError, "wrong number of arguments (given #{given}, expected #{expected})"
62
+ raise DB2Query::Error, "wrong number of arguments (given #{given}, expected #{expected})"
67
63
  end
68
64
 
69
65
  if args.is_a?(Hash)
70
66
  binds = args.map do |key, value|
71
67
  position = sql.enum_for(:scan, key_finder_regex(key)).map { Regexp.last_match.begin(0) }
72
68
  if position.empty?
73
- raise ArgumentError, "Column name: `#{key}` not found inside sql statement."
69
+ raise DB2Query::Error, "Column name: `#{key}` not found inside sql statement."
74
70
  elsif position.length > 1
75
- raise ArgumentError, "Can't handle such this kind of sql. Please refactor your sql."
71
+ raise DB2Query::Error, "Can't handle such this kind of sql. Please refactor your sql."
76
72
  else
77
73
  index = position[0]
78
74
  end
79
75
 
80
- OpenStruct.new({ name: key.to_s, value: value, index: index })
76
+ DB2Query::Bind.new(key.to_s, value, index)
81
77
  end
82
78
  binds = binds.sort_by { |bind| bind.index }
83
79
  [binds.map { |bind| [bind, bind.value] }, binds.map { |bind| bind.value }]
84
80
  elsif question_mark_positions.length == 1 && args.length == 1
85
- column = sql[/(.*?) = \?|(.*?) =\?|(.*?)= \?|(.*?)=\?/m, 1].split.last.downcase
86
- bind = OpenStruct.new({ name: column, value: args })
81
+ column = sql[/(.*?) . \?|(.*?) .\?|(.*?). \?|(.*?).\?/m, 1].split.last.downcase
82
+ bind = DB2Query::Bind.new(column.gsub(/[)(]/, ""), args, 0)
87
83
  [[[bind, bind.value]], bind.value]
88
84
  else
89
85
  [args.map { |arg| [nil, arg] }, args]
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DB2Query
4
+ class Error < StandardError
5
+ end
6
+
7
+ class StatementInvalid < ActiveRecord::ActiveRecordError
8
+ def initialize(message = nil, sql: nil, binds: nil)
9
+ super(message || $!.try(:message))
10
+ @sql = sql
11
+ @binds = binds
12
+ end
13
+
14
+ attr_reader :sql, :binds
15
+ end
16
+ end
@@ -21,7 +21,7 @@ module DB2Query
21
21
 
22
22
  class AbstractFormatter
23
23
  def format(value)
24
- raise NotImplementedError, "Implement format method in your subclass."
24
+ raise DB2Query::Error, "Implement format method in your subclass."
25
25
  end
26
26
  end
27
27
  end
@@ -16,13 +16,17 @@ module DB2Query
16
16
  def initialize(config)
17
17
  @config = config
18
18
  end
19
+
20
+ def connect
21
+ raise "abstract method #connect must be defined"
22
+ end
19
23
  end
20
24
 
21
25
  class DsnConnector < AbstractConnector
22
26
  def connect
23
27
  ::ODBC.connect(config[:dsn], config[:uid], config[:pwd])
24
28
  rescue ::ODBC::Error => e
25
- raise ArgumentError, "Unable to activate ODBC DSN connection #{e}"
29
+ raise DB2Query::Error, "Unable to activate ODBC DSN connection #{e}"
26
30
  end
27
31
  end
28
32
 
@@ -34,7 +38,7 @@ module DB2Query
34
38
  end
35
39
  ::ODBC::Database.new.drvconnect(driver)
36
40
  rescue ::ODBC::Error => e
37
- raise ArgumentError, "Unable to activate ODBC Conn String connection #{e}"
41
+ raise DB2Query::Error, "Unable to activate ODBC Conn String connection #{e}"
38
42
  end
39
43
  end
40
44
  end
@@ -5,12 +5,10 @@ DB2_QUERY_DATABASE_TEMPLATE ||= <<-EOF
5
5
  # Database configuration example
6
6
  development:
7
7
  primary:
8
- adapter: db2_query
9
8
  dsn: iseries
10
9
  uid: <%= ENV["ISERIES_UID"] %>
11
10
  pwd: <%= ENV["ISERIES_PWD"] %>
12
11
  secondary:
13
- adapter: db2_query
14
12
  conn_string:
15
13
  driver: DB2
16
14
  database: ARUNIT2
@@ -23,12 +21,10 @@ development:
23
21
  pwd: <%= ENV["DB2EC_PWD"] %>
24
22
  test:
25
23
  primary:
26
- adapter: db2_query
27
24
  dsn: iseries
28
25
  uid: <%= ENV["ISERIES_UID"] %>
29
26
  pwd: <%= ENV["ISERIES_PWD"] %>
30
27
  secondary:
31
- adapter: db2_query
32
28
  conn_string:
33
29
  driver: DB2
34
30
  database: ARUNIT2
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DB2Query
4
- VERSION = "0.2.2"
4
+ VERSION = "0.2.3"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: db2_query
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.2
4
+ version: 0.2.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - yohanes_l
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-08-10 00:00:00.000000000 Z
11
+ date: 2020-08-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ruby-odbc
@@ -108,20 +108,6 @@ dependencies:
108
108
  - - ">="
109
109
  - !ruby/object:Gem::Version
110
110
  version: '0'
111
- - !ruby/object:Gem::Dependency
112
- name: dotenv
113
- requirement: !ruby/object:Gem::Requirement
114
- requirements:
115
- - - ">="
116
- - !ruby/object:Gem::Version
117
- version: '0'
118
- type: :development
119
- prerelease: false
120
- version_requirements: !ruby/object:Gem::Requirement
121
- requirements:
122
- - - ">="
123
- - !ruby/object:Gem::Version
124
- version: '0'
125
111
  - !ruby/object:Gem::Dependency
126
112
  name: byebug
127
113
  requirement: !ruby/object:Gem::Requirement
@@ -136,20 +122,6 @@ dependencies:
136
122
  - - ">="
137
123
  - !ruby/object:Gem::Version
138
124
  version: '0'
139
- - !ruby/object:Gem::Dependency
140
- name: sqlite3
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
125
  description: A Rails query plugin to fetch data from Db2 database by using ODBC connection.
154
126
  email:
155
127
  - yohanes.lumentut@yahoo.com
@@ -162,11 +134,13 @@ files:
162
134
  - Rakefile
163
135
  - lib/db2_query.rb
164
136
  - lib/db2_query/base.rb
137
+ - lib/db2_query/bind.rb
165
138
  - lib/db2_query/config.rb
166
139
  - lib/db2_query/connection.rb
167
140
  - lib/db2_query/connection_handling.rb
168
141
  - lib/db2_query/core.rb
169
142
  - lib/db2_query/database_statements.rb
143
+ - lib/db2_query/error.rb
170
144
  - lib/db2_query/formatter.rb
171
145
  - lib/db2_query/odbc_connector.rb
172
146
  - lib/db2_query/railtie.rb
@@ -195,7 +169,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
195
169
  - !ruby/object:Gem::Version
196
170
  version: '0'
197
171
  requirements: []
198
- rubygems_version: 3.0.3
172
+ rubygems_version: 3.1.3
199
173
  signing_key:
200
174
  specification_version: 4
201
175
  summary: DB2Query