Class: Praxis::FieldExpander

Inherits:
Object
  • Object
show all
Defined in:
lib/praxis/field_expander.rb

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(displayable_filter: nil) ⇒ FieldExpander

displayable_filter is a proc that takes a set of strings (representing privileges of sort) and returns true if it should be displayed



18
19
20
21
22
23
24
25
26
# File 'lib/praxis/field_expander.rb', line 18

def initialize(displayable_filter: nil)
  @stack = Hash.new do |hash, key|
    hash[key] = Set.new
  end
  @history = Hash.new do |hash, key|
    hash[key] = {}
  end
  @displayable_filter = displayable_filter
end

Instance Attribute Details

#historyObject (readonly)

Returns the value of attribute history.



15
16
17
# File 'lib/praxis/field_expander.rb', line 15

def history
  @history
end

#stackObject (readonly)

Returns the value of attribute stack.



15
16
17
# File 'lib/praxis/field_expander.rb', line 15

def stack
  @stack
end

Class Method Details

.expand(object, fields = true, displayable_filter = nil) ⇒ Object



5
6
7
8
9
10
11
12
13
# File 'lib/praxis/field_expander.rb', line 5

def self.expand(object, fields = true, displayable_filter = nil)
  # check the displayability of the whole type/attr object at the top as well, not just the inner ones
  if (privs = object.options[:displayable])
    raise 'Attempting to expand fields for a type that uses :displayable, but the system (or at least this controller) does not have a displayable_filter setup.' unless privs && displayable_filter
    return {} unless displayable_filter.call(Array(privs))

  end
  new(displayable_filter: displayable_filter).expand(object, fields)
end

Instance Method Details

#expand(object, fields = true) ⇒ Object



28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# File 'lib/praxis/field_expander.rb', line 28

def expand(object, fields = true)
  if stack[object].include? fields
    return history[object][fields] if history[object].include? fields

    # We should probably never get here, since we should have a record
    # of the history of an expansion if we're trying to redo it,
    # but we should also be conservative and raise here just in case.
    raise "Circular expansion detected for object #{object.inspect} with fields #{fields.inspect}"
  else
    stack[object] << fields
  end

  result = if object.is_a? Attributor::Attribute
             expand_type(object.type, fields)
           else
             expand_type(object, fields)
           end

  result
ensure
  stack[object].delete fields
end

#expand_fields(attributes, fields) ⇒ Object

Raises:

  • (ArgumentError)


51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
# File 'lib/praxis/field_expander.rb', line 51

def expand_fields(attributes, fields)
  raise ArgumentError, 'expand_fields must be given a block' unless block_given?

  unless fields == true
    attributes = attributes.select do |k, _v|
      fields.key?(k)
    end
  end

  attributes.each_with_object({}) do |(name, dumpable), hash|
    # Filter out attributes that are not displayable (if a filter is provided and there is a displayable option)
    if (privs = dumpable.options[:displayable])
      raise "Found field named #{name} using :displayable, but the system (or at least this controller) does not have a displayable_filter setup." unless privs && @displayable_filter
      next unless @displayable_filter.call(Array(privs))

    end

    sub_fields = case fields
                 when true
                   true
                 when Hash
                   fields[name] || true
                 end
    hash[name] = yield(dumpable, sub_fields)
  end
end

#expand_type(object, fields = true) ⇒ Object



78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
# File 'lib/praxis/field_expander.rb', line 78

def expand_type(object, fields = true)
  unless object.respond_to?(:attributes)
    return expand_type(object.member_attribute.type, fields) if object.respond_to?(:member_attribute)

    return true
  end

  # just include the full thing if it has no attributes
  return fields if object.attributes.empty?

  # True, expands to the default fieldset for blueprints
  fields = object.default_fieldset if object < Praxis::Blueprint && fields == true

  return history[object][fields] if history[object].include? fields

  history[object][fields] = {}
  result = expand_fields(object.attributes, fields) do |dumpable, sub_fields|
    expand(dumpable.type, sub_fields)
  end
  unless fields == true
    non_matching = fields.keys - object.attributes.keys
    raise "FieldExpansion error: attribute(s) #{non_matching} do not exist in #{object}" unless non_matching.empty?
  end
  history[object][fields].merge!(result)
end