click_house 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +14 -0
  3. data/.rspec +3 -0
  4. data/.rubocop.yml +125 -0
  5. data/.travis.yml +16 -0
  6. data/Gemfile +8 -0
  7. data/Gemfile.lock +67 -0
  8. data/Makefile +9 -0
  9. data/README.md +413 -0
  10. data/Rakefile +8 -0
  11. data/bin/console +11 -0
  12. data/bin/setup +8 -0
  13. data/click_house.gemspec +31 -0
  14. data/doc/logo.svg +37 -0
  15. data/docker-compose.yml +21 -0
  16. data/lib/click_house.rb +53 -0
  17. data/lib/click_house/config.rb +61 -0
  18. data/lib/click_house/connection.rb +48 -0
  19. data/lib/click_house/definition.rb +8 -0
  20. data/lib/click_house/definition/column.rb +46 -0
  21. data/lib/click_house/definition/column_set.rb +95 -0
  22. data/lib/click_house/errors.rb +7 -0
  23. data/lib/click_house/extend.rb +14 -0
  24. data/lib/click_house/extend/configurable.rb +11 -0
  25. data/lib/click_house/extend/connectible.rb +15 -0
  26. data/lib/click_house/extend/connection_database.rb +37 -0
  27. data/lib/click_house/extend/connection_healthy.rb +16 -0
  28. data/lib/click_house/extend/connection_inserting.rb +13 -0
  29. data/lib/click_house/extend/connection_selective.rb +23 -0
  30. data/lib/click_house/extend/connection_table.rb +81 -0
  31. data/lib/click_house/extend/type_definition.rb +15 -0
  32. data/lib/click_house/middleware.rb +9 -0
  33. data/lib/click_house/middleware/logging.rb +61 -0
  34. data/lib/click_house/middleware/parse_csv.rb +17 -0
  35. data/lib/click_house/middleware/raise_error.rb +25 -0
  36. data/lib/click_house/response.rb +8 -0
  37. data/lib/click_house/response/factory.rb +17 -0
  38. data/lib/click_house/response/result_set.rb +79 -0
  39. data/lib/click_house/type.rb +16 -0
  40. data/lib/click_house/type/base_type.rb +15 -0
  41. data/lib/click_house/type/boolean_type.rb +18 -0
  42. data/lib/click_house/type/date_time_type.rb +15 -0
  43. data/lib/click_house/type/date_type.rb +15 -0
  44. data/lib/click_house/type/decimal_type.rb +15 -0
  45. data/lib/click_house/type/fixed_string_type.rb +15 -0
  46. data/lib/click_house/type/float_type.rb +15 -0
  47. data/lib/click_house/type/integer_type.rb +15 -0
  48. data/lib/click_house/type/nullable_type.rb +21 -0
  49. data/lib/click_house/type/undefined_type.rb +15 -0
  50. data/lib/click_house/util.rb +8 -0
  51. data/lib/click_house/util/pretty.rb +30 -0
  52. data/lib/click_house/util/statement.rb +21 -0
  53. data/lib/click_house/version.rb +5 -0
  54. data/log/.keep +0 -0
  55. data/tmp/.keep +1 -0
  56. metadata +208 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 05f0f9426500a803ec5d2d37b1a2a06d2eb2d3e4b934eac19a434acc5086dc5c
