Class: Bmg::Type

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

Constant Summary collapse

ANY =
Type.new

Instance Attribute 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.



46
47
48
# File 'lib/bmg/type.rb', line 46

def attrlist
  @attrlist
end

#keysObject

Returns the value of attribute keys.



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

def keys
  @keys
end

#predicateObject

Returns the value of attribute predicate.



35
36
37
# File 'lib/bmg/type.rb', line 35

def predicate
  @predicate
end

Instance Method Details

#[](attribute) ⇒ Object



96
97
98
# File 'lib/bmg/type.rb', line 96

def [](attribute)
  ANY
end

#allbut(butlist) ⇒ Object



102
103
104
105
106
107
108
109
# File 'lib/bmg/type.rb', line 102

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



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

def attrlist!
  knows_attrlist!
  attrlist
end

#autosummarize(by, summarization, options) ⇒ Object



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

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



118
119
120
121
122
123
124
125
126
127
128
# File 'lib/bmg/type.rb', line 118

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



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

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



340
341
342
343
344
345
# File 'lib/bmg/type.rb', line 340

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



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

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



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

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)


111
112
113
114
115
116
# File 'lib/bmg/type.rb', line 111

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



169
170
171
172
173
174
175
176
177
178
179
# File 'lib/bmg/type.rb', line 169

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



181
182
183
184
185
186
187
188
# File 'lib/bmg/type.rb', line 181

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:



331
332
333
334
335
336
337
338
# File 'lib/bmg/type.rb', line 331

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:



321
322
323
324
# File 'lib/bmg/type.rb', line 321

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

#knows_attrlist!Object



64
65
66
# File 'lib/bmg/type.rb', line 64

def knows_attrlist!
  raise UnknownAttributesError unless knows_attrlist?
end

#knows_attrlist?Boolean

Returns:

  • (Boolean)


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

def knows_attrlist?
  !self.attrlist.nil?
end

#knows_keys?Boolean

Returns:

  • (Boolean)


78
79
80
# File 'lib/bmg/type.rb', line 78

def knows_keys?
  !!@keys
end

#left_join(right, on, default_right_tuple) ⇒ Object



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

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



199
200
201
202
# File 'lib/bmg/type.rb', line 199

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

#minus(other) ⇒ Object



299
300
301
302
303
304
305
306
307
308
# File 'lib/bmg/type.rb', line 299

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



204
205
206
207
# File 'lib/bmg/type.rb', line 204

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

#page(ordering, page_size, options) ⇒ Object



209
210
211
212
# File 'lib/bmg/type.rb', line 209

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

#project(attrlist) ⇒ Object



214
215
216
217
218
219
220
221
# File 'lib/bmg/type.rb', line 214

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



223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
# File 'lib/bmg/type.rb', line 223

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



240
241
242
243
244
245
246
247
# File 'lib/bmg/type.rb', line 240

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



249
250
251
252
253
254
# File 'lib/bmg/type.rb', line 249

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

#to_attrlistObject



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

def to_attrlist
  self.attrlist
end

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



256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
# File 'lib/bmg/type.rb', line 256

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)


17
18
19
# File 'lib/bmg/type.rb', line 17

def typechecked?
  @typechecked
end

#ungroup(attrlist) ⇒ Object



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

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



290
291
292
293
294
295
296
297
# File 'lib/bmg/type.rb', line 290

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



347
348
349
350
351
352
353
354
# File 'lib/bmg/type.rb', line 347

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:



326
327
328
329
# File 'lib/bmg/type.rb', line 326

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

#unwrap(attrlist) ⇒ Object



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

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



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

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

#with_keys(keys) ⇒ Object



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

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

#with_predicate(predicate) ⇒ Object



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

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

#with_typecheckObject



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

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

#without_typecheckObject



27
28
29
30
31
# File 'lib/bmg/type.rb', line 27

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