db2_query 0.2.3 → 0.3.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/MIT-LICENSE +1 -1
  3. data/README.md +472 -124
  4. data/Rakefile +3 -2
  5. data/lib/db2_query/base.rb +15 -5
  6. data/lib/db2_query/config.rb +20 -17
  7. data/lib/db2_query/core.rb +79 -60
  8. data/lib/db2_query/db_client.rb +56 -0
  9. data/lib/db2_query/db_connection.rb +68 -0
  10. data/lib/db2_query/db_statements.rb +87 -0
  11. data/lib/db2_query/definitions.rb +93 -0
  12. data/lib/db2_query/error.rb +72 -7
  13. data/lib/db2_query/field_type.rb +31 -0
  14. data/lib/db2_query/helper.rb +50 -0
  15. data/lib/db2_query/logger.rb +52 -0
  16. data/lib/db2_query/query.rb +128 -0
  17. data/lib/db2_query/railtie.rb +5 -10
  18. data/lib/db2_query/result.rb +51 -31
  19. data/lib/db2_query/sql_statement.rb +34 -0
  20. data/lib/db2_query/tasks/database.rake +2 -46
  21. data/lib/db2_query/tasks/init.rake +1 -1
  22. data/lib/db2_query/tasks/initializer.rake +2 -34
  23. data/lib/db2_query/tasks/templates/database.rb.tt +19 -0
  24. data/lib/db2_query/tasks/templates/initializer.rb.tt +8 -0
  25. data/lib/db2_query/tasks.rb +29 -0
  26. data/lib/db2_query/type/binary.rb +19 -0
  27. data/lib/db2_query/type/boolean.rb +41 -0
  28. data/lib/db2_query/type/date.rb +34 -0
  29. data/lib/db2_query/type/decimal.rb +15 -0
  30. data/lib/db2_query/type/integer.rb +15 -0
  31. data/lib/db2_query/type/string.rb +30 -0
  32. data/lib/db2_query/type/text.rb +11 -0
  33. data/lib/db2_query/type/time.rb +30 -0
  34. data/lib/db2_query/type/timestamp.rb +30 -0
  35. data/lib/db2_query/type/value.rb +29 -0
  36. data/lib/db2_query/version.rb +2 -2
  37. data/lib/db2_query.rb +42 -18
  38. data/lib/rails/generators/query/USAGE +15 -0
  39. data/lib/rails/generators/query/query_generator.rb +70 -0
  40. data/lib/rails/generators/query/templates/query.rb.tt +26 -0
  41. data/lib/rails/generators/query/templates/query_definitions.rb.tt +18 -0
  42. data/lib/rails/generators/query/templates/unit_test.rb.tt +9 -0
  43. metadata +74 -36
  44. data/lib/db2_query/bind.rb +0 -6
  45. data/lib/db2_query/connection.rb +0 -164
  46. data/lib/db2_query/connection_handling.rb +0 -112
  47. data/lib/db2_query/database_statements.rb +0 -89
  48. data/lib/db2_query/formatter.rb +0 -27
  49. data/lib/db2_query/odbc_connector.rb +0 -44
data/README.md CHANGED
@@ -1,10 +1,12 @@
1
- # DB2Query
1
+ # Db2Query
2
2
 