4
+ data.tar.gz: c079c080dedbcc3342bbfde789099ddacf714a16029779bddbd8352d1912f1b0
5
+ SHA512:
6
+ metadata.gz: f2f1d0d8f438b317ee475499e24f9d3ecae9f2b6a872a21695f11e0f8d744693790cd6f1caa50beab7e2c11f008952e453ca37d9282caeed18a95f54e99dff44
7
+ data.tar.gz: 17f7aff9dad97b3ad4384618b8cf8ce92f550ec9ee5b7ee524413458dce48f7eb08a69d4f78abf5862ac01acd33f1257de37403f6ce42595b152435819bc2e2f
@@ -0,0 +1,14 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /pkg/
6
+ /spec/reports/
7
+ /log/*
8
+ /tmp/*
9
+
10
+ !/log/.keep
11
+ !/tmp/.keep
12
+
13
+ # rspec failure tracking
14
+ .rspec_status
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
@@ -0,0 +1,125 @@
1
+ require:
2
+ - rubocop-performance
3
+
4
+ AllCops:
5
+ AutoCorrect: false
6
+ Exclude:
7
+ - 'click_house.gemspec'
8
+ - 'bin/*'
9
+ - 'spec/**/*'
10
+ - 'vendor/**/*'
11
+ TargetRubyVersion: 2.6.5
12
+
13
+ Bundler/OrderedGems:
14
+ Enabled: false
15
+
16
+ # ============================== Documentation ======================
17
+ Style/Documentation:
18
+ Enabled: false
19
+
20
+ # ============================== Metrics ============================
21
+ Metrics/ClassLength:
22
+ Max: 180
23
+ Metrics/BlockLength:
24
+ Enabled: true
25
+ Metrics/MethodLength:
26
+ Max: 25
27
+ Metrics/LineLength:
28
+ Max: 140
29
+ Metrics/AbcSize:
30
+ Max: 40
31
+
32
+ # ============================== Naming =============================
33
+ Naming/PredicateName:
34
+ NamePrefixBlacklist:
35
+ - is_
36
+ Naming/FileName:
37
+ Enabled: true
38
+ Exclude:
39
+ - 'Gemfile'
40
+ Naming/UncommunicativeMethodParamName:
41
+ Enabled: false
42
+ Naming/AccessorMethodName:
43
+ Enabled: false
44
+
45
+ # ============================== Layout =============================
46
+ Layout/AlignHash:
47
+ EnforcedHashRocketStyle: key
48
+ EnforcedColonStyle: key
49
+ Layout/AlignParameters:
50
+ EnforcedStyle: with_fixed_indentation
51
+ Layout/CaseIndentation:
52
+ EnforcedStyle: case
53
+ IndentOneStep: false
54
+ Layout/MultilineMethodCallIndentation:
55
+ Enabled: true
56
+ EnforcedStyle: indented
57
+ Layout/SpaceBeforeBlockBraces:
58
+ EnforcedStyle: space
59
+ EnforcedStyleForEmptyBraces: space
60
+ Layout/EmptyLines:
61
+ Enabled: true
62
+ Layout/EmptyLineAfterMagicComment:
63
+ Enabled: false
64
+ Layout/EmptyLinesAroundBlockBody:
65
+ Enabled: true
66
+ Layout/EndAlignment:
67
+ EnforcedStyleAlignWith: variable
68
+ Layout/IndentFirstHashElement:
69
+ EnforcedStyle: consistent
70
+ Layout/IndentHeredoc:
71
+ Enabled: false
72
+ Layout/RescueEnsureAlignment:
73
+ Enabled: false
74
+
75
+ # ============================== Style ==============================
76
+ Style/RescueModifier:
77
+ Enabled: true
78
+ Style/PercentLiteralDelimiters:
79
+ PreferredDelimiters:
80
+ default: '[]'
81
+ '%i': '[]'
82
+ '%w': '[]'
83
+ Exclude:
84
+ - 'config/routes.rb'
85
+ Style/StringLiterals:
86
+ Enabled: true
87
+ Style/AsciiComments:
88
+ Enabled: false
89
+ Style/Copyright:
90
+ Enabled: false
91
+ Style/SafeNavigation:
92
+ Enabled: false
93
+ Style/Lambda:
94
+ Enabled: false
95
+ Style/Alias:
96
+ Enabled: true
97
+ EnforcedStyle: prefer_alias_method
98
+ Style/ClassAndModuleChildren:
99
+ Enabled: true
100
+ EnforcedStyle: nested
101
+ Style/TrailingCommaInArrayLiteral:
102
+ Enabled: true
103
+ EnforcedStyleForMultiline: no_comma
104
+ Style/RescueStandardError:
105
+ Enabled: true
106
+ EnforcedStyle: explicit
107
+ Style/InverseMethods:
108
+ AutoCorrect: false
109
+ Enabled: true
110
+ Style/IfUnlessModifier:
111
+ Enabled: false
112
+ Style/SpecialGlobalVars:
113
+ Enabled: false
114
+ Style/BlockComments:
115
+ Enabled: false
116
+ Style/GuardClause:
117
+ Enabled: false
118
+ Style/TrailingCommaInHashLiteral:
119
+ Enabled: false
120
+
121
+ # ============================== Lint ==============================
122
+ Lint/DuplicateMethods:
123
+ Enabled: false
124
+ Lint/AmbiguousOperator:
125
+ Enabled: false
@@ -0,0 +1,16 @@
1
+ language: ruby
2
+
3
+ rvm:
4
+ - 2.6.5
5
+
6
+ services:
7
+ - docker
8
+
9
+ before_install:
10
+ - docker pull yandex/clickhouse-server
11
+ - docker run -d -p 127.0.0.1:8123:8123 yandex/clickhouse-server
12
+ - bundle install
13
+
14
+ script:
15
+ - bundle exec rubocop
16
+ - bundle exec rspec
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
6
+
7
+ # Specify your gem's dependencies in click_house.gemspec
8
+ gemspec
@@ -0,0 +1,67 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ click_house (1.0.0)
5
+ faraday
6
+ faraday_middleware
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ ast (2.4.0)
12
+ coderay (1.1.2)
13
+ diff-lcs (1.3)
14
+ faraday (0.17.0)
15
+ multipart-post (>= 1.2, < 3)
16
+ faraday_middleware (0.13.1)
17
+ faraday (>= 0.7.4, < 1.0)
18
+ jaro_winkler (1.5.4)
19
+ method_source (0.9.2)
20
+ multipart-post (2.1.1)
21
+ parallel (1.18.0)
22
+ parser (2.6.5.0)
23
+ ast (~> 2.4.0)
24
+ pry (0.12.2)
25
+ coderay (~> 1.1.0)
26
+ method_source (~> 0.9.0)
27
+ rainbow (3.0.0)
28
+ rake (13.0.0)
29
+ rspec (3.9.0)
30
+ rspec-core (~> 3.9.0)
31
+ rspec-expectations (~> 3.9.0)
32
+ rspec-mocks (~> 3.9.0)
33
+ rspec-core (3.9.0)
34
+ rspec-support (~> 3.9.0)
35
+ rspec-expectations (3.9.0)
36
+ diff-lcs (>= 1.2.0, < 2.0)
37
+ rspec-support (~> 3.9.0)
38
+ rspec-mocks (3.9.0)
39
+ diff-lcs (>= 1.2.0, < 2.0)
40
+ rspec-support (~> 3.9.0)
41
+ rspec-support (3.9.0)
42
+ rubocop (0.76.0)
43
+ jaro_winkler (~> 1.5.1)
44
+ parallel (~> 1.10)
45
+ parser (>= 2.6)
46
+ rainbow (>= 2.2.2, < 4.0)
47
+ ruby-progressbar (~> 1.7)
48
+ unicode-display_width (>= 1.4.0, < 1.7)
49
+ rubocop-performance (1.5.0)
50
+ rubocop (>= 0.71.0)
51
+ ruby-progressbar (1.10.1)
52
+ unicode-display_width (1.6.0)
53
+
54
+ PLATFORMS
55
+ ruby
56
+
57
+ DEPENDENCIES
58
+ bundler (~> 1.17)
59
+ click_house!
60
+ pry
61
+ rake (~> 13.0)
62
+ rspec (~> 3.0)
63
+ rubocop
64
+ rubocop-performance
65
+
66
+ BUNDLED WITH
67
+ 1.17.3
@@ -0,0 +1,9 @@
1
+ .PHONY: help
2
+
3
+ help:
4
+ @echo 'Available targets:'
5
+ @echo ' make dockerize OR make ARGS="--build" dockerize'
6
+
7
+ dockerize:
8
+ rm -f tmp/pids/server.pid tmp/sockets/puma.sock
9
+ docker-compose up ${ARGS}
@@ -0,0 +1,413 @@
1
+ ![](./doc/logo.svg?sanitize=true)
2
+
3
+ # ClickHouse Ruby driver
4
+
5
+ [![pipeline status](https://travis-ci.com/shlima/click_house.svg?branch=master)](https://travis-ci.com/shlima/click_house)
6
+
7
+ ```bash
8
+ gem install click_house
9
+ ```
10
+
11
+ A modern Ruby database driver for ClickHouse. [ClickHouse](https://clickhouse.yandex)
12
+ is a high-performance column-oriented database management system developed by
13
+ [Yandex](https://yandex.com/company) which operates Russia's most popular search engine.
14
+
15
+ > This development was inspired by currently [unmaintainable alternative](https://github.com/archan937/clickhouse)
16
+ > but rewritten and well tested. Requires modern Ruby (>= 2.6) and Yandex ClickHouse
17
+
18
+ ### Why use the HTTP interface and not the TCP interface?
19
+
20
+ Well, the developers of ClickHouse themselves [discourage](https://github.com/yandex/ClickHouse/issues/45#issuecomment-231194134) using the TCP interface.
21
+
22
+ > TCP transport is more specific, we don't want to expose details.
23
+ Despite we have full compatibility of protocol of different versions of client and server, we want to keep the ability to "break" it for very old clients. And that protocol is not too clean to make a specification.
24
+
25
+ # TOC
26
+
27
+ * [Configuration](#configuration)
28
+ * [Usage](#usage)
29
+ * [Queries](#queries)
30
+ * [Insert](#insert)
31
+ * [Create a table](#create-a-table)
32
+ * [Type casting](#type-casting)
33
+ * [Using with a connection pool](#using-with-a-connection-pool)
34
+ * [Using with Rails](#using-with-rails)
35
+
36
+ ## Configuration
37
+
38
+ ```ruby
39
+ ClickHouse.config do |config|
40
+ config.logger = Logger.new(STDOUT)
41
+ config.adapter = :net_http
42
+ config.database = 'metrics'
43
+ config.url = 'http://localhost:8123'
44
+ config.timeout = 60
45
+ config.open_timeout = 3
46
+ config.ssl_verify = false
47
+
48
+ # or provide connection options separately
49
+ config.scheme = 'http'
50
+ config.host = 'localhost'
51
+ config.port = 'port'
52
+
53
+ # if you use HTTP basic Auth
54
+ config.username = 'user'
55
+ config.password = 'password'
56
+ end
57
+ ```
58
+
59
+ Alternative, you can assign configuration parameters via a hash
60
+
61
+ ```ruby
62
+ ClickHouse.config.assign(logger: Logger.new(STDOUT))
63
+ ```
64
+
65
+ Now you are able to communicate with ClickHouse:
66
+
67
+ ```ruby
68
+ ClickHouse.connection.ping #=> true
69
+ ```
70
+ You can easily build a new raw connection
71
+
72
+ ```ruby
73
+ @connection = ClickHouse::Connection.new(ClickHouse::Config.new(logger: Rails.logger))
74
+ @connection.ping
75
+ ```
76
+
77
+ ## Usage
78
+
79
+ ```ruby
80
+ ClickHouse.connection.ping #=> true
81
+ ClickHouse.connection.replicas_status #=> true
82
+
83
+ ClickHouse.connection.databases #=> ["default", "system"]
84
+ ClickHouse.connection.create_database('metrics', if_not_exists: true, engine: nil, cluster: nil)
85
+ ClickHouse.connection.drop_database('metrics', if_exists: true, cluster: nil)
86
+
87
+ ClickHouse.connection.tables #=> ["visits"]
88
+ ClickHouse.connection.describe_table('visits') #=> [{"name"=>"id", "type"=>"FixedString(16)", "default_type"=>""}]
89
+ ClickHouse.connection.table_exists?('visits', temporary: nil) #=> true
90
+ ClickHouse.connection.drop_table('visits', if_exists: true, temporary: nil, cluster: nil)
91
+ ClickHouse.connection.create_table(*) # see <Create a table> section
92
+ ```
93
+
94
+ ## Queries
95
+ ### Select All
96
+
97
+ Select all type-casted result set
98
+
99
+ ```ruby
100
+ @result = ClickHouse.connection.select_all('SELECT * FROM visits')
101
+
102
+ # all enumerable methods are delegated like #each, #map, #select etc
103
+ @result.to_a #=> [{"date"=>#<Date: 2000-01-01>, "id"=>1}]
104
+
105
+ # you can access raw data
106
+ @result.meta #=> [{"name"=>"date", "type"=>"Date"}, {"name"=>"id", "type"=>"UInt32"}]
107
+ @result.data #=> [{"date"=>"2000-01-01", "id"=>1}, {"date"=>"2000-01-02", "id"=>2}]
108
+ @result.statistics #=> {"elapsed"=>0.0002271, "rows_read"=>2, "bytes_read"=>12}
109
+ ```
110
+
111
+ ### Select Value
112
+
113
+ Select value returns exactly one type-casted value
114
+
115
+ ```ruby
116
+ ClickHouse.connection.select_value('SELECT COUNT(*) from visits') #=> 0
117
+ ClickHouse.connection.select_value("SELECT toDate('2019-01-01')") #=> #<Date: 2019-01-01>
118
+ ClickHouse.connection.select_value("SELECT toDateOrZero(NULL)") #=> nil
119
+ ```
120
+
121
+ ### Select One
122
+
123
+ Returns a record hash with the column names as keys and column values as values.
124
+
125
+ ```ruby
126
+ ClickHouse.connection.select_one('SELECT date, SUM(id) AS sum FROM visits GROUP BY date')
127
+ #=> {"date"=>#<Date: 2000-01-01>, "sum"=>1}
128
+ ```
129
+
130
+ ### Execute Raw SQL
131
+
132
+ By default, gem provides parser for `JSON` and `CSV` response formats. Type conversion
133
+ available for the `JSON`.
134
+
135
+ ```ruby
136
+ # format not specified
137
+ response = ClickHouse.connection.execute <<~SQL
138
+ SELECT count(*) AS counter FROM rspec
139
+ SQL
140
+
141
+ response.body #=> "2\n"
142
+
143
+ # JSON
144
+ response = ClickHouse.connection.execute <<~SQL
145
+ SELECT count(*) AS counter FROM rspec FORMAT JSON
146
+ SQL
147
+
148
+ response.body #=> {"meta"=>[{"name"=>"counter", "type"=>"UInt64"}], "data"=>[{"counter"=>"2"}], "rows"=>1, "statistics"=>{"elapsed"=>0.0002412, "rows_read"=>2, "bytes_read"=>4}}
149
+
150
+ # CSV
151
+ response = ClickHouse.connection.execute <<~SQL
152
+ SELECT count(*) AS counter FROM rspec FORMAT CSV
153
+ SQL
154
+
155
+ response.body #=> [["2"]]
156
+
157
+ # You may use any format supported by ClickHouse
158
+ response = ClickHouse.connection.execute <<~SQL
159
+ SELECT count(*) AS counter FROM rspec FORMAT RowBinary
160
+ SQL
161
+
162
+ response.body #=> "\u0002\u0000\u0000\u0000\u0000\u0000\u0000\u0000"
163
+ ```
164
+
165
+ ## Insert
166
+
167
+ ```ruby
168
+ ClickHouse.connection.insert('table', columns: %i[id name]) do |buffer|
169
+ buffer << [1, 'Mercury']
170
+ buffer << [2, 'Venus']
171
+ end
172
+
173
+ ClickHouse.connection.insert('table', columns: %i[id name], values: [[1, 'Mercury'], [2, 'Venus']])
174
+ #=> true
175
+ ```
176
+
177
+ ## Create a table
178
+ ### Create table using DSL
179
+
180
+ ```ruby
181
+ ClickHouse.connection.create_table('visits', if_not_exists: true, engine: 'MergeTree(date, (year, date), 8192)') do |t|
182
+ t.FixedString :id, 16
183
+ t.UInt16 :year
184
+ t.Date :date
185
+ t.DateTime :time, 'UTC'
186
+ t.Decimal :money, 5, 4
187
+ t.String :event
188
+ t.UInt32 :user_id
189
+ t.UInt32 :Float32
190
+ end
191
+ ```
192
+
193
+ ### Create nullable columns
194
+
195
+ ```ruby
196
+ ClickHouse.connection.create_table('visits', engine: 'TinyLog') do |t|
197
+ t.UInt16 :id, 16, nullable: true
198
+ end
199
+ ```
200
+
201
+ ### Set column options
202
+
203
+ ```ruby
204
+ ClickHouse.connection.create_table('visits', engine: 'MergeTree(date, (year, date), 8192)') do |t|
205
+ t.UInt16 :year
206
+ t.Date :date
207
+ t.UInt16 :id, 16, default: 0, ttl: 'date + INTERVAL 1 DAY'
208
+ end
209
+ ```
210
+
211
+ ### Define column with custom SQL
212
+
213
+ ```ruby
214
+ ClickHouse.connection.create_table('visits', engine: 'TinyLog') do |t|
215
+ t << "vendor Enum('microsoft' = 1, 'apple' = 2)"
216
+ t << "tags Array(String)"
217
+ end
218
+ ```
219
+
220
+ ### Define nested structures
221
+
222
+ ```ruby
223
+ ClickHouse.connection.create_table('visits', engine: 'TinyLog') do |t|
224
+ t.UInt8 :id
225
+ t.Nested :json do |n|
226
+ n.UInt8 :cid
227
+ n.Date :created_at
228
+ n.Date :updated_at
229
+ end
230
+ end
231
+ ```
232
+
233
+ ### Set table options
234
+
235
+ ```ruby
236
+ ClickHouse.connection.create_table('visits',
237
+ order: 'year',
238
+ ttl: 'date + INTERVAL 1 DAY',
239
+ sample: 'year',
240
+ settings: 'index_granularity=8192',
241
+ primary_key: 'year',
242
+ engine: 'MergeTree') do |t|
243
+ t.UInt16 :year
244
+ t.Date :date
245
+ end
246
+ ```
247
+
248
+ ### Create table with raw SQL
249
+
250
+ ```ruby
251
+ ClickHouse.connection.execute <<~SQL
252
+ CREATE TABLE visits(int Nullable(Int8), date Nullable(Date)) ENGINE TinyLog
253
+ SQL
254
+ ```
255
+
256
+ ## Type casting
257
+
258
+ By default gem provides all necessary type casting, but you may overwrite or define
259
+ your own logic
260
+
261
+ ```ruby
262
+ class DateType
263
+ def cast(value)
264
+ Date.parse(value)
265
+ end
266
+
267
+ def serialize(value)
268
+ value.strftime('%Y-%m-%d')
269
+ end
270
+ end
271
+
272
+ ClickHouse.add_type('Date', DateType.new)
273
+ ```
274
+
275
+ Actually `serialize` function is not used for now, but you may use it manually:
276
+
277
+ ```ruby
278
+ time_type = DateTimeType.new
279
+ string_type = FixedStringType.new
280
+
281
+ ClickHouse.connection.insert('table', columns: %i[name time]) do |buffer|
282
+ buffer << [string_type.serialize('a' * 1000, 20), time.serialize(Time.current, 'Europe/Moscow')]
283
+ end
284
+ ```
285
+
286
+ If native type supports arguments, define type with `%s` argument:
287
+
288
+ ```ruby
289
+ class DateTimeType
290
+ def cast(value, time_zone)
291
+ Time.parse("#{value} #{time_zone}")
292
+ end
293
+ end
294
+
295
+ ClickHouse.add_type('DateTime(%s)', DateTimeType.new)
296
+ ```
297
+
298
+ ## Using with a connection pool
299
+
300
+ ```ruby
301
+ require 'connection_pool'
302
+
303
+ ClickHouse.connection = ConnectionPool.new(size: 2) do
304
+ ClickHouse::Connection.new(ClickHouse::Config.new(url: 'http://replica.example.com'))
305
+ end
306
+
307
+ ClickHouse.connection.with do |conn|
308
+ conn.tables
309
+ end
310
+ ```
311
+
312
+ ## Using with Rails
313
+
314
+ ```yml
315
+ # config/click_house.yml
316
+
317
+ default: &default
318
+ url: http://localhost:8123
319
+ timeout: 60
320
+ open_timeout: 3
321
+
322
+ development:
323
+ database: ecliptic_development
324
+ <<: *default
325
+
326
+ test:
327
+ database: ecliptic_test
328
+ <<: *default
329
+ ```
330
+
331
+ ```ruby
332
+ # config/initializers/click_house.rb
333
+
334
+ ClickHouse.config do |config|
335
+ config.logger = Rails.logger
336
+ config.assign(Rails.application.config_for('click_house'))
337
+ end
338
+ ```
339
+
340
+ ```ruby
341
+ # lib/tasks/click_house.rake
342
+ namespace :click_house do
343
+ task prepare: :environment do
344
+ @environments = Rails.env.development? ? %w[development test] : [Rails.env]
345
+ end
346
+
347
+ task drop: :prepare do
348
+ @environments.each do |env|
349
+ config = ClickHouse.config.clone.assign(Rails.application.config_for('click_house', env: env))
350
+ connection = ClickHouse::Connection.new(config)
351
+ connection.drop_database(config.database, if_exists: true)
352
+ end
353
+ end
354
+
355
+ task create: :prepare do
356
+ @environments.each do |env|
357
+ config = ClickHouse.config.clone.assign(Rails.application.config_for('click_house', env: env))
358
+ connection = ClickHouse::Connection.new(config)
359
+ connection.create_database(config.database, if_not_exists: true)
360
+ end
361
+ end
362
+ end
363
+ ```
364
+
365
+ Prepare the ClickHouse database:
366
+
367
+ ```bash
368
+ rake click_house:drop click_house:create
369
+ ```
370
+
371
+ If your are using SQL Database in Rails, you can manage ClickHouse migrations
372
+ using `ActiveRecord::Migration` mechanism
373
+
374
+ ```ruby
375
+ class CreateAdvertVisits < ActiveRecord::Migration[6.0]
376
+ def up
377
+ ClickHouse.connection.create_table('visits', engine: 'MergeTree(date, (account_id, advert_id), 512)') do |t|
378
+ t.UInt16 :account_id
379
+ t.UInt16 :user_id
380
+ t.Date :date
381
+ t.DateTime :time, 'UTC'
382
+ t.UInt32 :ipv4
383
+ t.UInt64 :ipv6
384
+ t.UInt32 :device_type_id
385
+ t.UInt32 :os_family_id
386
+ end
387
+ end
388
+
389
+ def down
390
+ ClickHouse.connection.drop_table('visits')
391
+ end
392
+ end
393
+ ```
394
+
395
+ if you use `ActiveRecord`, you can use the ORM query builder by using fake models
396
+
397
+ ````ruby
398
+ # FAKE MODEL FOR ClickHouse
399
+ class Visit < ApplicationRecord
400
+ scope :with_os, -> { where.not(os_family_id: nil) }
401
+ end
402
+
403
+ scope = Visit.with_os.select('COUNT(*) as counter').group(:ipv4)
404
+ ClickHouse.connection.select_all(scope.to_sql)
405
+ ````
406
+
407
+ ## Development
408
+
409
+ ```bash
410
+ make dockerize
411
+ rspec
412
+ rubocop
413
+ ```