Class: Bmg::Type

Inherits:
Object
  • Object
show all
Defined in:
lib/bmg/type.rb

Constant Summary collapse

ANY =
Type.new

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(predicate = Predicate.tautology) ⇒ Type

Returns a new instance of Type.

Raises:

  • (ArgumentError)


4
5
6
7
8
# File 'lib/bmg/type.rb', line 4

def initialize(predicate = Predicate.tautology)
  @predicate = predicate
  @typechecked = false
  raise ArgumentError if @predicate.nil?
end

Instance Attribute Details

#attrlistObject

Returns the value of attribute attrlist.



66
67
68
# File 'lib/bmg/type.rb', line 66

def attrlist
  @attrlist
end

#headingObject

Returns the value of attribute heading.



50
51
52
# File 'lib/bmg/type.rb', line 50

def heading
  @heading
end

#keysObject

Returns the value of attribute keys.



94
95
96
# File 'lib/bmg/type.rb', line 94

def keys
  @keys
end

#predicateObject

Returns the value of attribute predicate.



39
40
41
# File 'lib/bmg/type.rb', line 39

def predicate
  @predicate
end

Class Method Details

.for_heading(heading) ⇒ Object



12
13
14
# File 'lib/bmg/type.rb', line 12

def self.for_heading(heading)
  Type.new.with_heading(heading)
end

Instance Method Details

#[](attribute) ⇒ Object



116
117
118
# File 'lib/bmg/type.rb', line 116

def [](attribute)
  ANY
end

#allbut(butlist) ⇒ Object



122
123
124
125
126
127
128
129
# File 'lib/bmg/type.rb', line 122

def allbut(butlist)
  known_attributes!(butlist) if typechecked? && knows_attrlist?
  dup.tap{|x|
    x.attrlist  = self.attrlist - butlist if knows_attrlist?
    x.predicate = Predicate.tautology
    x.keys      = self._keys.allbut(self, x, butlist) if knows_keys?
  }
end

#attrlist!Object



69
70
71
72
# File 'lib/bmg/type.rb', line 69

def attrlist!
  knows_attrlist!
  attrlist
end

#autosummarize(by, summarization, options) ⇒ Object



150
151
152
153
154
155
156
157
# File 'lib/bmg/type.rb', line 150

def autosummarize(by, summarization, options)
  known_attributes!(by + summarization.keys) if typechecked? && knows_attrlist?
  dup.tap{|x|
    x.attrlist = nil
    x.predicate = Predicate.tautology
    x.keys = nil
  }
end

#autowrap(options) ⇒ Object



138
139
140
141
142
143
144
145
146
147
148
# File 'lib/bmg/type.rb', line 138

def autowrap(options)
  sep = Operator::Autowrap.separator(options)
  splitter = ->(a){ a.to_s.split(sep).first }
  is_split = ->(a){ a.to_s.split(sep).size > 1 }
  dup.tap{|x|
    x.attrlist  = self.attrlist.map(&splitter).uniq.map(&:to_sym) if knows_attrlist?
    x.keys      = self._keys.autowrap(self, x, options) if knows_keys?
    autowrapped = self.predicate.free_variables.select(&is_split)
    x.predicate = autowrapped.empty? ? self.predicate : self.predicate.and_split(autowrapped).last
  }
end

#constants(cs) ⇒ Object



159
160
161
162
163
164
165
166
# File 'lib/bmg/type.rb', line 159

def constants(cs)
  unknown_attributes!(cs.keys) if typechecked? && knows_attrlist?
  dup.tap{|x|
    x.attrlist  = self.attrlist + cs.keys if knows_attrlist?
    x.predicate = self.predicate & Predicate.eq(cs)
    ### keys stay the same
  }
end

#cross_join_compatible!(right) ⇒ Object



366
367
368
369
370
371
# File 'lib/bmg/type.rb', line 366

def cross_join_compatible!(right)
  shared = self.attrlist & right.type.attrlist
  unless shared.empty?
    raise TypeError, "Cross product incompatible — duplicate attribute(s): #{shared.join(', ')}"
  end
