relationizer 0.2.3 → 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: ac854434e4951f07131e424948782d7121b286c1
4
- data.tar.gz: c1b758d8b06cd83331769f95deaf9862fec7ea0f
2
+ SHA256:
3
+ metadata.gz: c55b691cb2f721fd3fd39909e1745a72cb9743211587c6fdf477ba5e04fd209b
4
+ data.tar.gz: 7233397ac4d8ab8c4f29c2b1de073fa01634593884b53501f6bdfc1957e25f82
5
5
  SHA512:
6
- metadata.gz: 8cb01ce847a6662c03e85eabc5bf092e233b919641b284f1a93c1414004c6f7d56949ca8d3777c64395a398c4718ac9ff4debe4cd072d6d3470665a4566761df
7
- data.tar.gz: 808b44e83b0ed0cebc147763ce528322977f73fbd6826bc2163661e4e915346bcd82979e4c93579cf39ccf024bbe37d5f057d47da16d96d52436eda2a90d067b
6
+ metadata.gz: '093102036edcd8201974a31edb553e35c0cb2e5895ec134e091c50d9bdfdf7377fdaedc6e934d08575eeecafd81602c0d976f7d1716b9a3ad35ac5f9984b11ee'
7
+ data.tar.gz: bdb16173d5c526e7fd86ae4129522479e9913cc0a33d63d3aa7fec753d09f604681767f08ee3cfe26594046225693ee00c3ff557d5f60ce9c102997807a5a13d
@@ -0,0 +1,20 @@
1
+ name: Ruby
2
+
3
+ on: [push]
4
+
5
+ jobs:
6
+ build:
7
+
8
+ runs-on: ubuntu-latest
9
+
10
+ steps:
11
+ - uses: actions/checkout@v1
12
+ - name: Set up Ruby 2.6
13
+ uses: actions/setup-ruby@v1
14
+ with:
15
+ ruby-version: 2.6.x
16
+ - name: Build and test with Rake
17
+ run: |
18
+ gem install bundler
19
+ bundle install --jobs 4 --retry 3
20
+ bundle exec rake
data/lib/relationizer.rb CHANGED
@@ -1,7 +1,6 @@
1
- require "relationizer/version"
1
+ require_relative "relationizer/version"
2
2
  require_relative "relationizer/postgresql"
3
- require_relative "relationizer/big_query/standard"
3
+ require_relative "relationizer/big_query"
4
4
 
5
5
  module Relationizer
