db2_query 0.2.2 → 0.2.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
  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