clickhouse-ruby 0.1.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.
Files changed (33) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +80 -0
  3. data/LICENSE +21 -0
  4. data/README.md +251 -0
  5. data/lib/clickhouse_ruby/active_record/arel_visitor.rb +468 -0
  6. data/lib/clickhouse_ruby/active_record/connection_adapter.rb +723 -0
  7. data/lib/clickhouse_ruby/active_record/railtie.rb +192 -0
  8. data/lib/clickhouse_ruby/active_record/schema_statements.rb +693 -0
  9. data/lib/clickhouse_ruby/active_record.rb +121 -0
  10. data/lib/clickhouse_ruby/client.rb +471 -0
  11. data/lib/clickhouse_ruby/configuration.rb +145 -0
  12. data/lib/clickhouse_ruby/connection.rb +328 -0
  13. data/lib/clickhouse_ruby/connection_pool.rb +301 -0
  14. data/lib/clickhouse_ruby/errors.rb +144 -0
  15. data/lib/clickhouse_ruby/result.rb +189 -0
  16. data/lib/clickhouse_ruby/types/array.rb +183 -0
  17. data/lib/clickhouse_ruby/types/base.rb +77 -0
  18. data/lib/clickhouse_ruby/types/boolean.rb +68 -0
  19. data/lib/clickhouse_ruby/types/date_time.rb +163 -0
  20. data/lib/clickhouse_ruby/types/float.rb +115 -0
  21. data/lib/clickhouse_ruby/types/integer.rb +157 -0
  22. data/lib/clickhouse_ruby/types/low_cardinality.rb +58 -0
  23. data/lib/clickhouse_ruby/types/map.rb +249 -0
  24. data/lib/clickhouse_ruby/types/nullable.rb +73 -0
  25. data/lib/clickhouse_ruby/types/parser.rb +244 -0
  26. data/lib/clickhouse_ruby/types/registry.rb +148 -0
  27. data/lib/clickhouse_ruby/types/string.rb +83 -0
  28. data/lib/clickhouse_ruby/types/tuple.rb +206 -0
  29. data/lib/clickhouse_ruby/types/uuid.rb +84 -0
  30. data/lib/clickhouse_ruby/types.rb +69 -0
  31. data/lib/clickhouse_ruby/version.rb +5 -0
  32. data/lib/clickhouse_ruby.rb +101 -0
  33. metadata +150 -0
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ClickhouseRuby
4
+ module Types
5
+ # Type handler for ClickHouse UUID type
6
+ #
7
+ # UUIDs are stored as 16-byte values but represented as strings
8
+ # in the format: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
9
+ #
10
+ class UUID < Base
11
+ # UUID regex pattern
12
+ UUID_PATTERN = /\A[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\z/i
13
+
14
+ # Converts a Ruby value to a UUID string
15
+ #
16
+ # @param value [Object] the value to convert
17
+ # @return [String, nil] the UUID string
18
+ # @raise [TypeCastError] if the value is not a valid UUID
19
+ def cast(value)
20
+ return nil if value.nil?
21
+
22
+ str = normalize_uuid(value)
23
+ validate_uuid!(str, value)
24
+ str
25
+ end
26
+
27
+ # Converts a value from ClickHouse to a UUID string
28
+ #
29
+ # @param value [Object] the value from ClickHouse
30
+ # @return [String, nil] the UUID string
31
+ def deserialize(value)
32
+ return nil if value.nil?
33
+
34
+ normalize_uuid(value)
35
+ end
36
+
37
+ # Converts a UUID to SQL literal
38
+ #
39
+ # @param value [String, nil] the UUID value
40
+ # @return [String] the SQL literal
41
+ def serialize(value)
42
+ return 'NULL' if value.nil?
43
+
44
+ "'#{normalize_uuid(value)}'"
45
+ end
46
+
47
+ private
48
+
49
+ # Normalizes a UUID value to the standard format
50
+ #
51
+ # @param value [Object] the value to normalize
52
+ # @return [String] the normalized UUID
53
+ def normalize_uuid(value)
54
+ str = value.to_s.strip.downcase
55
+
56
+ # Remove braces if present
57
+ str = str.gsub(/[{}]/, '')
58
+
59
+ # If no hyphens, add them
60
+ if str.length == 32 && !str.include?('-')
61
+ str = "#{str[0..7]}-#{str[8..11]}-#{str[12..15]}-#{str[16..19]}-#{str[20..31]}"
62
+ end
63
+
64
+ str
65
+ end
66
+
67
+ # Validates that a string is a valid UUID
68
+ #
69
+ # @param str [String] the normalized UUID string
70
+ # @param original [Object] the original value (for error messages)
71
+ # @raise [TypeCastError] if invalid
72
+ def validate_uuid!(str, original)
73
+ return if str.match?(UUID_PATTERN)
74
+
75
+ raise TypeCastError.new(
76
+ "Invalid UUID format: '#{original}'",
77
+ from_type: original.class.name,
78
+ to_type: name,
79
+ value: original
80
+ )
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Load base class first, then parser, then specific types, then registry last
4
+ require_relative 'types/base'
5
+ require_relative 'types/parser'
6
+ require_relative 'types/integer'
7
+ require_relative 'types/float'
8
+ require_relative 'types/string'
9
+ require_relative 'types/date_time'
10
+ require_relative 'types/uuid'
11
+ require_relative 'types/boolean'
12
+ require_relative 'types/array'
13
+ require_relative 'types/map'
14
+ require_relative 'types/tuple'
15
+ require_relative 'types/nullable'
16
+ require_relative 'types/low_cardinality'
17
+ require_relative 'types/registry'
18
+
19
+ module ClickhouseRuby
20
+ # Type system for mapping between ClickHouse and Ruby types
21
+ #
22
+ # Key features:
23
+ # - AST-based type parser (handles nested types correctly)
24
+ # - Bidirectional conversion (Ruby ↔ ClickHouse)
25
+ # - Proper handling of complex types (Array, Map, Tuple, Nullable)
26
+ #
27
+ # @example Parse a complex type
28
+ # parser = ClickhouseRuby::Types::Parser.new
29
+ # ast = parser.parse('Array(Tuple(String, UInt64))')
30
+ # # => { type: 'Array', args: [{ type: 'Tuple', args: [...] }] }
31
+ #
32
+ # @example Convert values
33
+ # type = ClickhouseRuby::Types.lookup('UInt64')
34
+ # type.cast(42) # => 42
35
+ # type.serialize(42) # => "42"
36
+ # type.deserialize("42") # => 42
37
+ #
38
+ module Types
39
+ class << self
40
+ # Returns the global type registry
41
+ #
42
+ # @return [Registry] the type registry
43
+ def registry
44
+ @registry ||= Registry.new.tap(&:register_defaults)
45
+ end
46
+
47
+ # Looks up a type by its ClickHouse type string
48
+ #
49
+ # @param type_string [String] the ClickHouse type (e.g., 'Array(String)')
50
+ # @return [Base] the type instance
51
+ def lookup(type_string)
52
+ registry.lookup(type_string)
53
+ end
54
+
55
+ # Parses a ClickHouse type string into an AST
56
+ #
57
+ # @param type_string [String] the ClickHouse type string
58
+ # @return [Hash] the parsed AST
59
+ def parse(type_string)
60
+ Parser.new.parse(type_string)
61
+ end
62
+
63
+ # Resets the registry (useful for testing)
64
+ def reset!
65
+ @registry = nil
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ClickhouseRuby
4
+ VERSION = '0.1.0'
5
+ end
@@ -0,0 +1,101 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'clickhouse_ruby/version'
4
+ require_relative 'clickhouse_ruby/errors'
5
+ require_relative 'clickhouse_ruby/configuration'
6
+ require_relative 'clickhouse_ruby/types'
7
+ require_relative 'clickhouse_ruby/result'
8
+ require_relative 'clickhouse_ruby/client'
9
+ require_relative 'clickhouse_ruby/connection'
10
+ require_relative 'clickhouse_ruby/connection_pool'
11
+
12
+ # ClickhouseRuby - Ruby/ActiveRecord integration for ClickHouse
13
+ #
14
+ # A robust, reliable ClickHouse client for Ruby that prioritizes:
15
+ # - No silent failures (proper error handling)
16
+ # - Correct type handling (AST-based parser for complex types)
17
+ # - Performance (bulk operations, connection pooling)
18
+ # - Security (SSL verification enabled by default)
19
+ #
20
+ # @example Basic usage
21
+ # ClickhouseRuby.configure do |config|
22
+ # config.host = 'localhost'
23
+ # config.port = 8123
24
+ # config.database = 'analytics'
25
+ # end
26
+ #
27
+ # client = ClickhouseRuby::Client.new
28
+ # result = client.execute('SELECT * FROM events LIMIT 10')
29
+ # result.each { |row| puts row['name'] }
30
+ #
31
+ # @example Bulk insert
32
+ # client.insert('events', [
33
+ # { id: 1, name: 'click', timestamp: Time.now },
34
+ # { id: 2, name: 'view', timestamp: Time.now }
35
+ # ])
36
+ #
37
+ module ClickhouseRuby
38
+ class << self
39
+ # Returns the global configuration instance
40
+ #
41
+ # @return [Configuration] the configuration object
42
+ def configuration
43
+ @configuration ||= Configuration.new
44
+ end
45
+
46
+ # Allows configuration via a block
47
+ #
48
+ # @yield [Configuration] the configuration object
49
+ # @return [Configuration] the configuration object
50
+ #
51
+ # @example
52
+ # ClickhouseRuby.configure do |config|
53
+ # config.host = 'clickhouse.example.com'
54
+ # config.port = 8443
55
+ # config.ssl = true
56
+ # end
57
+ def configure
58
+ yield(configuration)
59
+ configuration
60
+ end
61
+
62
+ # Resets the configuration to defaults
63
+ # Primarily useful for testing
64
+ #
65
+ # @return [Configuration] a new configuration object
66
+ def reset_configuration!
67
+ @configuration = Configuration.new
68
+ end
69
+
70
+ # Creates a new client with the global configuration
71
+ #
72
+ # @return [Client] a new client instance
73
+ def client
74
+ Client.new(configuration)
75
+ end
76
+
77
+ # Convenience method to execute a query using global configuration
78
+ #
79
+ # @param sql [String] the SQL query to execute
80
+ # @param options [Hash] query options
81
+ # @return [Result] the query result
82
+ def execute(sql, **options)
83
+ client.execute(sql, **options)
84
+ end
85
+
86
+ # Convenience method to insert data using global configuration
87
+ #
88
+ # @param table [String] the table name
89
+ # @param rows [Array<Hash>] the rows to insert
90
+ # @param options [Hash] insert options
91
+ # @return [Result] the insert result
92
+ def insert(table, rows, **options)
93
+ client.insert(table, rows, **options)
94
+ end
95
+ end
96
+ end
97
+
98
+ # Load ActiveRecord integration if ActiveRecord is available
99
+ if defined?(ActiveRecord)
100
+ require_relative 'clickhouse_ruby/active_record'
101
+ end
metadata ADDED
@@ -0,0 +1,150 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: clickhouse-ruby
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Mohamad Kamar
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2026-01-31 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rake
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '13.0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '13.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rspec
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '3.12'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '3.12'
41
+ - !ruby/object:Gem::Dependency
42
+ name: simplecov
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 0.21.0
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 0.21.0
55
+ - !ruby/object:Gem::Dependency
56
+ name: webmock
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 3.18.0
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 3.18.0
69
+ - !ruby/object:Gem::Dependency
70
+ name: yard
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '0.9'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '0.9'
83
+ description: A lightweight Ruby client for ClickHouse with optional ActiveRecord integration.
84
+ Provides a simple interface for querying, inserting, and managing ClickHouse databases.
85
+ email:
86
+ - mohamad.kamar.dev@gmail.com
87
+ executables: []
88
+ extensions: []
89
+ extra_rdoc_files: []
90
+ files:
91
+ - CHANGELOG.md
92
+ - LICENSE
93
+ - README.md
94
+ - lib/clickhouse_ruby.rb
95
+ - lib/clickhouse_ruby/active_record.rb
96
+ - lib/clickhouse_ruby/active_record/arel_visitor.rb
97
+ - lib/clickhouse_ruby/active_record/connection_adapter.rb
98
+ - lib/clickhouse_ruby/active_record/railtie.rb
99
+ - lib/clickhouse_ruby/active_record/schema_statements.rb
100
+ - lib/clickhouse_ruby/client.rb
101
+ - lib/clickhouse_ruby/configuration.rb
102
+ - lib/clickhouse_ruby/connection.rb
103
+ - lib/clickhouse_ruby/connection_pool.rb
104
+ - lib/clickhouse_ruby/errors.rb
105
+ - lib/clickhouse_ruby/result.rb
106
+ - lib/clickhouse_ruby/types.rb
107
+ - lib/clickhouse_ruby/types/array.rb
108
+ - lib/clickhouse_ruby/types/base.rb
109
+ - lib/clickhouse_ruby/types/boolean.rb
110
+ - lib/clickhouse_ruby/types/date_time.rb
111
+ - lib/clickhouse_ruby/types/float.rb
112
+ - lib/clickhouse_ruby/types/integer.rb
113
+ - lib/clickhouse_ruby/types/low_cardinality.rb
114
+ - lib/clickhouse_ruby/types/map.rb
115
+ - lib/clickhouse_ruby/types/nullable.rb
116
+ - lib/clickhouse_ruby/types/parser.rb
117
+ - lib/clickhouse_ruby/types/registry.rb
118
+ - lib/clickhouse_ruby/types/string.rb
119
+ - lib/clickhouse_ruby/types/tuple.rb
120
+ - lib/clickhouse_ruby/types/uuid.rb
121
+ - lib/clickhouse_ruby/version.rb
122
+ homepage: https://github.com/kamardev/clickhouse-ruby
123
+ licenses:
124
+ - MIT
125
+ metadata:
126
+ homepage_uri: https://github.com/kamardev/clickhouse-ruby
127
+ source_code_uri: https://github.com/kamardev/clickhouse-ruby
128
+ changelog_uri: https://github.com/kamardev/clickhouse-ruby/blob/main/CHANGELOG.md
129
+ documentation_uri: https://rubydoc.info/gems/clickhouse-ruby
130
+ rubygems_mfa_required: 'true'
131
+ post_install_message:
132
+ rdoc_options: []
133
+ require_paths:
134
+ - lib
135
+ required_ruby_version: !ruby/object:Gem::Requirement
136
+ requirements:
137
+ - - ">="
138
+ - !ruby/object:Gem::Version
139
+ version: 2.6.0
140
+ required_rubygems_version: !ruby/object:Gem::Requirement
141
+ requirements:
142
+ - - ">="
143
+ - !ruby/object:Gem::Version
144
+ version: '0'
145
+ requirements: []
146
+ rubygems_version: 3.0.3.1
147
+ signing_key:
148
+ specification_version: 4
149
+ summary: Ruby/ActiveRecord integration for ClickHouse
150
+ test_files: []