bmg 0.17.5 → 0.18.1

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.
data/lib/bmg/support.rb CHANGED
@@ -1,2 +1,5 @@
1
1
  require_relative 'support/tuple_algebra'
2
+ require_relative 'support/tuple_transformer'
2
3
  require_relative 'support/keys'
4
+ require_relative 'support/ordering'
5
+ require_relative 'support/output_preferences'
@@ -7,6 +7,10 @@ module Bmg
7
7
 
8
8
  public ## tools
9
9
 
10
+ def select(&bl)
11
+ Keys.new(@keys.select(&bl), false)
12
+ end
13
+
10
14
  public ## algebra
11
15
 
12
16
  def allbut(oldtype, newtype, butlist)
@@ -0,0 +1,20 @@
1
+ module Bmg
2
+ class Ordering
3
+
4
+ def initialize(attrs)
5
+ @attrs = attrs
6
+ end
7
+ attr_reader :attrs
8
+
9
+ def comparator
10
+ ->(t1, t2) {
11
+ attrs.each do |(attr,direction)|
12
+ c = t1[attr] <=> t2[attr]
13
+ return (direction == :desc ? -c : c) unless c==0
14
+ end
15
+ 0
16
+ }
17
+ end
18
+
19
+ end # class Ordering
20
+ end # module Bmg
@@ -0,0 +1,44 @@
1
+ module Bmg
2
+ class OutputPreferences
3
+
4
+ DEFAULT_PREFS = {
5
+ attributes_ordering: nil,
6
+ extra_attributes: :after
7
+ }
8
+
9
+ def initialize(options)
10
+ @options = DEFAULT_PREFS.merge(options)
11
+ end
12
+ attr_reader :options
13
+
14
+ def self.dress(arg)
15
+ return arg if arg.is_a?(OutputPreferences)
16
+ arg = {} if arg.nil?
17
+ new(arg)
18
+ end
19
+
20
+ def attributes_ordering
21
+ options[:attributes_ordering]
22
+ end
23
+
24
+ def extra_attributes
25
+ options[:extra_attributes]
26
+ end
27
+
28
+ def order_attrlist(attrlist)
29
+ return attrlist if attributes_ordering.nil?
30
+ index = Hash[attributes_ordering.each_with_index.to_a]
31
+ attrlist.sort{|a,b|
32
+ ai, bi = index[a], index[b]
33
+ if ai && bi
34
+ ai <=> bi
35
+ elsif ai
36
+ extra_attributes == :after ? -1 : 1
37
+ else
38
+ extra_attributes == :after ? 1 : -1
39
+ end
40
+ }
41
+ end
42
+
43
+ end # class OutputPreferences
44
+ end # module Bmg
@@ -19,5 +19,11 @@ module Bmg
19
19
  end
20
20
  module_function :rename
21
21
 
22
+ def symbolize_keys(h)
23
+ return h if h.empty?
24
+ h.each_with_object({}){|(k,v),h| h[k.to_sym] = v }
25
+ end
26
+ module_function :symbolize_keys
27
+
22
28
  end # module TupleAlgebra
23
29
  end # module Bmg
@@ -0,0 +1,63 @@
1
+ module Bmg
2
+ class TupleTransformer
3
+
4
+ def initialize(transformation)
5
+ @transformation = transformation
6
+ end
7
+
8
+ def self.new(arg)
9
+ return arg if arg.is_a?(TupleTransformer)
10
+ super
11
+ end
12
+
13
+ def call(tuple)
14
+ transform_tuple(tuple, @transformation)
15
+ end
16
+
17
+ def knows_attrlist?
18
+ @transformation.is_a?(Hash)
19
+ end
20
+
21
+ def to_attrlist
22
+ @transformation.keys
23
+ end
24
+
25
+ private
26
+
27
+ def transform_tuple(tuple, with)
28
+ case with
29
+ when Symbol, Proc, Regexp
30
+ tuple.each_with_object({}){|(k,v),dup|
31
+ dup[k] = transform_attr(v, with)
32
+ }
33
+ when Hash
34
+ with.each_with_object(tuple.dup){|(k,v),dup|
35
+ dup[k] = transform_attr(dup[k], v)
36
+ }
37
+ when Array
38
+ with.inject(tuple){|dup,on|
39
+ transform_tuple(dup, on)
40
+ }
41
+ else
42
+ raise ArgumentError, "Unexpected transformation `#{with.inspect}`"
43
+ end
44
+ end
45
+
46
+ def transform_attr(value, with)
47
+ case with
48
+ when Symbol
49
+ value.send(with)
50
+ when Regexp
51
+ m = with.match(value.to_s)
52
+ m.nil? ? m : m.to_s
53
+ when Proc
54
+ with.call(value)
55
+ when Hash
56
+ with[value]
57
+ else
58
+ raise ArgumentError, "Unexpected transformation `#{with.inspect}`"
59
+ end
60
+ end
61
+
62
+ end # module TupleTransformer
63
+ end # module Bmg
data/lib/bmg/type.rb CHANGED
@@ -241,6 +241,31 @@ module Bmg
241
241
  }