end

#extend(extension) ⇒ Object



168
169
170
171
172
173
174
175
# File 'lib/bmg/type.rb', line 168

def extend(extension)
  unknown_attributes!(extension.keys) if typechecked? && knows_attrlist?
  dup.tap{|x|
    x.attrlist  = self.attrlist + extension.keys if knows_attrlist?
    x.predicate = Predicate.tautology
    ### keys stay the same (?)
  }
end

#group(attrs, as) ⇒ Object



177
178
179
180
181
182
183
184
185
186
187
# File 'lib/bmg/type.rb', line 177

def group(attrs, as)
  if typechecked? && knows_attrlist?
    known_attributes!(attrs)
    unknown_attributes!([as])
  end
  dup.tap{|x|
    x.attrlist  = self.attrlist - attrs + [as] if knows_attrlist?
    x.predicate = Predicate.tautology
    x.keys      = self._keys.group(self, x, attrs, as) if knows_keys?
  }
end

#identity_autowrap?(options) ⇒ Boolean

Returns:

  • (Boolean)


131
132
133
134
135
136
# File 'lib/bmg/type.rb', line 131

def identity_autowrap?(options)
  return false unless knows_attrlist?

  sep = Operator::Autowrap.separator(options)
  self.attrlist.all?{|a| a.to_s.index(sep).nil? }
end

#image(right, as, on, options) ⇒ Object



189
190
191
192
193
194
195
196
197
198
199
# File 'lib/bmg/type.rb', line 189

def image(right, as, on, options)
  if typechecked? && knows_attrlist?
    join_compatible!(right, on)
    unknown_attributes!([as])
  end
  dup.tap{|x|
    x.attrlist  = self.attrlist + [as] if knows_attrlist?
    x.predicate = Predicate.tautology
    x.keys      = self._keys
  }
end

#join(right, on) ⇒ Object



201
202
203
204
205
206
207
208
# File 'lib/bmg/type.rb', line 201

def join(right, on)
  join_compatible!(right, on) if typechecked? && knows_attrlist?
  dup.tap{|x|
    x.attrlist  = (knows_attrlist? and right.knows_attrlist?) ? (self.attrlist + right.attrlist).uniq : nil
    x.predicate = self.predicate & right.predicate
    x.keys      = (knows_keys? and right.knows_keys?) ? self._keys.join(self, x, right, on) : nil
  }
end

#join_compatible!(right, on) ⇒ Object

Raises:



357
358
359
360
361
362
363
364
# File 'lib/bmg/type.rb', line 357

def join_compatible!(right, on)
  extra = on - self.attrlist
  raise TypeError, "Unknow attributes #{extra.join(', ')}" unless extra.empty?
  if right.knows_attrlist?
    extra = on - right.attrlist
    raise TypeError, "Unknow attributes #{extra.join(', ')}" unless extra.empty?
  end
end

#known_attributes!(attrs) ⇒ Object

Raises:



347
348
349
350
# File 'lib/bmg/type.rb', line 347

def known_attributes!(attrs)
  extra = attrs - self.attrlist
  raise TypeError, "Unknown attributes #{extra.join(', ')}" unless extra.empty?
end

#knows_attrlist!Object



84
85
86
# File 'lib/bmg/type.rb', line 84

def knows_attrlist!
  raise UnknownAttributesError unless knows_attrlist?
end

#knows_attrlist?Boolean

Returns:

  • (Boolean)


80
81
82
# File 'lib/bmg/type.rb', line 80

def knows_attrlist?
  !self.attrlist.nil?
end

#knows_keys?Boolean

Returns:

  • (Boolean)


98
99
100
# File 'lib/bmg/type.rb', line 98

def knows_keys?
  !!@keys
end

#knows_types?Boolean

Returns:

  • (Boolean)


60
61
62
# File 'lib/bmg/type.rb', line 60

def knows_types?
  !@heading.nil?
end

#left_join(right, on, default_right_tuple) ⇒ Object



210
211
212
213
214
215
216
217
# File 'lib/bmg/type.rb', line 210

