phlexi-display 0.0.2 → 0.0.4
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.
- checksums.yaml +4 -4
- data/lib/phlexi/display/base.rb +37 -30
- data/lib/phlexi/display/builder.rb +174 -0
- data/lib/phlexi/display/components/association.rb +41 -0
- data/lib/phlexi/display/components/base.rb +2 -29
- data/lib/phlexi/display/components/color.rb +20 -0
- data/lib/phlexi/display/components/concerns/displays_value.rb +5 -19
- data/lib/phlexi/display/components/date_time.rb +14 -14
- data/lib/phlexi/display/components/email.rb +43 -0
- data/lib/phlexi/display/components/enum.rb +17 -0
- data/lib/phlexi/display/components/integer.rb +17 -0
- data/lib/phlexi/display/components/json.rb +25 -0
- data/lib/phlexi/display/components/password.rb +23 -0
- data/lib/phlexi/display/components/placeholder.rb +1 -1
- data/lib/phlexi/display/components/time.rb +15 -0
- data/lib/phlexi/display/components/url.rb +44 -0
- data/lib/phlexi/display/html.rb +15 -0
- data/lib/phlexi/display/options/inferred_types.rb +29 -0
- data/lib/phlexi/display/theme.rb +35 -0
- data/lib/phlexi/display/version.rb +1 -1
- data/lib/phlexi/display.rb +4 -6
- metadata +29 -17
- data/lib/phlexi/display/field_options/associations.rb +0 -21
- data/lib/phlexi/display/field_options/attachments.rb +0 -21
- data/lib/phlexi/display/field_options/description.rb +0 -22
- data/lib/phlexi/display/field_options/hints.rb +0 -22
- data/lib/phlexi/display/field_options/inferred_types.rb +0 -129
- data/lib/phlexi/display/field_options/labels.rb +0 -28
- data/lib/phlexi/display/field_options/placeholders.rb +0 -18
- data/lib/phlexi/display/field_options/themes.rb +0 -132
- data/lib/phlexi/display/option_mapper.rb +0 -154
- data/lib/phlexi/display/structure/dom.rb +0 -42
- data/lib/phlexi/display/structure/field_builder.rb +0 -160
- data/lib/phlexi/display/structure/field_collection.rb +0 -39
- data/lib/phlexi/display/structure/namespace.rb +0 -123
- data/lib/phlexi/display/structure/namespace_collection.rb +0 -40
- data/lib/phlexi/display/structure/node.rb +0 -24
@@ -1,154 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Phlexi
|
4
|
-
module Display
|
5
|
-
# OptionMapper is responsible for converting a collection of objects into a hash of options
|
6
|
-
# suitable for form controls, such as `select > options`.
|
7
|
-
# Both values and labels are converted to strings.
|
8
|
-
#
|
9
|
-
# @example Basic usage
|
10
|
-
# collection = [["First", 1], ["Second", 2]]
|
11
|
-
# mapper = OptionMapper.new(collection)
|
12
|
-
# mapper.each { |value, label| puts "#{value}: #{label}" }
|
13
|
-
#
|
14
|
-
# @example Using with ActiveRecord objects
|
15
|
-
# users = User.all
|
16
|
-
# mapper = OptionMapper.new(users)
|
17
|
-
# mapper.each { |id, name| puts "#{id}: #{name}" }
|
18
|
-
#
|
19
|
-
# @example Array access with different value types
|
20
|
-
# mapper = OptionMapper.new([["Integer", 1], ["String", "2"], ["Symbol", :three]])
|
21
|
-
# puts mapper["1"] # Output: "Integer"
|
22
|
-
# puts mapper["2"] # Output: "String"
|
23
|
-
# puts mapper["three"] # Output: "Symbol"
|
24
|
-
#
|
25
|
-
# @note This class is thread-safe as it doesn't maintain mutable state.
|
26
|
-
class OptionMapper
|
27
|
-
include Enumerable
|
28
|
-
|
29
|
-
# Initializes a new OptionMapper instance.
|
30
|
-
#
|
31
|
-
# @param collection [#call, #to_a] The collection to be mapped.
|
32
|
-
# @param label_method [Symbol, nil] The method to call on each object to get the label.
|
33
|
-
# @param value_method [Symbol, nil] The method to call on each object to get the value.
|
34
|
-
def initialize(collection, label_method: nil, value_method: nil)
|
35
|
-
@raw_collection = collection
|
36
|
-
@label_method = label_method
|
37
|
-
@value_method = value_method
|
38
|
-
end
|
39
|
-
|
40
|
-
# Iterates over the collection, yielding value-label pairs.
|
41
|
-
#
|
42
|
-
# @yieldparam value [String] The string value for the current item.
|
43
|
-
# @yieldparam label [String] The string label for the current item.
|
44
|
-
# @return [Enumerator] If no block is given.
|
45
|
-
def each(&)
|
46
|
-
collection.each(&)
|
47
|
-
end
|
48
|
-
|
49
|
-
# @return [Array<String>] An array of all labels in the collection.
|
50
|
-
def labels
|
51
|
-
collection.values
|
52
|
-
end
|
53
|
-
|
54
|
-
# @return [Array<String>] An array of all values in the collection.
|
55
|
-
def values
|
56
|
-
collection.keys
|
57
|
-
end
|
58
|
-
|
59
|
-
# Retrieves the label for a given value.
|
60
|
-
#
|
61
|
-
# @param value [#to_s] The value to look up.
|
62
|
-
# @return [String, nil] The label corresponding to the value, or nil if not found.
|
63
|
-
def [](value)
|
64
|
-
collection[value.to_s]
|
65
|
-
end
|
66
|
-
|
67
|
-
private
|
68
|
-
|
69
|
-
# @return [Hash<String, String>] The materialized collection as a hash of string value => string label.
|
70
|
-
def collection
|
71
|
-
@collection ||= materialize_collection(@raw_collection)
|
72
|
-
end
|
73
|
-
|
74
|
-
# Converts the raw collection into a materialized hash.
|
75
|
-
#
|
76
|
-
# @param collection [#call, #to_a] The collection to be materialized.
|
77
|
-
# @return [Hash<String, String>] The materialized collection as a hash of string value => string label.
|
78
|
-
# @raise [ArgumentError] If the collection cannot be materialized into an enumerable.
|
79
|
-
def materialize_collection(collection)
|
80
|
-
case collection
|
81
|
-
in Hash => hash
|
82
|
-
hash.transform_keys(&:to_s).transform_values(&:to_s)
|
83
|
-
in Array => arr
|
84
|
-
array_to_hash(arr)
|
85
|
-
in Range => range
|
86
|
-
range_to_hash(range)
|
87
|
-
in Proc => proc
|
88
|
-
materialize_collection(proc.call)
|
89
|
-
in Symbol
|
90
|
-
raise ArgumentError, "Symbol collections are not supported in this context"
|
91
|
-
in Set => set
|
92
|
-
array_to_hash(set.to_a)
|
93
|
-
else
|
94
|
-
array_to_hash(Array(collection))
|
95
|
-
end
|
96
|
-
rescue ArgumentError
|
97
|
-
# Rails.logger.warn("Unhandled inclusion collection type: #{e}")
|
98
|
-
{}
|
99
|
-
end
|
100
|
-
|
101
|
-
# Converts an array to a hash using detected or specified methods.
|
102
|
-
#
|
103
|
-
# @param array [Array] The array to convert.
|
104
|
-
# @return [Hash<String, String>] The resulting hash of string value => string label.
|
105
|
-
def array_to_hash(array)
|
106
|
-
sample = array.first || array.last
|
107
|
-
methods = detect_methods_for_sample(sample)
|
108
|
-
|
109
|
-
array.each_with_object({}) do |item, hash|
|
110
|
-
value = item.public_send(methods[:value]).to_s
|
111
|
-
label = item.public_send(methods[:label]).to_s
|
112
|
-
hash[value] = label
|
113
|
-
end
|
114
|
-
end
|
115
|
-
|
116
|
-
# Converts a range to a hash.
|
117
|
-
#
|
118
|
-
# @param range [Range] The range to convert.
|
119
|
-
# @return [Hash<String, String>] The range converted to a hash of string value => string label.
|
120
|
-
# @raise [ArgumentError] If the range is unbounded.
|
121
|
-
def range_to_hash(range)
|
122
|
-
raise ArgumentError, "Cannot safely materialize an unbounded range" if range.begin.nil? || range.end.nil?
|
123
|
-
|
124
|
-
range.each_with_object({}) { |value, hash| hash[value.to_s] = value.to_s }
|
125
|
-
end
|
126
|
-
|
127
|
-
# Detects suitable methods for label and value from a sample object.
|
128
|
-
#
|
129
|
-
# @param sample [Object] A sample object from the collection.
|
130
|
-
# @return [Hash{Symbol => Symbol}] A hash containing :label and :value keys with corresponding method names.
|
131
|
-
def detect_methods_for_sample(sample)
|
132
|
-
case sample
|
133
|
-
when Array
|
134
|
-
{value: :last, label: :first}
|
135
|
-
else
|
136
|
-
{
|
137
|
-
value: @value_method || collection_value_methods.find { |m| sample.respond_to?(m) },
|
138
|
-
label: @label_method || collection_label_methods.find { |m| sample.respond_to?(m) }
|
139
|
-
}
|
140
|
-
end
|
141
|
-
end
|
142
|
-
|
143
|
-
# @return [Array<Symbol>] An array of method names to try for collection values.
|
144
|
-
def collection_value_methods
|
145
|
-
@collection_value_methods ||= %i[id to_s].freeze
|
146
|
-
end
|
147
|
-
|
148
|
-
# @return [Array<Symbol>] An array of method names to try for collection labels.
|
149
|
-
def collection_label_methods
|
150
|
-
@collection_label_methods ||= %i[to_label name title to_s].freeze
|
151
|
-
end
|
152
|
-
end
|
153
|
-
end
|
154
|
-
end
|
@@ -1,42 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Phlexi
|
4
|
-
module Display
|
5
|
-
module Structure
|
6
|
-
# Generates DOM IDs for a Field, Namespace, or Node based on
|
7
|
-
# norms that were established by Rails. These can be used outside of Rails in
|
8
|
-
# other Ruby web frameworks since it has no dependencies on Rails.
|
9
|
-
class DOM
|
10
|
-
def initialize(field:)
|
11
|
-
@field = field
|
12
|
-
end
|
13
|
-
|
14
|
-
# Converts the value of the field to a String, which is required to work
|
15
|
-
# with Phlex. Assumes that `Object#to_s` emits a format suitable for display.
|
16
|
-
def value
|
17
|
-
@field.value.to_s
|
18
|
-
end
|
19
|
-
|
20
|
-
# Walks from the current node to the parent node, grabs the names, and separates
|
21
|
-
# them with a `_` for a DOM ID.
|
22
|
-
def id
|
23
|
-
@id ||= begin
|
24
|
-
root, *rest = lineage
|
25
|
-
root_key = root.respond_to?(:dom_id) ? root.dom_id : root.key
|
26
|
-
rest.map(&:key).unshift(root_key).join("_")
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
# One-liner way of walking from the current node all the way up to the parent.
|
31
|
-
def lineage
|
32
|
-
@lineage ||= Enumerator.produce(@field, &:parent).take_while(&:itself).reverse
|
33
|
-
end
|
34
|
-
|
35
|
-
# Emit the id and value in an HTML tag-ish that doesn't have an element.
|
36
|
-
def inspect
|
37
|
-
"<#{self.class.name} id=#{id.inspect} value=#{value.inspect}/>"
|
38
|
-
end
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
@@ -1,160 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "phlex"
|
4
|
-
|
5
|
-
module Phlexi
|
6
|
-
module Display
|
7
|
-
module Structure
|
8
|
-
# FieldBuilder class is responsible for building display fields with various options and components.
|
9
|
-
#
|
10
|
-
# @attr_reader [Structure::DOM] dom The DOM structure for the field.
|
11
|
-
# @attr_reader [Hash] options Options for the field.
|
12
|
-
# @attr_reader [Object] object The object associated with the field.
|
13
|
-
# @attr_reader [Hash] attributes Attributes for the field.
|
14
|
-
# @attr_accessor [Object] value The value of the field.
|
15
|
-
class FieldBuilder < Node
|
16
|
-
include Phlex::Helpers
|
17
|
-
include FieldOptions::Themes
|
18
|
-
include FieldOptions::Associations
|
19
|
-
include FieldOptions::Attachments
|
20
|
-
include FieldOptions::InferredTypes
|
21
|
-
include FieldOptions::Labels
|
22
|
-
include FieldOptions::Placeholders
|
23
|
-
include FieldOptions::Description
|
24
|
-
# include FieldOptions::Hints
|
25
|
-
|
26
|
-
attr_reader :dom, :options, :object, :value
|
27
|
-
|
28
|
-
# Initializes a new FieldBuilder instance.
|
29
|
-
#
|
30
|
-
# @param key [Symbol, String] The key for the field.
|
31
|
-
# @param parent [Structure::Namespace] The parent object.
|
32
|
-
# @param object [Object, nil] The associated object.
|
33
|
-
# @param value [Object] The initial value for the field.
|
34
|
-
# @param options [Hash] Additional options for the field.
|
35
|
-
def initialize(key, parent:, object: nil, value: NIL_VALUE, **options)
|
36
|
-
super(key, parent: parent)
|
37
|
-
|
38
|
-
@object = object
|
39
|
-
@value = determine_value(value)
|
40
|
-
@options = options
|
41
|
-
@dom = Structure::DOM.new(field: self)
|
42
|
-
end
|
43
|
-
|
44
|
-
# Creates a label tag for the field.
|
45
|
-
#
|
46
|
-
# @param attributes [Hash] Additional attributes for the label.
|
47
|
-
# @return [Components::Label] The label component.
|
48
|
-
def label_tag(**attributes, &)
|
49
|
-
create_component(Components::Label, :label, **attributes, &)
|
50
|
-
end
|
51
|
-
|
52
|
-
# Creates a Placeholder tag for the field.
|
53
|
-
#
|
54
|
-
# @param attributes [Hash] Additional attributes for the placeholder.
|
55
|
-
# @return [Components::Placeholder] The placeholder component.
|
56
|
-
def placeholder_tag(**attributes, &)
|
57
|
-
create_component(Components::Placeholder, :placeholder, **attributes, &)
|
58
|
-
end
|
59
|
-
|
60
|
-
# Creates a Description tag for the field.
|
61
|
-
#
|
62
|
-
# @param attributes [Hash] Additional attributes for the description.
|
63
|
-
# @return [Components::Description] The description component.
|
64
|
-
def description_tag(**attributes, &)
|
65
|
-
create_component(Components::Description, :description, **attributes, &)
|
66
|
-
end
|
67
|
-
|
68
|
-
# Creates a string display tag for the field.
|
69
|
-
#
|
70
|
-
# @param attributes [Hash] Additional attributes for the string display.
|
71
|
-
# @return [Components::String] The string component.
|
72
|
-
def string_tag(**attributes, &)
|
73
|
-
create_component(Components::String, :string, **attributes, &)
|
74
|
-
end
|
75
|
-
|
76
|
-
# # Creates a text display tag for the field.
|
77
|
-
# #
|
78
|
-
# # @param attributes [Hash] Additional attributes for the text display.
|
79
|
-
# # @return [Components::Text] The text component.
|
80
|
-
# def text_tag(**attributes, &)
|
81
|
-
# create_component(Components::Text, :text, **attributes, &)
|
82
|
-
# end
|
83
|
-
|
84
|
-
# Creates a number display tag for the field.
|
85
|
-
#
|
86
|
-
# @param attributes [Hash] Additional attributes for the number display.
|
87
|
-
# @return [Components::Number] The number component.
|
88
|
-
def number_tag(**attributes, &)
|
89
|
-
create_component(Components::Number, :number, **attributes, &)
|
90
|
-
end
|
91
|
-
|
92
|
-
# Creates a datetime display for the field.
|
93
|
-
#
|
94
|
-
# @param attributes [Hash] Additional attributes for the datetime display.
|
95
|
-
# @return [Components::DateTime] The datetime component.
|
96
|
-
def datetime_tag(**attributes, &)
|
97
|
-
create_component(Components::DateTime, :datetime, **attributes, &)
|
98
|
-
end
|
99
|
-
|
100
|
-
# # Creates a boolean display tag for the field.
|
101
|
-
# #
|
102
|
-
# # @param attributes [Hash] Additional attributes for the boolean display.
|
103
|
-
# # @return [Components::Boolean] The boolean component.
|
104
|
-
# def boolean_tag(**attributes, &)
|
105
|
-
# create_component(Components::Boolean, :boolean, **attributes, &)
|
106
|
-
# end
|
107
|
-
|
108
|
-
# # Creates an association display tag for the field.
|
109
|
-
# #
|
110
|
-
# # @param attributes [Hash] Additional attributes for the association display.
|
111
|
-
# # @return [Components::Association] The association component.
|
112
|
-
# def association_tag(**attributes, &)
|
113
|
-
# create_component(Components::Association, :association, **attributes, &)
|
114
|
-
# end
|
115
|
-
|
116
|
-
# # Creates an attachment display tag for the field.
|
117
|
-
# #
|
118
|
-
# # @param attributes [Hash] Additional attributes for the attachment display.
|
119
|
-
# # @return [Components::Attachment] The attachment component.
|
120
|
-
# def attachment_tag(**attributes, &)
|
121
|
-
# create_component(Components::Attachment, :attachment, **attributes, &)
|
122
|
-
# end
|
123
|
-
|
124
|
-
# Wraps the field with additional markup.
|
125
|
-
#
|
126
|
-
# @param attributes [Hash] Additional attributes for the wrapper.
|
127
|
-
# @yield [block] The block to be executed within the wrapper.
|
128
|
-
# @return [Components::Wrapper] The wrapper component.
|
129
|
-
def wrapped(**attributes, &)
|
130
|
-
create_component(Components::Wrapper, :wrapper, **attributes, &)
|
131
|
-
end
|
132
|
-
|
133
|
-
# Creates a repeated field collection.
|
134
|
-
#
|
135
|
-
# @param range [#each] The collection of items to generate displays for.
|
136
|
-
# @yield [block] The block to be executed for each item in the collection.
|
137
|
-
# @return [FieldCollection] The field collection.
|
138
|
-
def repeated(collection = [], &)
|
139
|
-
FieldCollection.new(field: self, collection:, &)
|
140
|
-
end
|
141
|
-
|
142
|
-
protected
|
143
|
-
|
144
|
-
def create_component(component_class, theme_key, **attributes, &)
|
145
|
-
component_class.new(self, class: component_class_for(theme_key, attributes), **attributes, &)
|
146
|
-
end
|
147
|
-
|
148
|
-
def component_class_for(theme_key, attributes)
|
149
|
-
attributes.delete(:class) || themed(attributes.key?(:theme) ? attributes.delete(:theme) : theme_key)
|
150
|
-
end
|
151
|
-
|
152
|
-
def determine_value(value)
|
153
|
-
return value unless value == NIL_VALUE
|
154
|
-
|
155
|
-
object.respond_to?(key) ? object.public_send(key) : nil
|
156
|
-
end
|
157
|
-
end
|
158
|
-
end
|
159
|
-
end
|
160
|
-
end
|
@@ -1,39 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Phlexi
|
4
|
-
module Display
|
5
|
-
module Structure
|
6
|
-
class FieldCollection
|
7
|
-
include Enumerable
|
8
|
-
|
9
|
-
class Builder
|
10
|
-
attr_reader :key, :index
|
11
|
-
|
12
|
-
def initialize(key, field, index)
|
13
|
-
@key = key.to_s
|
14
|
-
@field = field
|
15
|
-
@index = index
|
16
|
-
end
|
17
|
-
|
18
|
-
def field(**)
|
19
|
-
@field.class.new(key, **, parent: @field).tap do |field|
|
20
|
-
yield field if block_given?
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
def initialize(field:, collection:, &)
|
26
|
-
@field = field
|
27
|
-
@collection = collection
|
28
|
-
each(&) if block_given?
|
29
|
-
end
|
30
|
-
|
31
|
-
def each(&)
|
32
|
-
@collection.each.with_index do |item, index|
|
33
|
-
yield Builder.new(item, @field, index)
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|
@@ -1,123 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Phlexi
|
4
|
-
module Display
|
5
|
-
module Structure
|
6
|
-
# A Namespace maps an object to values, but doesn't actually have a value itself. For
|
7
|
-
# example, a `User` object or ActiveRecord model could be passed into the `:user` namespace.
|
8
|
-
#
|
9
|
-
# To access single values on a Namespace, #field can be used.
|
10
|
-
#
|
11
|
-
# To access nested objects within a namespace, two methods are available:
|
12
|
-
#
|
13
|
-
# 1. #nest_one: Used for single nested objects, such as if a `User belongs_to :profile` in
|
14
|
-
# ActiveRecord. This method returns another Namespace object.
|
15
|
-
#
|
16
|
-
# 2. #nest_many: Used for collections of nested objects, such as if a `User has_many :addresses` in
|
17
|
-
# ActiveRecord. This method returns a NamespaceCollection object.
|
18
|
-
class Namespace < Structure::Node
|
19
|
-
include Enumerable
|
20
|
-
|
21
|
-
attr_reader :builder_klass, :object
|
22
|
-
|
23
|
-
def initialize(key, parent:, builder_klass:, object: nil)
|
24
|
-
super(key, parent: parent)
|
25
|
-
@builder_klass = builder_klass
|
26
|
-
@object = object
|
27
|
-
@children = {}
|
28
|
-
yield self if block_given?
|
29
|
-
end
|
30
|
-
|
31
|
-
def field(key, **attributes)
|
32
|
-
create_child(key, attributes.delete(:builder_klass) || builder_klass, object: object, **attributes).tap do |field|
|
33
|
-
yield field if block_given?
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
# Creates a `Namespace` child instance with the parent set to the current instance, adds to
|
38
|
-
# the `@children` Hash to ensure duplicate child namespaces aren't created, then calls the
|
39
|
-
# method on the `@object` to get the child object to pass into that namespace.
|
40
|
-
#
|
41
|
-
# For example, if a `User#permission` returns a `Permission` object, we could map that to a
|
42
|
-
# display like this:
|
43
|
-
#
|
44
|
-
# ```ruby
|
45
|
-
# Phlexi::Display(user) do |display|
|
46
|
-
# display.nest_one :profile do |profile|
|
47
|
-
# render profile.field(:gender).text
|
48
|
-
# end
|
49
|
-
# end
|
50
|
-
# ```
|
51
|
-
def nest_one(key, object: nil, &)
|
52
|
-
object ||= object_value_for(key: key)
|
53
|
-
create_child(key, self.class, object:, builder_klass:, &)
|
54
|
-
end
|
55
|
-
|
56
|
-
# Wraps an array of objects in Namespace classes. For example, if `User#addresses` returns
|
57
|
-
# an enumerable or array of `Address` classes:
|
58
|
-
#
|
59
|
-
# ```ruby
|
60
|
-
# Phlexi::Display(user) do |display|
|
61
|
-
# render display.field(:email).text
|
62
|
-
# render display.field(:name).text
|
63
|
-
# display.nest_many :addresses do |address|
|
64
|
-
# render address.field(:street).text
|
65
|
-
# render address.field(:state).text
|
66
|
-
# render address.field(:zip).text
|
67
|
-
# end
|
68
|
-
# end
|
69
|
-
# ```
|
70
|
-
# The object within the block is a `Namespace` object that maps each object within the enumerable
|
71
|
-
# to another `Namespace` or `Field`.
|
72
|
-
def nest_many(key, collection: nil, &)
|
73
|
-
collection ||= Array(object_value_for(key: key))
|
74
|
-
create_child(key, NamespaceCollection, collection:, &)
|
75
|
-
end
|
76
|
-
|
77
|
-
# Iterates through the children of the current namespace, which could be `Namespace` or `Field`
|
78
|
-
# objects.
|
79
|
-
def each(&)
|
80
|
-
@children.values.each(&)
|
81
|
-
end
|
82
|
-
|
83
|
-
def dom_id
|
84
|
-
@dom_id ||= begin
|
85
|
-
id = if object.nil?
|
86
|
-
nil
|
87
|
-
elsif object.class.respond_to?(:primary_key)
|
88
|
-
object.public_send(object.class.primary_key) || :new
|
89
|
-
elsif object.respond_to?(:id)
|
90
|
-
object.id || :new
|
91
|
-
end
|
92
|
-
[key, id].compact.join("_").underscore
|
93
|
-
end
|
94
|
-
end
|
95
|
-
|
96
|
-
# Creates a root Namespace
|
97
|
-
def self.root(*, builder_klass:, **, &)
|
98
|
-
new(*, parent: nil, builder_klass:, **, &)
|
99
|
-
end
|
100
|
-
|
101
|
-
protected
|
102
|
-
|
103
|
-
# Calls the corresponding method on the object for the `key` name, if it exists. For example
|
104
|
-
# if the `key` is `email` on `User`, this method would call `User#email` if the method is
|
105
|
-
# present.
|
106
|
-
#
|
107
|
-
# This method could be overwritten if the mapping between the `@object` and `key` name is not
|
108
|
-
# a method call. For example, a `Hash` would be accessed via `user[:email]` instead of `user.send(:email)`
|
109
|
-
def object_value_for(key:)
|
110
|
-
@object.send(key) if @object.respond_to? key
|
111
|
-
end
|
112
|
-
|
113
|
-
private
|
114
|
-
|
115
|
-
# Checks if the child exists. If it does then it returns that. If it doesn't, it will
|
116
|
-
# build the child.
|
117
|
-
def create_child(key, child_class, **kwargs, &block)
|
118
|
-
@children.fetch(key) { @children[key] = child_class.new(key, parent: self, **kwargs, &block) }
|
119
|
-
end
|
120
|
-
end
|
121
|
-
end
|
122
|
-
end
|
123
|
-
end
|
@@ -1,40 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Phlexi
|
4
|
-
module Display
|
5
|
-
module Structure
|
6
|
-
class NamespaceCollection < Node
|
7
|
-
include Enumerable
|
8
|
-
|
9
|
-
def initialize(key, parent:, collection: nil, &block)
|
10
|
-
raise ArgumentError, "block is required" unless block.present?
|
11
|
-
|
12
|
-
super(key, parent: parent)
|
13
|
-
|
14
|
-
@collection = collection
|
15
|
-
@block = block
|
16
|
-
each(&block)
|
17
|
-
end
|
18
|
-
|
19
|
-
private
|
20
|
-
|
21
|
-
def each(&)
|
22
|
-
namespaces.each(&)
|
23
|
-
end
|
24
|
-
|
25
|
-
# Builds and memoizes namespaces for the collection.
|
26
|
-
#
|
27
|
-
# @return [Array<Namespace>] An array of namespace objects.
|
28
|
-
def namespaces
|
29
|
-
@namespaces ||= @collection.map.with_index do |object, key|
|
30
|
-
build_namespace(key, object: object)
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
def build_namespace(index, **)
|
35
|
-
parent.class.new(index, parent: self, builder_klass: parent.builder_klass, **)
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|
40
|
-
end
|
@@ -1,24 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Phlexi
|
4
|
-
module Display
|
5
|
-
module Structure
|
6
|
-
# Superclass for Namespace and Field classes. Represents a node in the display tree structure.
|
7
|
-
#
|
8
|
-
# @attr_reader [Symbol] key The node's key
|
9
|
-
# @attr_reader [Node, nil] parent The node's parent in the tree structure
|
10
|
-
class Node
|
11
|
-
attr_reader :key, :parent
|
12
|
-
|
13
|
-
# Initializes a new Node instance.
|
14
|
-
#
|
15
|
-
# @param key [Symbol, String] The key for the node
|
16
|
-
# @param parent [Node, nil] The parent node
|
17
|
-
def initialize(key, parent:)
|
18
|
-
@key = :"#{key}"
|
19
|
-
@parent = parent
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|