242
242
  end
243
243
 
244
+ def transform(transformation, options = {})
245
+ transformer = TupleTransformer.new(transformation)
246
+ if typechecked? && knows_attrlist? && transformer.knows_attrlist?
247
+ known_attributes!(transformer.to_attrlist)
248
+ end
249
+ keys = if options[:key_preserving]
250
+ self._keys
251
+ elsif transformer.knows_attrlist? && knows_keys?
252
+ touched_attrs = transformer.to_attrlist
253
+ keys = self._keys.select{|k| (k & touched_attrs).empty? }
254
+ else
255
+ nil
256
+ end
257
+ pred = if transformer.knows_attrlist?
258
+ attr_list = transformer.to_attrlist
259
+ predicate.and_split(attr_list).last
260
+ else
261
+ Predicate.tautology
262
+ end
263
+ dup.tap{|x|
264
+ x.keys = keys
265
+ x.predicate = pred
266
+ }
267
+ end
268
+
244
269
  def union(other)
245
270
  if typechecked? && knows_attrlist? && other.knows_attrlist?
246
271
  missing = self.attrlist - other.attrlist
data/lib/bmg/version.rb CHANGED
@@ -1,8 +1,8 @@
1
1
  module Bmg
2
2
  module Version
3
3
  MAJOR = 0
4
- MINOR = 17
5
- TINY = 5
4
+ MINOR = 18
5
+ TINY = 1
6
6
  end
7
7
  VERSION = "#{Version::MAJOR}.#{Version::MINOR}.#{Version::TINY}"
8
8
  end
@@ -6,26 +6,37 @@ module Bmg
6
6
  DEFAULT_OPTIONS = {
7
7
  }
8
8
 
9
- def initialize(options)
10
- @options = DEFAULT_OPTIONS.merge(options)
9
+ def initialize(csv_options, output_preferences = nil)
10
+ @csv_options = DEFAULT_OPTIONS.merge(csv_options)
11
+ @output_preferences = OutputPreferences.dress(output_preferences)
11
12
  end
12
- attr_reader :options
13
+ attr_reader :csv_options, :output_preferences
13
14
 
14
15
  def call(relation, string_or_io = nil)
15
16
  require 'csv'
16
17
  string_or_io, to_s = string_or_io.nil? ? [StringIO.new, true] : [string_or_io, false]
17
- headers = relation.type.to_attrlist if relation.type.knows_attrlist?
18
- csv = nil
18
+ headers, csv = infer_headers(relation.type), nil
19
19
  relation.each do |tuple|
20
20
  if csv.nil?
21
- headers = tuple.keys if headers.nil?
22
- csv = CSV.new(string_or_io, options.merge(headers: headers))
21
+ headers = infer_headers(tuple) if headers.nil?
22
+ csv = CSV.new(string_or_io, csv_options.merge(headers: headers))
23
23
  end
24
24
  csv << headers.map{|h| tuple[h] }
25
25
  end
26
26
  to_s ? string_or_io.string : string_or_io
27
27
  end
28
28
 
29
+ private
30
+
31
+ def infer_headers(from)
32
+ attrlist = if from.is_a?(Type) && from.knows_attrlist?
33
+ from.to_attrlist
34
+ elsif from.is_a?(Hash)
35
+ from.keys
36
+ end
37
+ attrlist ? output_preferences.order_attrlist(attrlist) : nil
38
+ end
39
+
29
40
  end # class Csv
30
41
  end # module Writer
31
42
  end # module Bmg
data/tasks/test.rake CHANGED
@@ -6,17 +6,24 @@ namespace :test do
6
6
  desc "Runs unit tests"
7
7
  RSpec::Core::RakeTask.new(:unit) do |t|
8
8
  t.pattern = "spec/unit/**/test_*.rb"
9
- t.rspec_opts = ["-Ilib", "-Ispec/unit", "--fail-fast", "--color", "--backtrace", "--format=progress"]
9
+ t.rspec_opts = ["-Ilib", "-Ispec/unit", "--color", "--backtrace", "--format=progress"]
10
10
  end
11
11
  tests << :unit
12
12
 
13
13
  desc "Runs integration tests"
14
14
  RSpec::Core::RakeTask.new(:integration) do |t|
15
15
  t.pattern = "spec/integration/**/test_*.rb"
16
- t.rspec_opts = ["-Ilib", "-Ispec/integration", "--fail-fast", "--color", "--backtrace", "--format=progress"]
16
+ t.rspec_opts = ["-Ilib", "-Ispec/integration", "--color", "--backtrace", "--format=progress"]
17
17
  end