def left_join(right, on, default_right_tuple)
  join_compatible!(right, on) if typechecked? && knows_attrlist?
  dup.tap{|x|
    x.attrlist  = (knows_attrlist? and right.knows_attrlist?) ? (self.attrlist + right.attrlist).uniq : nil
    x.predicate = Predicate.tautology
    x.keys      = nil
  }
end

#matching(right, on) ⇒ Object



219
220
221
222
# File 'lib/bmg/type.rb', line 219

def matching(right, on)
  join_compatible!(right, on) if typechecked? && knows_attrlist?
  self
end

#minus(other) ⇒ Object



224
225
226
227
228
229
230
231
232
233
# File 'lib/bmg/type.rb', line 224

def minus(other)
  union_compatible!(other, "Minus")
  dup.tap{|x|
    # 1. attributes do not change
    # 2. predicate does not change, we can't strengthen it in any way but it
    #    does not become weaker either
    # 3. we can safely keep all existing keys, but it's not obvious we can
    #    gain some new ones
  }
end

#not_matching(right, on) ⇒ Object



235
236
237
238
# File 'lib/bmg/type.rb', line 235

def not_matching(right, on)
  join_compatible!(right, on) if typechecked? && knows_attrlist?
  self
end

#page(ordering, page_size, options) ⇒ Object



240
241
242
243
# File 'lib/bmg/type.rb', line 240

def page(ordering, page_size, options)
  known_attributes!(ordering.map{|(k,v)| k}) if typechecked? && knows_attrlist?
  self
end

#project(attrlist) ⇒ Object



245
246
247
248
249
250
251
252
# File 'lib/bmg/type.rb', line 245

def project(attrlist)
  known_attributes!(attrlist) if typechecked? && knows_attrlist?
  dup.tap{|x|
    x.attrlist  = attrlist
    x.predicate = Predicate.tautology
    x.keys      = self._keys.project(self, x, attrlist) if knows_keys?
  }
end

#rename(renaming) ⇒ Object



254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
# File 'lib/bmg/type.rb', line 254

def rename(renaming)
  if typechecked? && knows_attrlist?
    known_attributes!(renaming.keys)
    unknown_attributes!(renaming.values)
  end
  new_pred = begin
    self.predicate.rename(renaming)
  rescue Predicate::NotSupportedError => e
    Predicate.tautology
  end
  dup.tap{|x|
    x.attrlist  = self.attrlist.map{|a| renaming[a] || a } if knows_attrlist?
    x.predicate = new_pred
    x.keys      = self._keys.rename(self, x, renaming) if knows_keys?
  }
end

#restrict(predicate) ⇒ Object



271
272
273
274
275
276
277
278
# File 'lib/bmg/type.rb', line 271

def restrict(predicate)
  known_attributes!(predicate.free_variables) if typechecked? && knows_attrlist?
  dup.tap{|x|
    ### attrlist stays the same
    x.predicate = self.predicate & predicate
    x.keys      = self._keys.restrict(self, x, predicate) if knows_keys?
  }
end

#summarize(by, summarization) ⇒ Object



280
281
282
283
284
285
# File 'lib/bmg/type.rb', line 280

def summarize(by, summarization)
  dup.tap{|x|
    x.attrlist = by + summarization.keys
    x.keys     = Keys.new([by])
  }
end

#to_attrlistObject



88
89
90
# File 'lib/bmg/type.rb', line 88

def to_attrlist
  self.attrlist
end

#transform(transformation, options = {}) ⇒ Object



287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
# File 'lib/bmg/type.rb', line 287

def transform(transformation, options = {})
  transformer = TupleTransformer.new(transformation)
  if typechecked? && knows_attrlist? && transformer.knows_attrlist?
    known_attributes!(transformer.to_attrlist)
  end
  keys = if options[:key_preserving]
    self._keys
  elsif transformer.knows_attrlist? && knows_keys?
    touched_attrs = transformer.to_attrlist
    keys = self._keys.select{|k| (k & touched_attrs).empty? }
  else
    nil
  end
  pred = if transformer.knows_attrlist?
    attr_list = transformer.to_attrlist
    predicate.and_split(attr_list).last
  else
    Predicate.tautology
  end
  dup.tap{|x|
    x.keys = keys
    x.predicate = pred
  }
