db2_query 0.1.0 → 0.3.0

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: 5d2946448234d56484778d59e3051573c81c2789631776f0ed543a79fdc510fa
4
- data.tar.gz: a257fabf1a7de500f7d325c70dd11f494a08163dbf78460f61b3bf51cb626b77
3
+ metadata.gz: '09ecde3a8404d9aa56b416cbd3aa89efb21519166dea225cf8e3d7a54a2b4ed1'
4
+ data.tar.gz: c72fe3cc5e681d99d3dd5a6ec85863cb15b4b95e72b92e42dcb9d20fac910fbc
5
5
  SHA512:
6
- metadata.gz: ca8c1b52138ec97d9f4e9cbbca0d8bb148e95f4ac181a929e3e5715b597ed59b70e25b779156ba45a7fe9f119203e4162a2a932c63d11c94acdc2a1eaeb56db8
7
- data.tar.gz: 67f0d7228274d4a7a6e38771cc0743b83da27621d3ce310e0d5d7d323e937316f700ec3b278734312ff27cdc88796321cb09f02d7c2bf48b8c5607a7331a7f27
6
+ metadata.gz: 20370e437967f91a7b273b21339c22ad4f868310812be5ff40676eb0ae28044def876276044aa1a0f237fec1b47e3acf6b7591e2bf1d76d53b7876ca688528ae
7
+ data.tar.gz: aa62b2ff0df74fb300963ab83c36f066b6e032d26de149ade42ed7cd79daac5579aedeae1bafd264747466a0719520b76ef8cef49e7e1e4c64a9cc84eb9f46db
data/MIT-LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright 2020 yohanes_l
1
+ Copyright 2021 yohanes oktavianus lumentut
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
data/README.md CHANGED
@@ -1,5 +1,10 @@
1
- # Db2Query
2
- A Rails query plugin to fetch data from Db2 database by using ODBC connection.
1
+ # DB2Query
2
+
3
+ [![Gem Version](https://badge.fury.io/rb/db2_query.svg)](https://badge.fury.io/rb/db2_query)
4
+
5
+ A Rails 5 & Rails 6 plugin for handling Db2 SQL database `SIUD` statement (`SELECT`, `INPUT`, `UPDATE`, `DELETE`) by using ODBC connection.
6
+
7
+ Note: Tested at Rails 5.2.6 and Rails 6.1.4
3
8
 
4
9
  ## Installation
5
10
  Add this line to your application's Gemfile:
@@ -15,7 +20,7 @@ $ bundle
15
20
 
16
21
  Or install it yourself as:
17
22
  ```bash
18
- $ gem install db2-query
23
+ $ gem install db2_query
19
24
  ```
20
25
 
21
26
  ## Initialization
@@ -24,67 +29,170 @@ Execute init task at the app root
24
29
  $ rake db2query:init
25
30
  ```
26
31
  Db2Query will generate two required files:
27
- - `config/db2query_database.yml`
28
- - `config/initializers/db2query`
32
+ - `config/db2query.yml`
33
+ - `config/initializers/db2query.rb`
29
34
 
30
- Edit these files according to the requirement.
35
+ Edit those files according to the requirement.
31
36
 
32
37
  ### Database Configuration
33
- At `db2query_database.yml` we can use two type of connection:
34
- 1. DSN connection config
35
- 2. Connection String config
38
+ At version 0.3.0, `db2query.yml` only use DSN connection config:
36
39
  ```yml
37
- development:
38
- primary: # Connection String Example
39
- conn_string:
40
- driver: DB2
41
- database: SAMPLE
42
- dbalias: SAMPLE
43
- hostname: LOCALHOST
44
- currentschema: LIBTEST
45
- port: "0"
46
- protocol: IPC
47
- uid: <%= ENV["DB2EC_UID"] %>
48
- pwd: <%= ENV["DB2EC_PWD"] %>
49
- secondary: # DSN Example
50
- dsn: SAMPLE
51
- uid: <%= ENV["DB2EC_UID"] %>
52
- pwd: <%= ENV["DB2EC_PWD"] %>
40
+ development:
41
+ dsn: ARUNIT
42
+ pool: 5
43
+ timeout: 5
44
+ test:
45
+ dsn: ARUNIT
46
+ pool: 5
47
+ timeout: 5
48
+ production:
49
+ dsn: ARUNIT
50
+ pool: 5
51
+ timeout: 5
52
+ ```
53
+
54
+ Ensure that `unixodbc` have been installed and test your connection first by using `isql` commands.
55
+
56
+ Example:
57
+
58
+ Secondary database connection test
59
+ ```bash
60
+ $ isql -v ARUNIT
61
+ +---------------------------------------+
62
+ | Connected! |
63
+ | |
64
+ | sql-statement |
65
+ | help [tablename] |
66
+ | quit |
67
+ | |
68
+ +---------------------------------------+
69
+ SQL>
53
70
  ```
54
71
 
55
72
  ## Usage
73
+ Note: Version 0.1.0 use `Db2Query` namespace. Version 0.2 use `DB2Query`. At version 0.3.0 we use `Db2Query` , revert back to the previous namespace.
74
+
56
75
  ### Basic Usage
57
- Create query class that inherit from `Db2Query::Base` in `app/queries` folder
76
+ Create query class that inherit from `DB2Query::Base` in `app/queries` folder.
77
+
78
+ Note: `$`symbol is used as column name prefix.
79
+
80
+ ### 1. `query` method
81
+ The `query` method must have 2 inputs:
82
+ 1. Method name
83
+ 2. Body (can be an SQL statement or lamda).
84
+
85
+ The lambda is used to facilitate us in using `built-in methods` as shown at two query methods below:
86
+ Example 1.
58
87
  ```ruby
59
- class Users < Db2Query::Base
88
+ class User < Db2Query::Base
60
89
  query :find_by, <<-SQL
61
- SELECT * FROM LIBTEST.USERS WHERE user_id = ?
90
+ SELECT * FROM LIBTEST.USERS WHERE $id = ?
62
91
  SQL
63
92
  end
64
93
  ```
65
- Or use a normal sql method (don't forget the `_sql` suffix)
94
+ ```bash
95
+ irb(main):004:0> User.find_by 10000
96
+ SQL (3.2ms) SELECT * FROM LIBTEST.USERS WHERE id = ? [["id", 10000]]
97
+ => #<Db2Query::Result [#<Record id: 10000, first_name: "Wilma", last_name: "Lindgren", email: "cleveland_kilback@breitenberg.com">]>
98
+ ```
99
+ Example 2.
66
100
  ```ruby
67
- class Users < Db2Query::Base
101
+ class User < Db2query::Base
102
+ query :id_greater_than, -> id {
103
+ exec_query({}, "SELECT * FROM LIBTEST.USERS WHERE $id > ?", [id])
104
+ }
105
+ end
106
+ ```
107
+ ```bash
108
+ irb(main):003:0> User.id_greater_than 10000
109
+ SQL (3.2ms) SELECT * FROM LIBTEST.USERS WHERE id > ? [["id", 1000]]
110
+ => #<Db2Query::Result [#<Record id: 10000, first_name: "Wilma", last_name: "Lindgren", email: "cleveland_kilback@breitenberg.com">...">]>
111
+ ```
112
+ Example 3.
113
+ ```ruby
114
+ class User < Db2query::Base
115
+ query :insert_record, -> args {
116
+ exec_query({},
117
+ "INSERT INTO users ($id, $first_name, $last_name, $email) VALUES (?, ?, ?, ?)", args
118
+ )
119
+ }
120
+ end
121
+ ```
122
+ ```bash
123
+ ```
124
+
125
+ ### 2. Plain/normal method
126
+ At a plain/normal sql method we add `_sql` suffix. For example `find_by_sql`
127
+ ```ruby
128
+ class User < Db2Query::Base
68
129
  def find_by_sql
69
- "SELECT * FROM LIBTEST.USERS WHERE user_id = ?"
130
+ "SELECT * FROM LIBTEST.USERS WHERE $id = ?"
70
131
  end
71
132
  end
72
133
  ```
73
- Check it at rails console
134
+ Then we can call it by using `find_by` class method.
135
+ ```bash
136
+ irb(main):001:0> User.find_by 10000
137
+ SQL Load (3.28ms) SELECT * FROM LIBTEST.USERS WHERE id = ? [[nil, 10000]]
138
+ => #<DB2Query::Result @records=[#<Record id: 10000, first_name: "Strange", last_name: "Stephen", email: "strange@marvel.universe.com">]>
139
+ ```
140
+ Or with hash arguments input
141
+ ```ruby
142
+ class User < Db2Query::Base
143
+ def by_name_and_email_sql
144
+ "SELECT * FROM LIBTEST.USERS WHERE $first_name = ? AND $email = ?"
145
+ end
146
+ end
147
+ ```
148
+ ```bash
149
+ irb(main):001:0> User.by_name_and_email first_name: "Strange", email: "strange@marvel.universe.com"
150
+ SQL Load (3.28ms) SELECT * FROM LIBTEST.USERS WHERE first_name = ? AND last_name = ? [["first_name", Strange], ["email", strange@marvel.universe.com]]
151
+ => #<DB2Query::Result @records=[#<Record id: 10000, first_name: "Strange", last_name: "Stephen", email: "strange@marvel.universe.com">]>
152
+ ```
153
+ ### SQL extention (`@extention`)
154
+ For a reusable sql, we can extend it by using a combination of `extention` and `sql_with_extention` methods, with an `@extention` pointer at SQL statement.
155
+ ```ruby
156
+ class User < Db2Query::Base
157
+ # reusable SQL
158
+ _SQL = -> extention {
159
+ sql_with_extention("SELECT * FROM LIBTEST.USERS WHERE @extention", extention)
160
+ }
161
+ # implementation
162
+ query :by_email, _SQL.("$email = ?")
163
+ end
164
+ ```
74
165
  ```bash
75
- Users.find_by 10000
76
- Users Load (330.28ms) SELECT * FROM LIBTEST.USERS WHERE user_id = ? [[10000]]
77
- => #<Db2Query::Result [#<Users::FindBy user_id: 10000, first_name: "Alex", last_name: "Jacobi", email: "lula_durgan@dooley.com">]>
166
+ irb(main):004:0> User.by_email "strange@marvel.universe.com"
167
+ SQL (3.2ms) SELECT * FROM LIBTEST.USERS WHERE email = ? [["email", "strange@marvel.universe.com"]]
168
+ => #<DB2Query::Result @records=[#<Record id: 10000, first_name: "Strange", last_name: "Stephen", email: "strange@marvel.universe.com">]>
169
+ ```
170
+ ### List input (`@list`)
171
+ For an array consist list of inputs, we can use `fetch_list` method and `@list` pointer at the SQL statement.
172
+
173
+ ```ruby
174
+ class User < Db2Query
175
+ query :by_ids, -> ids {
176
+ fetch_list("SELECT * FROM LIBTEST.USERS WHERE ID IN (@list)", ids)
177
+ }
178
+ end
179
+ ```
180
+ ```bash
181
+ irb(main):007:0> User.by_ids [10000,10001,10002]
182
+ SQL (2.8ms) SELECT * FROM LIBTEST.USERS WHERE ID IN ('10000', '10001', '10002')
183
+ => #<Db2Query::Result [#<Record id: 10000, name: "Carol", last_name: "Danvers", email: "captain.marvel@marvel.universe.com">, #<Record id: 10001, first_name: "Natasha", last_name: "Romanova", email: "black.widow@marvel.universe">, #<Record id: 10002, first_name: "Wanda", last_name: "Maximoff", email: "scarlet.witch@marvel.universe.com">]>
184
+
78
185
  ```
186
+
79
187
  ### Formatter
80
- In order to get different result column format, a query result can be reformatted by add a formatter class that inherit `Db2Query::AbstractFormatter` then register at `config\initializers\db2query.rb`
188
+ In order to get different result column format, a query result can be reformatted by adding a formatter class that inherits `DB2Query::AbstractFormatter` then register at `config\initializers\db2query.rb`
81
189
  ```ruby
82
190
  require "db2_query/formatter"
83
191
 
84
192
  # create a formatter class
85
193
  class FirstNameFormatter < Db2Query::AbstractFormatter
86
194
  def format(value)
87
- "Mr/Mrs. " + value
195
+ "Dr." + value
88
196
  end
89
197
  end
90
198
 
@@ -95,30 +203,76 @@ end
95
203
  ```
96
204
  Use it at query class
97
205
  ```ruby
98
- class Users < Db2Query::Base
206
+ class Doctor < User
99
207
  attributes :first_name, :first_name_formatter
100
-
101
- query :find_by, <<-SQL
102
- SELECT * FROM LIBTEST.USERS WHERE user_id = ?
103
- SQL
104
208
  end
105
209
  ```
106
210
  Check it at rails console
107
211
  ```bash
108
- Users.find_by 10000
109
- Users Load (330.28ms) SELECT * FROM LIBTEST.USERS WHERE user_id = ? [[10000]]
110
- => #<Db2Query::Result [#<Users::FindBy user_id: 10000, first_name: "Mr/Mrs. Alex", last_name: "Jacobi", email: "lula_durgan@dooley.com">]>
212
+ Doctor.find_by id: 10000
213
+ SQL Load (3.28ms) SELECT * FROM LIBTEST.USERS WHERE id = ? [["id", 10000]]
214
+ => #<DB2Query::Result @records=[#<Record id: 10000, first_name: "Dr.Strange", last_name: "Stephen", email: "strange@marvel.universe.com">]>
111
215
  ```
112
- ### Available methods
113
- Db2Query::Result has public methods as follows:
114
- - to_a
115
- - to_hash
116
- - pluck
117
- - first
118
- - last
119
- - size
120
- - each
121
216
 
217
+ For complete examples please see the basic examples [here](https://github.com/yohaneslumentut/db2_query/blob/master/test/dummy/app/queries/test_query.rb).
218
+
219
+ ### Available Result Object methods
220
+ `Db2Query::Result` inherit all `ActiveRecord::Result` methods with additional custom methods:
221
+ 1. `records` to convert query result into array of Record objects.
222
+ 2. `to_h` to convert query result into hash with symbolized keys.
223
+
224
+ ### Built-in methods
225
+ These built-in methods are delegated to `Db2Query::Connection` methods
226
+ 1. `query_rows(sql)`
227
+ 2. `query_value(sql)`
228
+ 3. `query_values(sql)`
229
+ 4. `execute(sql)`
230
+ 5. `exec_query(formatters, sql, args = [])`
231
+ They behave just likely `ActiveRecords` connection's public methods.
232
+
233
+ ### ActiveRecord Combination
234
+
235
+ Create an abstract class that inherit from `ActiveRecord::Base`
236
+ ```ruby
237
+ class Db2Record < ActiveRecord::Base
238
+ self.abstract_class = true
239
+
240
+ def self.query(formatter, sql, args = [])
241
+ Db2Query::Base.connection.exec_query(formatter, sql, args).to_a.map(&:deep_symbolize_keys)
242
+ end
243
+ end
244
+ ```
245
+
246
+ Utilize the goodness of rails model `scope`
247
+ ```ruby
248
+ class User < Db2Record
249
+ scope :by_name, -> *args {
250
+ query(
251
+ {}, "SELECT * FROM LIBTEST.USERS WHERE $first_name = ? AND $last_name = ?", args
252
+ )
253
+ }
254
+ end
255
+ ```
256
+ ```bash
257
+ User.by_name first_name: "Strange", last_name: "Stephen"
258
+ SQL Load (3.28ms) SELECT * FROM LIBTEST.USERS WHERE first_name = ? AND last_name = ? [["first_name", Strange], ["last_name", Stephen]]
259
+ => [{:id=> 10000, :first_name=> "Strange", :last_name=> "Stephen", :email=> "strange@marvel.universe.com"}]
260
+ ```
261
+
262
+ Another example:
263
+ ```ruby
264
+ class User < Db2Record
265
+ scope :age_gt, -> age {
266
+ query("SELECT * FROM LIBTEST.USERS WHERE age > #{age}")
267
+ }
268
+ end
269
+ ```
270
+
271
+ ```bash
272
+ User.age_gt 500
273
+ SQL Load (3.28ms) SELECT * FROM LIBTEST.USERS WHERE age > 500
274
+ => [{:id=> 99999, :first_name=> "Ancient", :last_name=> "One", :email=> "ancientone@marvel.universe.com"}]
275
+ ```
122
276
 
123
277
  ## License
124
- The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
278
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile CHANGED
@@ -1,23 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- begin
4
- require "bundler/setup"
5
- rescue LoadError
6
- puts "You must `gem install bundler` and `bundle install` to run rake tasks"
7
- end
8
-
9
- require "rdoc/task"
10
-
11
- RDoc::Task.new(:rdoc) do |rdoc|
12
- rdoc.rdoc_dir = "rdoc"
13
- rdoc.title = "Db2Query"
14
- rdoc.options << "--line-numbers"
15
- rdoc.rdoc_files.include("README.md")
16
- rdoc.rdoc_files.include("lib/**/*.rb")
17
- end
18
-
3
+ require "bundler/setup"
19
4
  require "bundler/gem_tasks"
20
-
21
5
  require "rake/testtask"
22
6
 
23
7
  Rake::TestTask.new(:test) do |t|
data/lib/db2_query.rb CHANGED
@@ -1,29 +1,22 @@
1
- # frozen_string_literal:true
1
+ # frozen_string_literal: true
2
2
 
3
- require "odbc"
4
- require "yaml"
5
- require "erb"
6
3
  require "active_record"
7
4
  require "active_support"
5
+ require "active_support/concurrency/load_interlock_aware_monitor"
6
+ require "connection_pool"
7
+ require "odbc_utf8"
8
8
 
9
9
  module Db2Query
10
10
  extend ActiveSupport::Autoload
11
11
 
12
12
  autoload :Version
13
- autoload :Error
14
- autoload :Path
15
- autoload :Schema
16
- autoload :DatabaseConfigurations
17
- autoload :ODBCConnector
13
+ autoload :Config
18
14
  autoload :Connection
19
- autoload :ConnectionHandling
20
- autoload :DatabaseStatements
21
- autoload :SQLValidator
22
- autoload :LogSubscriber
23
- autoload :Formatter
24
- autoload :Column
15
+ autoload :Core
25
16
  autoload :Result
17
+ autoload :Logger
26
18
  autoload :Base
27
- end
19
+ autoload :Error
28
20
 
29
- require "db2_query/railtie"
21
+ require "db2_query/railtie" if defined?(Rails)
22
+ end
@@ -2,83 +2,7 @@
2
2
 
3
3
  module Db2Query
4
4
  class Base
5
- include DatabaseConfigurations
6
- include ConnectionHandling
7
-
8
- class << self
9
- include SQLValidator
10
-
11
- def attributes(attr_name, format)
12
- attr_format.store(attr_name, format)
13
- end
14
-
15
- def query(method_name, sql_statement)
16
- unless is_query?(sql_statement)
17
- raise Error, "Query only for SQL query commands."
18
- end
19
-
20
- if self.class.respond_to?(method_name)
21
- raise Error, "Query :#{method_name} has been defined before"
22
- end
23
-
24
- self.class.define_method(method_name) do |*args|
25
- log(sql_statement, args) do
26
- columns, rows = connection.exec_query(sql_statement, *args)
27
- Result.new(self, method_name, columns, rows, attr_format)
28
- end
29
- end
30
- end
31
-
32
- private
33
- def attr_format
34
- @attr_format ||= Hash.new
35
- end
36
-
37
- def define_query_method(method_name, sql_statement)
38
- if is_query?(sql_statement)
39
- query(method_name, sql_statement)
40
- else
41
- raise NotImplementedError
42
- end
43
- end
44
-
45
- def method_missing(method_name, *args, &block)
46
- sql_methods = self.instance_methods.grep(/_sql/)
47
- sql_method = "#{method_name}_sql".to_sym
48
-
49
- if sql_methods.include?(sql_method)
50
- sql_statement = allocate.method(sql_method).call
51
-
52
- raise Error, "Query methods must return a SQL statement string!" unless sql_statement.is_a? String
53
-
54
- expected_args = sql_statement.count "?"
55
- given_args = args.size
56
-
57
- if expected_args == given_args
58
- define_query_method(method_name, sql_statement)
59
- else
60
- raise ArgumentError, "wrong number of arguments (given #{given_args}, expected #{expected_args})"
61
- end
62
-
63
- method(method_name).call(*args)
64
- else
65
- super
66
- end
67
- end
68
-
69
- def instrumenter
70
- @instrumenter ||= ActiveSupport::Notifications.instrumenter
71
- end
72
-
73
- def log(sql_statement, args)
74
- instrumenter.instrument(
75
- "sql.db2_query",
76
- sql: sql_statement,
77
- name: self,
78
- binds: args) do
79
- yield
80
- end
81
- end
82
- end
5
+ include Config
6
+ include Core
83
7
  end
84
8
  end