6
- # Your code goes here...
7
- end
6
+ end
@@ -0,0 +1,138 @@
1
+ require 'bigdecimal'
2
+ require 'date'
3
+
4
+ module Relationizer
5
+ module BigQuery
6
+ class ReasonlessTypeError < StandardError; end
7
+
8
+ KNOWN_TYPES = [:INT64, :FLOAT64, :STRING, :BOOL, :TIMESTAMP, :DATE]
9
+
10
+ DEFAULT_TYPES = -> (obj) {
11
+ case obj
12
+ when Integer then :INT64
13
+ when BigDecimal then :FLOAT64
14
+ when Float then :FLOAT64
15
+ when String then :STRING
16
+ when TrueClass then :BOOL
17
+ when FalseClass then :BOOL
18
+ when Time then :TIMESTAMP
19
+ when DateTime then :TIMESTAMP
20
+ when Date then :DATE
21
+ when Array then :ARRAY
22
+ else
23
+ nil
24
+ end
25
+ }
26
+
27
+ def create_relation_literal(schema, tuples)
28
+ types = fixed_types(schema.values, tuples)
29
+
30
+ _types_exp = types_exp(schema.keys, types)
31
+
32
+ tuples_exp = tuples.map { |tuple|
33
+ tuple.zip(types).
34
+ map { |(col, type)| to_literal(col, type) }.
35
+ join(", ").
36
+ tap { |t| break "(#{t}#{', NULL' if tuple.length == 1})" }
37
+ }.join(", ").tap { |t| break "[#{t}]"}
38
+
39
+ select_exp = if schema.one?
40
+ "#{schema.keys.first}"
41
+ else
42
+ '*'
43
+ end
44
+
45
+ "SELECT #{select_exp} FROM UNNEST(#{_types_exp}#{tuples_exp})"
46
+ end
47
+
48
+ private
49
+
50
+ def array_type(array)
51
+ classes = array.compact.map(&:class).uniq
52
+
53
+ unless classes.length == 1
54
+ raise ReasonlessTypeError.new("Ambiguous type of element in array: #{classes}")
55
+ end
56
+
57
+ DEFAULT_TYPES[array.first] || :STRING
58
+ end
59
+
60
+ def types_exp(names, types)
61
+ case names.length
62
+ when 1
63
+ %Q{ARRAY<STRUCT<`#{names.first}` #{types.first}, `___dummy` STRING>>}
64
+ else
65
+ %Q{ARRAY<STRUCT<#{names.zip(types).map { |(name, type)| "`#{name}` #{type}" }.join(", ")}>>}
66
+ end
67
+ end
68
+
69
+ def many_candidate_check(types)
70
+ raise ReasonlessTypeError.new("Many candidate: #{types.join(', ')}") unless types.one?
71
+ end
72
+
73
+ def empty_candidate_check(types)
74
+ raise ReasonlessTypeError.new("Candidate nothing") if types.empty?
75
+ end
76
+
77
+ def fixed_types(schema, tuples)
78
+ tuples.transpose.zip(schema.to_a).map { |values, type|
79
+ next type if type
80
+
81
+ if values.map { |o| o.is_a?(Array) }.all?
82
+ types = values.
83
+ map(&method(:array_type)).uniq.
84
+ tap(&method(:empty_candidate_check)).
85
+ tap(&method(:many_candidate_check))
86
+
87
+ next "ARRAY<#{types.first}>".to_sym
88
+ end
89
+
90
+ values.
91
+ map(&DEFAULT_TYPES).compact.uniq.
92
+ tap(&method(:empty_candidate_check)).
93
+ tap(&method(:many_candidate_check)).
94
+ first || :STRING
95
+ }
96
+ end
97
+
98
+ def to_literal(obj, type)
99
+ return "NULL" if obj.nil?
100
+
101
+ case type
102
+ when :ARRAY
103
+ t = array_type(obj)
104
+ obj.map { |e| to_literal(e, t) }.join(', ').tap { |s| break "[#{s}]"}
105
+ when /^ARRAY\<.+\>$/
106
+ t = /^ARRAY\<(.+)\>$/.match(type).to_a&.dig(1).to_sym
107
+ raise "Unknown type: #{t}" unless KNOWN_TYPES.include?(t)
108
+ obj.map { |e| to_literal(e, t) }.join(', ').tap { |s| break "[#{s}]"}
109
+ when :TIMESTAMP
110
+ %Q{'#{obj.strftime('%Y-%m-%d %H:%M:%S')}'}
111
+ when :STRING, :DATE
112
+ obj.to_s.gsub(/'/, "\'").tap do |s|
113
+ break "'#{s}'"
114
+ end
115
+ when :BOOL
116
+ case obj
117
+ when TrueClass, FalseClass
118
+ obj
119
+ else
120
+ !!obj
121
+ end
122
+ when :FLOAT64
123
+ case obj
124
+ when Float::INFINITY
125
+ "CAST('inf' AS FLOAT64)"
126
+ when -Float::INFINITY
127
+ "CAST('-inf' AS FLOAT64)"
128
+ else
129
+ obj
130
+ end
131
+ when :INT64
132
+ obj.to_s
133
+ else
134
+ raise "Unknown type: #{type}"
135
+ end
136
+ end
137
+ end
138
+ end
@@ -49,7 +49,7 @@ module Relationizer
49
49
 
50
50
  def select_exp(schema, tuples)
51
51
  tuples.transpose.zip(schema.to_a).map { |(values, (name, type))|
52
- next "#{name}::#{type.to_s.upcase}" if type
52
+ next %Q{"#{name}"::#{type.to_s.upcase}} if type
53
53
 
54
54
  values.
55
55
  map(&DEFAULT_TYPES).compact.uniq.
@@ -57,7 +57,7 @@ module Relationizer
57
57
  tap(&method(:many_candidate_check)).
58
58
  first.
59
59
  to_s.upcase.
60
- tap { |fixed_type| break "#{name}::#{fixed_type}" }
60
+ tap { |fixed_type| break %Q{"#{name}"::#{fixed_type}} }
61
61
  }.join(", ")
62
62
  end
63
63
 
@@ -1,3 +1,3 @@
1
1
  module Relationizer
2
- VERSION = "0.2.3"
2
+ VERSION = "0.3.0"
3
3
  end
data/relationizer.gemspec CHANGED
@@ -18,7 +18,7 @@ Gem::Specification.new do |spec|
18
18
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
19
19
  spec.require_paths = ["lib"]
20
20
 
21
- spec.add_development_dependency "bundler", "~> 1.12"
21
+ spec.add_development_dependency "bundler"
22
22
  spec.add_development_dependency "rake", "~> 10.0"
23
23
  spec.add_development_dependency "test-unit", "~> 3.0.0"
24
24
  end
metadata CHANGED
@@ -1,29 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: relationizer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.3
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - yancya
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-11-17 00:00:00.000000000 Z
11
+ date: 2019-12-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '1.12'
19
+ version: '0'
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - "~>"
24
+ - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: '1.12'
26
+ version: '0'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rake
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -59,6 +59,7 @@ executables: []
59
59
  extensions: []
60
60
  extra_rdoc_files: []
61
61
  files:
62
+ - ".github/workflows/ruby.yml"
62
63
  - ".gitignore"
63
64
  - Gemfile
64
65
  - LICENSE.txt
@@ -66,11 +67,8 @@ files:
66
67
  - Rakefile
67
68
  - bin/console
68
69
  - bin/setup
69
- - lib/rel_lib/relationizer.rb
70
- - lib/rel_lib/relationizer/version.rb
71
70
  - lib/relationizer.rb
72
- - lib/relationizer/big_query/legacy.rb
73
- - lib/relationizer/big_query/standard.rb
71
+ - lib/relationizer/big_query.rb
74
72
  - lib/relationizer/postgresql.rb
75
73
  - lib/relationizer/version.rb
76
74
  - relationizer.gemspec
@@ -93,8 +91,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
93
91
  - !ruby/object:Gem::Version
94
92
  version: '0'
95
93
  requirements: []
96
- rubyforge_project:
97
- rubygems_version: 2.6.13
94
+ rubygems_version: 3.0.3
98
95
  signing_key:
99
96
  specification_version: 4
100
97
  summary: Array<Array> to Relation ppoi String
@@ -1,8 +0,0 @@
1
- require "relationizer/version"
2
- require_relative "./relationizer/postgresql.rb"
3
- require_relative "./relationizer/big_query/Legacy.rb"
4
- require_relative "./relationizer/big_query/Standard.rb"
5
-
6
- module Relationizer
7
- # Your code goes here...
8
- end
@@ -1,3 +0,0 @@
1
- module Relationizer
2
- VERSION = "0.1.0"
3
- end
@@ -1,8 +0,0 @@
1
- module Relationizer
2
- module BigQuery
3
- module Legacy
4
- def create_relation_literal(rows, schema)
5
- end
6
- end
7
- end
8
- end
@@ -1,140 +0,0 @@
1
- require 'bigdecimal'
2
- require 'date'
3
-
4
- module Relationizer
5
- module BigQuery
6
- module Standard
7
- class ReasonlessTypeError < StandardError; end
8
-
9
- KNOWN_TYPES = [:INT64, :FLOAT64, :STRING, :BOOL, :TIMESTAMP, :DATE]
10
-
11
- DEFAULT_TYPES = -> (obj) {
12
- case obj
13
- when Integer then :INT64
14
- when BigDecimal then :FLOAT64
15
- when Float then :FLOAT64
16
- when String then :STRING
17
- when TrueClass then :BOOL
18
- when FalseClass then :BOOL
19
- when Time then :TIMESTAMP
20
- when DateTime then :TIMESTAMP
21
- when Date then :DATE
22
- when Array then :ARRAY
23
- else
24
- nil
25
- end
26
- }
27
-
28
- def create_relation_literal(schema, tuples)
29
- types = fixed_types(schema.values, tuples)
30
-
31
- _types_exp = types_exp(schema.keys, types)
32
-
33
- tuples_exp = tuples.map { |tuple|
34
- tuple.zip(types).
35
- map { |(col, type)| to_literal(col, type) }.
36
- join(", ").
37
- tap { |t| break "(#{t}#{', NULL' if tuple.length == 1})" }
38
- }.join(", ").tap { |t| break "[#{t}]"}
39
-
40
- select_exp = if schema.one?
41
- "#{schema.keys.first}"
42
- else
43
- '*'
44
- end
45
-
46
- "SELECT #{select_exp} FROM UNNEST(#{_types_exp}#{tuples_exp})"
47
- end
48
-
49
- private
50
-
51
- def array_type(array)
52
- classes = array.compact.map(&:class).uniq
53
-
54
- unless classes.length == 1
55
- raise ReasonlessTypeError.new("Ambiguous type of element in array: #{classes}")
56
- end
57
-
58
- DEFAULT_TYPES[array.first] || :STRING
59
- end
60
-
61
- def types_exp(names, types)
62
- case names.length
63
- when 1
64
- %Q{ARRAY<STRUCT<#{names.first} #{types.first}, ___dummy STRING>>}
65
- else
66
- %Q{ARRAY<STRUCT<#{names.zip(types).map { |(name, type)| "#{name} #{type}" }.join(", ")}>>}
67
- end
68
- end
69
-
70
- def many_candidate_check(types)
71
- raise ReasonlessTypeError.new("Many candidate: #{types.join(', ')}") unless types.one?
72
- end
73
-
74
- def empty_candidate_check(types)
75
- raise ReasonlessTypeError.new("Candidate nothing") if types.empty?
76
- end
77
-
78
- def fixed_types(schema, tuples)
79
- tuples.transpose.zip(schema.to_a).map { |values, type|
80
- next type if type
81
-
82
- if values.map { |o| o.is_a?(Array) }.all?
83
- types = values.
84
- map(&method(:array_type)).uniq.
85
- tap(&method(:empty_candidate_check)).
86
- tap(&method(:many_candidate_check))
87
-
88
- next "ARRAY<#{types.first}>".to_sym
89
- end
90
-
91
- values.
92
- map(&DEFAULT_TYPES).compact.uniq.
93
- tap(&method(:empty_candidate_check)).
94
- tap(&method(:many_candidate_check)).
95
- first || :STRING
96
- }
97
- end
98
-
99
- def to_literal(obj, type)
100
- return "NULL" if obj.nil?
101
-
102
- case type
103
- when :ARRAY
104
- t = array_type(obj)
105
- obj.map { |e| to_literal(e, t) }.join(', ').tap { |s| break "[#{s}]"}
106
- when /^ARRAY\<.+\>$/
107
- t = /^ARRAY\<(.+)\>$/.match(type).to_a&.dig(1).to_sym
108
- raise "Unknown type: #{t}" unless KNOWN_TYPES.include?(t)
109
- obj.map { |e| to_literal(e, t) }.join(', ').tap { |s| break "[#{s}]"}
110
- when :TIMESTAMP
111
- %Q{'#{obj.strftime('%Y-%m-%d %H:%M:%S')}'}
112
- when :STRING, :DATE
113
- obj.to_s.gsub(/'/, "\'").tap do |s|
114
- break "'#{s}'"
115
- end
116
- when :BOOL
117
- case obj
118
- when TrueClass, FalseClass
119
- obj
120
- else
121
- !!obj
122
- end
123
- when :FLOAT64
124
- case obj
125
- when Float::INFINITY
126
- "CAST('inf' AS FLOAT64)"
127
- when -Float::INFINITY
128
- "CAST('-inf' AS FLOAT64)"
129
- else
130
- obj
131
- end
132
- when :INT64
133
- obj.to_s
134
- else
135
- raise "Unknown type: #{type}"
136
- end
137
- end
138
- end
139
- end
140
- end