end

#typechecked?Boolean

Returns:

  • (Boolean)


21
22
23
# File 'lib/bmg/type.rb', line 21

def typechecked?
  @typechecked
end

#undress(options) ⇒ Object



312
313
314
315
316
# File 'lib/bmg/type.rb', line 312

def undress(options)
  dup.tap{|x|
    x.predicate = Predicate.tautology
  }
end

#ungroup(attrlist) ⇒ Object



318
319
320
321
322
323
324
325
# File 'lib/bmg/type.rb', line 318

def ungroup(attrlist)
  known_attributes!(attrlist) if typechecked? && knows_attrlist?
  dup.tap{|x|
    x.attrlist  = nil
    x.predicate = Predicate.tautology
    x.keys      = nil
  }
end

#union(other) ⇒ Object



327
328
329
330
331
332
333
334
# File 'lib/bmg/type.rb', line 327

def union(other)
  union_compatible!(other, "Union")
  dup.tap{|x|
    ### attrlist stays the same
    x.predicate = self.predicate | predicate
    x.keys      = self._keys.union(self, x, other) if knows_keys?
  }
end

#union_compatible!(other, opname) ⇒ Object



373
374
375
376
377
378
379
380
# File 'lib/bmg/type.rb', line 373

def union_compatible!(other, opname)
  if typechecked? && knows_attrlist? && other.knows_attrlist?
    missing = self.attrlist - other.attrlist
    raise TypeError, "#{opname} requires compatible attribute lists, but the right operand is missing the following attributes: #{missing.join(', ')}" unless missing.empty?
    extra = other.attrlist - self.attrlist
    raise TypeError, "#{opname} requires compatible attribute lists, but the left operand is missing the following attributes: #{extra.join(', ')}" unless extra.empty?
  end
end

#unknown_attributes!(attrs) ⇒ Object

Raises:



352
353
354
355
# File 'lib/bmg/type.rb', line 352

def unknown_attributes!(attrs)
  clash = self.attrlist & attrs
  raise TypeError, "Existing attributes #{clash.join(', ')}" unless clash.empty?
end

#unwrap(attrlist) ⇒ Object



336
337
338
339
340
341
342
343
# File 'lib/bmg/type.rb', line 336

def unwrap(attrlist)
  known_attributes!(attrlist) if typechecked? && knows_attrlist?
  dup.tap{|x|
    x.attrlist  = nil
    x.predicate = predicate.and_split(attrlist).last
    x.keys      = self._keys.unwrap(self, x, attrlist) if knows_keys?
  }
end

#with_attrlist(attrlist) ⇒ Object



74
75
76
77
78
# File 'lib/bmg/type.rb', line 74

def with_attrlist(attrlist)
  dup.tap{|x|
    x.attrlist = attrlist
  }
end

#with_heading(heading) ⇒ Object



53
54
55
56
57
58
# File 'lib/bmg/type.rb', line 53

def with_heading(heading)
  dup.tap{|x|
    x.heading = heading
    x.attrlist = heading.keys
  }
end

#with_keys(keys) ⇒ Object



108
109
110
111
112
# File 'lib/bmg/type.rb', line 108

def with_keys(keys)
  dup.tap{|x|
    x.keys = keys ? Keys.new(keys) : nil
  }
end

#with_predicate(predicate) ⇒ Object



42
43
44
45
46
# File 'lib/bmg/type.rb', line 42

def with_predicate(predicate)
  dup.tap{|x|
    x.predicate = predicate
  }
end

#with_typecheckObject



25
26
27
28
29
# File 'lib/bmg/type.rb', line 25

def with_typecheck
  dup.tap{|x|
    x.typechecked = true
  }
end

#without_typecheckObject



31
32
33
34
35
# File 'lib/bmg/type.rb', line 31

def without_typecheck
  dup.tap{|x|
    x.typechecked = false
  }
end