db2_query 0.1.0 → 0.3.0

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: 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