18
18
  tests << :integration
19
19
 
20
+ desc "Runs github regression tests"
21
+ RSpec::Core::RakeTask.new(:regression) do |t|
22
+ t.pattern = "spec/regression/**/test_*.rb"
23
+ t.rspec_opts = ["-Ilib", "-Ispec/regression", "--color", "--backtrace", "--format=progress"]
24
+ end
25
+ tests << :regression
26
+
20
27
  task :all => tests
21
28
  end
22
29
 
metadata CHANGED
@@ -1,49 +1,49 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bmg
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.17.5
4
+ version: 0.18.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bernard Lambeau
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-08-17 00:00:00.000000000 Z
11
+ date: 2021-04-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: predicate
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
18
- - !ruby/object:Gem::Version
19
- version: '2.4'
20
17
  - - ">="
21
18
  - !ruby/object:Gem::Version
22
- version: 2.4.0
19
+ version: 2.5.0
20
+ - - "~>"
21
+ - !ruby/object:Gem::Version
22
+ version: '2.5'
23
23
  type: :runtime
24
24
  prerelease: false
25
25
  version_requirements: !ruby/object:Gem::Requirement
26
26
  requirements:
27
- - - "~>"
28
- - !ruby/object:Gem::Version
29
- version: '2.4'
30
27
  - - ">="
31
28
  - !ruby/object:Gem::Version
32
- version: 2.4.0
29
+ version: 2.5.0
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: '2.5'
33
33
  - !ruby/object:Gem::Dependency
34
34
  name: path
35
35
  requirement: !ruby/object:Gem::Requirement
36
36
  requirements:
37
37
  - - ">="
38
38
  - !ruby/object:Gem::Version
39
- version: '1.3'
39
+ version: '2.0'
40
40
  type: :runtime
41
41
  prerelease: false
42
42
  version_requirements: !ruby/object:Gem::Requirement
43
43
  requirements:
44
44
  - - ">="
45
45
  - !ruby/object:Gem::Version
46
- version: '1.3'
46
+ version: '2.0'
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: rake
49
49
  requirement: !ruby/object:Gem::Requirement
@@ -78,14 +78,14 @@ dependencies:
78
78
  requirements:
79
79
  - - ">="
80
80
  - !ruby/object:Gem::Version
81
- version: '2.7'
81
+ version: '2.8'
82
82
  type: :development
83
83
  prerelease: false
84
84
  version_requirements: !ruby/object:Gem::Requirement
85
85
  requirements:
86
86
  - - ">="
87
87
  - !ruby/object:Gem::Version
88
- version: '2.7'
88
+ version: '2.8'
89
89
  - !ruby/object:Gem::Dependency
90
90
  name: sequel
91
91
  requirement: !ruby/object:Gem::Requirement
@@ -149,14 +149,17 @@ files:
149
149
  - lib/bmg/operator/shared/nary.rb
150
150
  - lib/bmg/operator/shared/unary.rb
151
151
  - lib/bmg/operator/summarize.rb
152
+ - lib/bmg/operator/transform.rb
152
153
  - lib/bmg/operator/union.rb
153
154
  - lib/bmg/reader.rb
154
155
  - lib/bmg/reader/csv.rb
155
156
  - lib/bmg/reader/excel.rb
157
+ - lib/bmg/reader/text_file.rb
156
158
  - lib/bmg/relation.rb
157
159
  - lib/bmg/relation/empty.rb
158
160
  - lib/bmg/relation/in_memory.rb
159
161
  - lib/bmg/relation/materialized.rb
162
+ - lib/bmg/relation/proxy.rb
160
163
  - lib/bmg/relation/spied.rb
161
164
  - lib/bmg/sequel.rb
162
165
  - lib/bmg/sequel/ext.rb
@@ -258,7 +261,10 @@ files:
258
261
  - lib/bmg/summarizer/variance.rb
259
262
  - lib/bmg/support.rb
260
263
  - lib/bmg/support/keys.rb
264
+ - lib/bmg/support/ordering.rb
265
+ - lib/bmg/support/output_preferences.rb
261
266
  - lib/bmg/support/tuple_algebra.rb
267
+ - lib/bmg/support/tuple_transformer.rb
262
268
  - lib/bmg/type.rb
263
269
  - lib/bmg/version.rb
264
270
  - lib/bmg/writer.rb
@@ -284,7 +290,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
284
290
  - !ruby/object:Gem::Version
285
291
  version: '0'
286
292
  requirements: []
287
- rubygems_version: 3.1.2
293
+ rubygems_version: 3.0.8
288
294
  signing_key:
289
295
  specification_version: 4
290
296
  summary: Bmg is Alf's successor.