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 +4 -4
- data/MIT-LICENSE +1 -1
- data/README.md +209 -55
- data/Rakefile +1 -17
- data/lib/db2_query.rb +10 -17
- data/lib/db2_query/base.rb +2 -78
- data/lib/db2_query/config.rb +29 -0
- data/lib/db2_query/connection.rb +108 -30
- data/lib/db2_query/core.rb +140 -0
- data/lib/db2_query/error.rb +12 -1
- data/lib/db2_query/formatter.rb +5 -23
- data/lib/db2_query/logger.rb +42 -0
- data/lib/db2_query/railtie.rb +5 -12
- data/lib/db2_query/result.rb +47 -65
- data/lib/db2_query/tasks/database.rake +38 -0
- data/lib/{tasks → db2_query/tasks}/init.rake +0 -0
- data/lib/{tasks → db2_query/tasks}/initializer.rake +10 -4
- data/lib/db2_query/version.rb +1 -1
- metadata +45 -57
- data/lib/db2_query/column.rb +0 -42
- data/lib/db2_query/connection_handling.rb +0 -56
- data/lib/db2_query/database_configurations.rb +0 -50
- data/lib/db2_query/database_statements.rb +0 -32
- data/lib/db2_query/log_subscriber.rb +0 -50
- data/lib/db2_query/odbc_connector.rb +0 -38
- data/lib/db2_query/path.rb +0 -17
- data/lib/db2_query/schema.rb +0 -113
- data/lib/db2_query/sql_validator.rb +0 -26
- data/lib/tasks/database.rake +0 -56
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: '09ecde3a8404d9aa56b416cbd3aa89efb21519166dea225cf8e3d7a54a2b4ed1'
|
4
|
+
data.tar.gz: c72fe3cc5e681d99d3dd5a6ec85863cb15b4b95e72b92e42dcb9d20fac910fbc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 20370e437967f91a7b273b21339c22ad4f868310812be5ff40676eb0ae28044def876276044aa1a0f237fec1b47e3acf6b7591e2bf1d76d53b7876ca688528ae
|
7
|
+
data.tar.gz: aa62b2ff0df74fb300963ab83c36f066b6e032d26de149ade42ed7cd79daac5579aedeae1bafd264747466a0719520b76ef8cef49e7e1e4c64a9cc84eb9f46db
|
data/MIT-LICENSE
CHANGED
data/README.md
CHANGED
@@ -1,5 +1,10 @@
|
|
1
|
-
#
|
2
|
-
|
1
|
+
# DB2Query
|
2
|
+
|
3
|
+
[](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
|
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/
|
28
|
-
- `config/initializers/db2query`
|
32
|
+
- `config/db2query.yml`
|
33
|
+
- `config/initializers/db2query.rb`
|
29
34
|
|
30
|
-
Edit
|
35
|
+
Edit those files according to the requirement.
|
31
36
|
|
32
37
|
### Database Configuration
|
33
|
-
At `
|
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
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
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 `
|
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
|
88
|
+
class User < Db2Query::Base
|
60
89
|
query :find_by, <<-SQL
|
61
|
-
SELECT * FROM LIBTEST.USERS WHERE
|
90
|
+
SELECT * FROM LIBTEST.USERS WHERE $id = ?
|
62
91
|
SQL
|
63
92
|
end
|
64
93
|
```
|
65
|
-
|
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
|
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
|
130
|
+
"SELECT * FROM LIBTEST.USERS WHERE $id = ?"
|
70
131
|
end
|
71
132
|
end
|
72
133
|
```
|
73
|
-
|
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
|
-
|
76
|
-
|
77
|
-
=> #<
|
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
|
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
|
-
"
|
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
|
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
|
-
|
109
|
-
|
110
|
-
=> #<
|
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
|
-
|
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 :
|
14
|
-
autoload :Path
|
15
|
-
autoload :Schema
|
16
|
-
autoload :DatabaseConfigurations
|
17
|
-
autoload :ODBCConnector
|
13
|
+
autoload :Config
|
18
14
|
autoload :Connection
|
19
|
-
autoload :
|
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
|
-
|
19
|
+
autoload :Error
|
28
20
|
|
29
|
-
require "db2_query/railtie"
|
21
|
+
require "db2_query/railtie" if defined?(Rails)
|
22
|
+
end
|
data/lib/db2_query/base.rb
CHANGED
@@ -2,83 +2,7 @@
|
|
2
2
|
|
3
3
|
module Db2Query
|
4
4
|
class Base
|
5
|
-
include
|
6
|
-
include
|
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
|