3
3
  [![Gem Version](https://badge.fury.io/rb/db2_query.svg)](https://badge.fury.io/rb/db2_query)
4
4
 
5
- A Rails 6+ query plugin to fetch data from Db2 database by using ODBC connection
5
+ A Rails 5 & Rails 6 plugin for handling Db2 SQL database `SIUD` statement (`SELECT`, `INSERT`, `UPDATE`, `DELETE`) by using ODBC connection.
6
6
 
7
- ## Installation
7
+ Note: Tested at Rails 5.2.6 and Rails 6.1.4
8
+
9
+ ## 1. Installation
8
10
  Add this line to your application's Gemfile:
9
11
 
10
12
  ```ruby
@@ -20,170 +22,512 @@ Or install it yourself as:
20
22
  ```bash
21
23
  $ gem install db2_query
22
24
  ```
23
-
24
- ## Initialization
25
- Execute init task at the app root
25
+ ## 2. Initialization
26
+ Execute **db2query:init** task at the app root to create database configurations and initializer file.
26
27
  ```bash
27
28
  $ rake db2query:init
29
+ create config/db2query.yml
30
+ create config/initializers/db2query.rb
28
31
  ```
29
- DB2Query will generate two required files:
30
- - `config/db2query_database.yml`
31
- - `config/initializers/db2query.rb`
32
32
 
33
- Edit these files according to the requirement.
33
+ Complete the configurations by editing the files according to your application requirement.
34
34
 
35
- Note:
35
+ ### Database Configuration
36
+ File **config/db2query.yml** consist of DSN/database name and connection pool config:
37
+ ```yml
38
+ development:
39
+ dsn: LIBDEV
40
+ idle: 5
41
+ pool: 5
42
+ timeout: 5
43
+ test:
44
+ dsn: LIBTEST
45
+ idle: 5
46
+ pool: 5
47
+ timeout: 5
48
+ production:
49
+ dsn: LIBPROD
50
+ idle: 5
51
+ pool: 5
52
+ timeout: 5
53
+ ```
36
54
 
37
- To upgrade from the previous version to v.0.2.1, please delete all the files generated by init task and do init task again (don't forget to backup your db2query_database.yml).
55
+ Key **idle** is a **client** idle maximum limit value (in minutes) to avoid the client being disconnected by the host server. Setting this value to zero will lead to an "ODBC driver Communication Link Failure. Comm rc 10054 . [CWBCO1047](https://www.ibm.com/support/pages/cwbco1047-any-function-uses-database-host-server)" error after your application idle in a certain period of time.
38
56
 
39
- In v.0.2.0, we have to `require 'db2_query'` at initializer manually.
57
+ [**Ensure**](https://github.com/yohaneslumentut/db2_query/wiki/DB2-ODBC-Connection#verify-odbc-connection) that **unixodbc** has been installed and test your connection first by using **isql** commands.
40
58
 
41
- ### Database Configuration
42
- At `db2query_database.yml` we can use two type of connection:
43
- 1. DSN connection config
44
- 2. Connection String config
45
- ```yml
46
- development:
47
- primary: # Connection String Example
48
- conn_string:
49
- driver: DB2
50
- database: SAMPLE
51
- dbalias: SAMPLE
52
- hostname: LOCALHOST
53
- currentschema: LIBTEST
54
- port: "0"
55
- protocol: IPC
56
- uid: <%= ENV["DB2EC_UID"] %>
57
- pwd: <%= ENV["DB2EC_PWD"] %>
58
- secondary: # DSN Example
59
- dsn: iseries
60
- uid: <%= ENV["ISERIES_UID"] %>
61
- pwd: <%= ENV["ISERIES_PWD"] %>
62
- ```
63
-
64
- Ensure that `unixodbc` have been installed and test your connection first by using `isql` commands.
59
+ ### Initializer File
60
+ This file is used by **Db2Query::Base** to load **field types** configurations and establish a **connection** instance.
61
+ ```ruby
62
+ # app_root/config/initializers/db2query.rb
65
63
 
66
- Example:
64
+ require "db2_query"
67
65
 
68
- Secondary database connection test
69
- ```bash
70
- $ isql -v iseries
71
- +---------------------------------------+
72
- | Connected! |
73
- | |
74
- | sql-statement |
75
- | help [tablename] |
76
- | quit |
77
- | |
78
- +---------------------------------------+
79
- SQL>
66
+ Db2Query::Base.initiation do |base|
67
+ base.set_field_types # or base.set_field_types(CUSTOM_FIELD_TYPES) if you have CUSTOM TYPES
68
+ base.establish_connection
69
+ end
70
+ ```
71
+
72
+ ### Custom Field Type
73
+ **FieldTypes** are classes that are used by **Db2Query** to format the data before sending it to the database by using `serialize` method and `deserialize` the returned query result data by converting the **query result** before consumed by your **Rails application**. Both `serialize` and `deserialize` operations are only applied when you provide **QueryDefinitions** on your query.
74
+
75
+ By default, there are ten field types that can be used in your [query definitions](#32-querydefinitions) :
76
+
77
+ ```ruby
78
+ DEFAULT_FIELD_TYPES = {
79
+ binary: Db2Query::Type::Binary,
80
+ boolean: Db2Query::Type::Boolean,
81
+ string: Db2Query::Type::String,
82
+ varchar: Db2Query::Type::String,
83
+ longvarchar: Db2Query::Type::String,
84
+ decimal: Db2Query::Type::Decimal,
85
+ integer: Db2Query::Type::Integer,
86
+ date: Db2Query::Type::Date,
87
+ time: Db2Query::Type::Time,
88
+ timestamp: Db2Query::Type::Timestamp
89
+ }
90
+ ```
91
+ You can use your own Field type class by extending **Db2Query::Type::Value** class. For example:
92
+ ```ruby
93
+ class CustomTypeClass < Db2Query::Type::Value
94
+ # Method to convert data from ruby type value into data that is understood by Db2
95
+ def serialize(value)
96
+ # Your logic
97
+ end
98
+
99
+ # Method to convert Db2 database output data type that is recognized by your rails app
100
+ def deserialize(value)
101
+ # Your logic
102
+ end
103
+ end
104
+ ```
105
+ Then put the classes into a field types hash constant and load it into the **Db2Query::Base** by using **set_field_types** method in the initializer file.
106
+ ```ruby
107
+ # app_root/config/initializers/db2query.rb
108
+
109
+ require "db2_query"
110
+
111
+ CUSTOM_FIELD_TYPES = {
112
+ binary: CustomBinaryTypeClass
113
+ integer: CustomIntegerTypeClass
114
+ string: CustomStringTypeClass
115
+ ...
116
+ }
117
+
118
+ Db2Query::Base.initiation do |base|
119
+ base.set_field_types(CUSTOM_FIELD_TYPES)
120
+ base.establish_connection
121
+ end
122
+
123
+ ```
124
+
125
+ ## 3. Usage
126
+
127
+ Once you completely do the [**Installation**](#1-installation) & [**Initialization**](#2-initialization) steps, basically you has been ready to use **Db2Query::Base**. There are three additional rules that help **Db2Query** run properly: **SQL Convention**, **Field Type Convention**, and **Argument Key Convention**.
128
+
129
+ **SQL Convention**:
130
+ > Dollar symbol **$** is used as the prefix of all column names **in the WHERE clause** of provided **Parameterized Query** SQL string. It is used as a pointer in the query arguments key and value binding process. We have to provide it manually in the SQL string of each **Parameterized Query**. Here, **Parameterized Query** is used to minimize SQL injection risks.
131
+
132
+ ```ruby
133
+ # SQL Convention Examples
134
+ # Example of Parameterized Query SQL usage
135
+
136
+ Db2Query::Base.query("SELECT * FROM USERS WHERE $email = ?", "my_account@email.com")
137
+
138
+ # Example of Normal SQL usage
139
+
140
+ Db2Query::Base.query("SELECT * FROM USERS WHERE email = 'my_account@email.com'")
141
+ ```
142
+
143
+ **Field Type Convention**:
144
+ > Query definition's **field_name** written in **query_definition** block must be in downcased format.
145
+
146
+ ```ruby
147
+ # Field Type Convention Example
148
+
149
+ module Definitions
150
+ class UsersQueryDefinitions < Db2Query::Definitions
151
+ def describe
152
+ query_definition :all do |c|
153
+ c.id :integer
154
+ c.first_name :varchar
155
+ c.last_name :varchar
156
+ c.email :varchar
157
+ end
158
+ end
159
+ end
160
+ end
80
161
  ```
81
162
 
82
- ## Usage
83
- Note: Version 0.1.0 use `Db2Query` namespace. Please use `DB2Query` in versions greater than it.
163
+ **Argument Key Convention**:
164
+ > The letter case of a **Named Argument** key that passed into a query, has to follow its parameter letter case format that is written in the SQL. The argument key is case-sensitive. If the parameter in your SQL is written in downcase format, then your argument key has to be in downcase format too, and vice versa.
84
165
 
85
- ### Basic Usage
86
- Create query class that inherit from `DB2Query::Base` in `app/queries` folder
87
166
  ```ruby
88
- class User < DB2Query::Base
167
+ # Argument Key Convention Example
168
+
169
+ class MyQuery < Db2Query::Base
170
+ ...
89
171
  query :find_by, <<-SQL
90
- SELECT * FROM LIBTEST.USERS WHERE id = ?
172
+ SELECT * FROM USERS WHERE $id = ?
91
173
  SQL
174
+ ...
92
175
  end
93
176
 
94
- class User < DB2query::Base
95
- query :id_greater_than, -> id {
96
- exec_query({}, "SELECT * FROM LIBTEST.USERS WHERE id > ?", [id])
97
- }
177
+ MyQuery.find_user_by_id id: 10000
98
178
 
99
- query :insert_record, -> *args {
100
- execute(
101
- "INSERT INTO users (id, first_name, last_name, email) VALUES (?, ?, ?, ?)", args
102
- )
103
- }
179
+ ```
180
+
181
+
182
+ ### 3.1 Basic Usage
183
+
184
+ #### Base Class Query Methods
185
+
186
+ ##### #query(sql, args)
187
+ A raw query to perform a `connection.run(sql, args)` operation and returns an array of hashes representing each row record being executed.
188
+ ```ruby
189
+ Db2Query::Base.query("SELECT * FROM USERS WHERE $id < ?", 10003)
190
+ => [{:id=>10000, :first_name=>"Taisha", :last_name=>"Kutch", :email=>"willie.lesch@toy.org"}, {:id=>10001, :first_name=>"Setsuko", :last_name=>"Kutch", :email=>"thelma@purdy.co"}, {:id=>10002, :first_name=>"Trina", :last_name=>"Mayer", :email=>"dorsey_upton@flatley-gulgowski.name"}]
191
+ ```
192
+
193
+ ##### #query_rows(sql)
194
+ Execute the `SELECT Statement SQL` and returns collections of arrays consisting of row values.
195
+ ```ruby
196
+ Db2Query::Base.query_rows("SELECT * FROM USERS WHERE id < 10003")
197
+ => [[10000, "Taisha", "Kutch", "willie.lesch@toy.org"], [10001, "Setsuko", "Kutch", "thelma@purdy.co"], [10002, "Trina", "Mayer", "dorsey_upton@flatley-gulgowski.name"]]
198
+ ```
199
+
200
+ ##### #query_value(sql)
201
+ Execute the `SELECT Statement SQL` and returns the first value of the query results first row.
202
+ ```ruby
203
+ Db2Query::Base.query_value("SELECT * FROM USERS WHERE id < 10003")
204
+ => 10000
205
+ ```
206
+
207
+ ##### #query_values(sql)
208
+ Execute the `SELECT Statement SQL` and returns a collection of the first value of each query result rows.
209
+ ```ruby
210
+ Db2Query::Base.query_values("SELECT * FROM USERS WHERE id < 10003")
211
+ => [10000, 10001, 10002]
212
+ ```
213
+
214
+ ##### #execute(sql, args)
215
+ A method to execute `DUI Statement SQL` by using `connection.do(sql, args)`
216
+ ```ruby
217
+ Db2Query::Base.execute("DELETE FROM users WHERE $id = ?", 10000)
218
+ => -1
219
+ ```
220
+
221
+ ### 3.2 QueryDefinitions
222
+
223
+ #### 3.2.1 Query Field Definitions
224
+
225
+ QueryDefinitions is helpful when you need formatter methods that **serialize** the data before it being sent to the database and **deserialize** database output data before being consumed by **Rails application**. The real examples are **Binary** and **Boolean** field types.
226
+ At **Db2Query::Type::Binary**, the data `unpacked` by `serialize` method before sending to the database and do `deserialize` operation to `pack` the database returned data.
227
+ QueryDefinition can be used as **Query Schema** where the **field types** of a query are outlined. The field-type written in QueryDefinition has to follow the **Field Type Convention**.
228
+
229
+ A QueryDefinitions reside in `app_root/app/queries/definitions` directory. It is automatically created when you create your query by running `rails g query query_name` [**generator**](#33-generator) command. The QueryDefinitions class can be defined as follow:
230
+ ```ruby
231
+ # app_root/app/queries/definitions/your_query_definitions.rb
232
+ module Definitions
233
+ class YourQueryDefinitions < Db2Query::Definitions
234
+ def describe # method that is used by Db2Query to describe your query definition
235
+ query_definition :your_first_query_name do |c|
236
+ c.field_name :field_type, options
237
+ ...
238
+ end
239
+
240
+ query_definition :your_next_query_name do |c|
241
+ c.field_name :field_type, options
242
+ ...
243
+ end
244
+ end
245
+ end
104
246
  end
105
247
  ```
248
+ For Example:
249
+
250
+ ```ruby
251
+ # app_root/app/queries/definitions/users_query_definitions.rb
252
+
253
+ module Definitions
254
+ class UsersQueryDefinitions < Db2Query::Definitions
255
+ def describe
256
+ query_definition :all do |c|
257
+ c.id :integer
258
+ c.first_name :varchar
259
+ c.last_name :varchar
260
+ c.email :varchar
261
+ end
262
+
263
+ query_definition :insert do |c|
264
+ c.id :integer
265
+ c.first_name :varchar
266
+ c.last_name :varchar
267
+ c.email :varchar
268
+ end
269
+ end
270
+ end
271
+ end
106
272
 
107
- The query method must have 2 inputs:
108
- 1. Method name
109
- 2. Body (can be an SQL statement or lamda).
273
+ ```
274
+ #### 3.2.2 Query Argument Types
110
275
 
111
- The lambda is used to facilitate us in using `built-in methods` as shown at two query methods above.
276
+ Sometimes, the `query arguments` do not exist in query definitions fields. In such a case, we have to provide `query argument types` at the Query class.
112
277
 
113
- Or use a normal sql method (don't forget the `_sql` suffix)
114
278
  ```ruby
115
- class User < DB2Query::Base
116
- def find_by_sql
117
- "SELECT * FROM LIBTEST.USERS WHERE id = ?"
279
+ module NameSpace
280
+ class QueryName < Db2Query::Base
281
+ arguments :user_by_email, { email: :string, trim: true }
282
+
283
+ def user_by_email_sql
284
+ "SELECT id, first_name, last_name FROM USERS WHERE $email = ?"
285
+ end
118
286
  end
119
287
  end
120
288
  ```
121
- Check it at rails console
289
+
290
+ ### 3.3 Generator
291
+
292
+ Create query class by using `rails g query NAME` commands. For example:
293
+
122
294
  ```bash
123
- User.find_by 10000
124
- SQL Load (3.28ms) SELECT * FROM LIBTEST.USERS WHERE id = ? [[nil, 10000]]
125
- => #<DB2Query::Result @records=[#<Record id: 10000, first_name: "Strange", last_name: "Stephen", email: "strange@marvel.universe.com">]>
295
+ $ rails g query NameSpace::Name --defines=first_query --queries=next_query --lambdas=last_query
296
+ create app/queries/name_space/name_query.rb
297
+ create app/queries/definitions/name_space/name_query_definitions.rb
298
+ create test/queries/name_space/name_query_test.rb
126
299
  ```
127
- Or using keywords argument if the sql use `=` operator, e.g `first_name = ?`
128
- ```bash
129
- User.by_name first_name: "Strange", last_name: "Stephen"
130
- SQL Load (3.28ms) SELECT * FROM LIBTEST.USERS WHERE first_name = ? AND last_name = ? [["first_name", Strange], ["last_name", Stephen]]
131
- => #<DB2Query::Result @records=[#<Record id: 10000, first_name: "Strange", last_name: "Stephen", email: "strange@marvel.universe.com">]>
300
+ This will create `app/queries/name_space/name_query.rb` file in `app/queries` directory.
301
+
302
+ ```ruby
303
+ module NameSpace
304
+ class Name < Db2Query::Base
305
+ def first_query_sql
306
+
307
+ end
308
+
309
+ query :next_query, <<-SQL
310
+
311
+ SQL
312
+
313
+ query :last_query, -> {
314
+
315
+ }
316
+ end
317
+ end
132
318
  ```
133
319
 
134
- ### Formatter
135
- 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`
136
320
  ```ruby
137
- require "db2_query/formatter"
321
+ # app_root/app/queries/definitions/name_space/name_query.rb
322
+
323
+ module Definitions
324
+ module NameSpace
325
+ class NameQueryDefinition < Db2Query::Definitions
326
+ def describe # method that is used by Db2Query to describe your query definition
327
+ query_definition :first_query do |c|
328
+
329
+ end
330
+
331
+ query_definition :next_query do |c|
332
+
333
+ end
334
+
335
+ query_definition :last_query do |c|
138
336
 
139
- # create a formatter class
140
- class FirstNameFormatter < DB2Query::AbstractFormatter
141
- def format(value)
142
- "Dr." + value
337
+ end
338
+ end
339
+ end
143
340
  end
144
341
  end
342
+ ```
343
+
344
+ Please run `rails g query --help` to get more information on how to use the file generator.
345
+
346
+ ### 3.4 Queries Methods
347
+
348
+ In a **Query** class that extends **Db2Query::Base** class, there are 3 ways of query implementation:
349
+
350
+ ```ruby
351
+ class MyQuery < Db2Query::Base
352
+ # 1. Plain Query (--defines)
353
+ def query_name_sql
354
+ "YOUR AMAZING SQL STATEMENT STRING"
355
+ end
356
+
357
+ # 2. String Query (--queries)
358
+ query :query_name, <<-SQL
359
+ YOUR AMAZING SQL STATEMENT
360
+ SQL
361
+
362
+ # 3. Lambda Query (--lambdas)
363
+ query :query_name, -> args {
364
+ # implement fetch, fetch_list, and exec_query
365
+ fetch("YOUR AMAZING SQL", args)
366
+ }
367
+ end
368
+ ```
369
+
370
+ #### 3.4.1 Plain Query (--defines)
371
+ Query implementation that uses the plain method. The method name must have a `_sql` suffix and return SQL statement string.
372
+
373
+ Example:
374
+ ```ruby
375
+ class MyQuery < Db2Query::Base
376
+ def all_users_sql
377
+ "SELECT * FROM USERS"
378
+ end
145
379
 
146
- # register the formatter class
147
- DB2Query::Formatter.registration do |format|
148
- format.register(:first_name_formatter, FirstNameFormatter)
380
+ def find_user_by_id_sql
381
+ "SELECT * FROM USERS WHERE $id = ?"
382
+ end
149
383
  end
150
384
  ```
151
- Use it at query class
385
+
386
+ #### 3.4.2 String Query (--queries)
387
+ Query implementation that uses the built-in `query` method. The input arguments consist of `query_name` symbol and SQL statement
388
+
389
+ Example:
152
390
  ```ruby
153
- class Doctor < User
154
- attributes :first_name, :first_name_formatter
391
+ class MyQuery < Db2Query::Base
392
+ query :all_users, <<-SQL
393
+ SELECT * FROM USERS
394
+ SQL
395
+
396
+ query :find_user_by_id, <<-SQL
397
+ SELECT * FROM USERS WHERE $id = ?
398
+ SQL
155
399
  end
156
400
  ```
157
- Check it at rails console
401
+
402
+ #### 3.4.3 Lambda Query (--lambdas)
403
+ Query implementation that uses the built-in `query` method. The input arguments consist of the `query_name` symbol and a lambda function. We have to pass `args` as the arguments of a lambda function. Do not change the `args` with let's say `-> id, email { ... }`. Just leave it written as `args`. The `args` is used by `Db2Query::Base` to store `query_name` and the other `arg` inputs.
404
+
405
+ Example:
406
+ ```ruby
407
+ class MyQuery < Db2Query::Base
408
+ query :all_users, -> args {
409
+ fetch("SELECT * FROM USERS", args)
410
+ }
411
+
412
+ query :find_user_by_id, -> args {
413
+ fetch("SELECT * FROM USERS WHERE $id = ?", args)
414
+ }
415
+ end
416
+ ```
417
+
418
+ Then you can call all three example with the same methods:
419
+
158
420
  ```bash
159
- Doctor.find_by id: 10000
160
- SQL Load (3.28ms) SELECT * FROM LIBTEST.USERS WHERE id = ? [["id", 10000]]
161
- => #<DB2Query::Result @records=[#<Record id: 10000, first_name: "Dr.Strange", last_name: "Stephen", email: "strange@marvel.universe.com">]>
421
+ irb(main):001:0> MyQuery.all_users
422
+ SQL (2.7ms) SELECT * FROM USERS
423
+ => #<Db2Query::Result [#<Record id: 10000, first_name: Yohanes, ...]>
424
+
425
+ irb(main):001:0> MyQuery.find_user_by_id 10000
426
+ SQL (3.0ms) SELECT * FROM USERS WHERE id = ? [["id", 10000]]
427
+ => #<Db2Query::Result [#<Record id: 10004, first_name: Yohanes, ...]>
162
428
  ```
429
+ If you pass a key-value argument into query, the key has to follow **Argument Key Convention**
163
430
 
164
- ### Available Result Ogject methods
165
- `DB2Query::Result` inherit all `ActiveRecord::Result` methods with additional custom methods:
166
- 1. `records` to convert query result into array of Record objects.
167
- 2. `to_h` to convert query result into hash with symbolized keys.
431
+ ```bash
432
+ irb(main):001:0> MyQuery.find_user_by_id(id: 10000)
433
+ SQL (3.0ms) SELECT * FROM USERS WHERE id = ? [["id", 10000]]
434
+ => #<Db2Query::Result [#<Record id: 10004, first_name: Yohanes, ...]>
435
+
436
+ ```
168
437
 
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.
438
+ And use it at your application
439
+ ```ruby
440
+ users = MyQuery.all
441
+ user_records = users.records
442
+ user_1 = user_records.first
443
+ user_1.id # => 10000
444
+ user_1.first_name # => "Yohanes"
445
+ user_1.last_name # => "Lumentut"
446
+ user_1.email # => "yohanes@github.com"
447
+
448
+ user_1 == users.record # => true
449
+
450
+ user = MyQuery.find_user_by_id id: 10000
451
+ user.id # => 10000
452
+ user.first_name # => "Yohanes"
453
+ user.last_name # => "Lumentut"
454
+ user.email # => "yohanes@github.com"
455
+ ```
177
456
 
178
- ### ActiveRecord Combination
457
+ ### 3.5 SQL extension (`@extension`)
458
+ For the sake of reusable SQL string, we can reuse the most commonly used SQL part by implementing `sql_with_extension` methods with an SQL string argument contain `@extension` pointer at SQL statement.
459
+
460
+ ```ruby
461
+ class MyQuery < Db2Query::Base
462
+ # reusable SQL
463
+ _SQL = -> extension {
464
+ sql_with_extension("SELECT * FROM USERS WHERE @extension", extension)
465
+ }
466
+
467
+ # implementation
468
+ query :user_by_email, _SQL.("$email = ?")
469
+ end
470
+ ```
471
+ ```bash
472
+ irb(main):001:0> MyQuery.user_by_email email: "yohanes@github.com"
473
+ SQL (2.7ms) SELECT * FROM USERS email = ? [["email", "yohanes@github.com"]]
474
+ => #<Db2Query::Result [#<Record id: 10000, first_name: Yohanes, ...]>
475
+ ```
476
+ ```ruby
477
+ user = MyQuery.user_by_email "yohanes@github.com"
478
+ user.id # => 10000
479
+ user.first_name # => "Yohanes"
480
+ user.last_name # => "Lumentut"
481
+ user.email # => "yohanes@github.com"
482
+ ```
483
+ ### 3.6 List input (`@list`)
484
+ For an array consist list of inputs, we can use `fetch_list` method and `@list` pointer at the SQL statement.
485
+
486
+ ```ruby
487
+ class MyQuery < Db2Query::Base
488
+ query :user_by_ids, -> args {
489
+ fetch_list("SELECT * FROM USERS WHERE ID IN (@list)", args)
490
+ }
491
+ end
492
+ ```
493
+ ```bash
494
+ irb(main):007:0> MyQuery.user_by_ids [10000,10001,10002]
495
+ SQL (2.8ms) SELECT * FROM USERS WHERE ID IN ('10000', '10001', '10002')
496
+ => #<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">]>
497
+
498
+ ```
499
+ ```ruby
500
+ users = MyQuery.user_by_ids [10000,10001,10002]
501
+ user = users.first
502
+ user == users.record # => true
503
+
504
+ user.id # => 10000
505
+ user.first_name # => "Carol"
506
+ user.last_name # => "Danvers"
507
+ user.email # => "captain.marvel@marvel.universe.com"
508
+ ```
509
+
510
+ ### 3.7 Formatter
511
+ For the latest version of **Db2Query**, there is no more **Db2Query::Formatter** class. We can implement our formater into **deserialize** method of our [**QueryDefinitions**](#32-querydefinitions).
512
+
513
+ If you upgrade from the previous version, you have to run **`rake db2query:init`** again to override the initializer. Please create a backup of your Formatter classes before you do this operation. Then you can implement your Formatter methods into your **QueryDefinitions**.
514
+
515
+ ## 4. Available Result Object methods
516
+ `Db2Query::Result` inherit all `ActiveRecord::Result` methods with additional custom methods:
517
+ 1. `records` to convert query result into an array of Result query's Record objects.
518
+ 2. `record` to get the first Record Object of Result query.
519
+ 3. `to_h` to convert query result into an array of hashes with symbolized keys.
520
+
521
+ ## 5. ActiveRecord Combination
522
+
523
+ Create an abstract class that inherits from `ActiveRecord::Base`. We have to implement `splat` operator correctly at the arguments to make it works.
179
524
 
180
- Create an abstract class that inherit from `ActiveRecord::Base`
181
525
  ```ruby
182
526
  class Db2Record < ActiveRecord::Base
183
527
  self.abstract_class = true
184
528
 
185
- def self.query(sql, formatter = {}, args = [])
186
- DB2Query::Base.connection.exec_query(sql, formatter, args).to_a.map(&:deep_symbolize_keys)
529
+ def self.query(sql, args)
530
+ Db2Query::Base.query(sql, *args)
187
531
  end
188
532
  end
189
533
  ```
@@ -192,32 +536,36 @@ Utilize the goodness of rails model `scope`
192
536
  ```ruby
193
537
  class User < Db2Record
194
538
  scope :by_name, -> *args {
195
- query(
196
- "SELECT * FROM LIBTEST.USERS WHERE first_name = ? AND last_name = ?", {}, args
197
- )
539
+ query("SELECT * FROM USERS WHERE $first_name = ? AND $last_name = ?", args)
198
540
  }
199
541
  end
200
542
  ```
543
+
201
544
  ```bash
202
545
  User.by_name first_name: "Strange", last_name: "Stephen"
203
- SQL Load (3.28ms) SELECT * FROM LIBTEST.USERS WHERE first_name = ? AND last_name = ? [["first_name", Strange], ["last_name", Stephen]]
546
+ SQL Load (3.28ms) SELECT * FROM USERS WHERE first_name = ? AND last_name = ? [["first_name", Strange], ["last_name", Stephen]]
204
547
  => [{:id=> 10000, :first_name=> "Strange", :last_name=> "Stephen", :email=> "strange@marvel.universe.com"}]
205
548
  ```
206
549
 
207
550
  Another example:
208
551
  ```ruby
209
552
  class User < Db2Record
210
- scope :age_gt, -> age {
211
- query("SELECT * FROM LIBTEST.USERS WHERE age > #{age}")
553
+ scope :age_gt, -> *args {
554
+ query("SELECT * FROM USERS WHERE age > ?", args)
212
555
  }
213
556
  end
214
557
  ```
215
558
 
216
559
  ```bash
217
560
  User.age_gt 500
218
- SQL Load (3.28ms) SELECT * FROM LIBTEST.USERS WHERE age > 500
561
+ SQL Load (3.28ms) SELECT * FROM USERS WHERE age > 500
219
562
  => [{:id=> 99999, :first_name=> "Ancient", :last_name=> "One", :email=> "ancientone@marvel.universe.com"}]
220
563
  ```
221
564
 
222
- ## License
223
- The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
565
+ ## 6. Examples
566
+
567
+ For complete examples please see the basic examples [here](https://github.com/yohaneslumentut/db2_query/blob/master/test/dummy/app/queries/user_query.rb).
568
+ Please see [**Db2Session**](https://github.com/yohaneslumentut/db2_session) for **REST** and **GraphQL** implementation of multi-user on the remote server.
569
+
570
+ ## 7. License
571
+ The gem is available as open-source under the terms of the [MIT License](https://opensource.org/licenses/MIT).