Class: Appear::Util::Join

Inherits:
Object
  • Object
show all
Defined in:
lib/appear/util/join.rb

Overview

Class for joining objects based on hash value or method value.

See Also:

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(*objs) ⇒ Join

A Join is a union of data objects. You can use a Join to group objects of different types, so that you may read from whichever has a given field.

It is more useful to use self.join to perform a join operation on collections than to create Join objects directly.



81
82
83
# File 'lib/appear/util/join.rb', line 81

def initialize(*objs)
  @objs = objs
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(method, *args, &block) ⇒ Object

the #method_missing implementation on a Join allows you to access valid fields with regular accessors.

Raises:

  • (NoMethodError)


129
130
131
132
133
134
# File 'lib/appear/util/join.rb', line 129

def method_missing(method, *args, &block)
  raise NoMethodError.new("Cannot access #{method.inspect}") unless respond_to?(method)
  raise ArgumentError.new("Passed args to accessor") if args.length > 0
  raise ArgumentError.new("Passed block to accessor") if block
  self[method]
end

Class Method Details

.access(obj, field) ⇒ Any

Access the given field on an object. Raises an error if the field cannot be accessed.



66
67
68
69
70
71
72
73
74
# File 'lib/appear/util/join.rb', line 66

def self.access(obj, field)
  if obj.respond_to?(field)
    obj.send(field)
  elsif obj.respond_to?(:[])
    obj[field]
  else
    raise "cannot access #{field.inspect} on #{obj.inspect}"
  end
end

.can_access?(obj, field) ⇒ Boolean

True if we can access the given field on an object, either by calling that method on the object, or by accessing using []



51
52
53
54
55
56
57
58
# File 'lib/appear/util/join.rb', line 51

def self.can_access?(obj, field)
  if obj.respond_to?(field)
    return true
  elsif obj.respond_to?(:[])
    return true
  end
  return false
end

.join(field, *tables) ⇒ Array<Join>

Join objects or hashes together where their field values match. This method is analogous to a JOIN in SQL, although the behavior is not exactly the same.

foo_bars is an array of Join instances. Reads from a foo_bar will read first from the foo, and then from the bar - this is based on the order of “tables” passed to Join.join().

Examples:

foos = many_foos
bars = many_bars
foo_bars = Join.join(:common_attribute, foos, bars)

# can still access all the properties on either a foo or a bar
foo_bars.first.common_attribute

# can access attributes by symbol, too
foo_bars.first[:something_else]


29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# File 'lib/appear/util/join.rb', line 29

def self.join(field, *tables)
  by_field = Hash.new { |h, k| h[k] = self.new }

  tables.each do |table|
    table.each do |row|
      field_value = access(row, field)
      joined = by_field[field_value]
      joined.push!(row)
    end
  end

  by_field.values.select do |joined|
    joined.joined_count >= tables.length
  end
end

Instance Method Details

#[](sym) ⇒ Any?

read a field from the join. Returns the first non-nil value we can read.

See Also:

  • for information about how fields are accessed.


110
111
112
113
114
115
116
117
118
119
120
121
# File 'lib/appear/util/join.rb', line 110

def [](sym)
  result = nil

  @objs.each do |obj|
    if self.class.can_access?(obj, sym)
      result = self.class.access(obj, sym)
    end
    break unless result.nil?
  end

  result
end

#joined_countFixnum

get the number of objects in this join



95
96
97
# File 'lib/appear/util/join.rb', line 95

def joined_count
  @objs.length
end

#push!(obj) ⇒ Object

add another data object to this join.



88
89
90
# File 'lib/appear/util/join.rb', line 88

def push!(obj)
  @objs << obj
end

#respond_to?(sym, priv = false) ⇒ Boolean



139
140
141
# File 'lib/appear/util/join.rb', line 139

def respond_to?(sym, priv = false)
  super(sym, priv) || (@objs.any? { |o| self.class.can_access?(o, sym) })
end

#unjoin {|Object| ... } ⇒ Object

Return the first member in the join that matches the given block.

Yields:

  • (Object)

    join member.



101
102
103
# File 'lib/appear/util/join.rb', line 101

def unjoin(&block)
  @objs.find(&block)
end