compony 0.2.2 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +2 -1
- data/CHANGELOG.md +23 -0
- data/Gemfile.lock +3 -3
- data/README.md +80 -13
- data/Rakefile +1 -1
- data/VERSION +1 -1
- data/compony.gemspec +5 -5
- data/doc/ComponentGenerator.html +1 -1
- data/doc/Components.html +1 -1
- data/doc/ComponentsGenerator.html +1 -1
- data/doc/Compony/Component.html +308 -341
- data/doc/Compony/ComponentMixins/Default/Labelling.html +1 -1
- data/doc/Compony/ComponentMixins/Default/Standalone/ResourcefulVerbDsl.html +1 -1
- data/doc/Compony/ComponentMixins/Default/Standalone/StandaloneDsl.html +1 -1
- data/doc/Compony/ComponentMixins/Default/Standalone/VerbDsl.html +1 -1
- data/doc/Compony/ComponentMixins/Default/Standalone.html +1 -1
- data/doc/Compony/ComponentMixins/Default.html +1 -1
- data/doc/Compony/ComponentMixins/Resourceful.html +1 -1
- data/doc/Compony/ComponentMixins.html +1 -1
- data/doc/Compony/Components/Button.html +3 -3
- data/doc/Compony/Components/Destroy.html +3 -3
- data/doc/Compony/Components/Edit.html +19 -19
- data/doc/Compony/Components/Form.html +3 -3
- data/doc/Compony/Components/New.html +19 -19
- data/doc/Compony/Components/WithForm.html +4 -4
- data/doc/Compony/Components.html +1 -1
- data/doc/Compony/ControllerMixin.html +1 -1
- data/doc/Compony/Engine.html +1 -1
- data/doc/Compony/MethodAccessibleHash.html +1 -1
- data/doc/Compony/ModelFields/Anchormodel.html +7 -3
- data/doc/Compony/ModelFields/Association.html +1 -1
- data/doc/Compony/ModelFields/Attachment.html +1 -1
- data/doc/Compony/ModelFields/Base.html +1 -1
- data/doc/Compony/ModelFields/Boolean.html +1 -1
- data/doc/Compony/ModelFields/Color.html +1 -1
- data/doc/Compony/ModelFields/Currency.html +1 -1
- data/doc/Compony/ModelFields/Date.html +1 -1
- data/doc/Compony/ModelFields/Datetime.html +1 -1
- data/doc/Compony/ModelFields/Decimal.html +1 -1
- data/doc/Compony/ModelFields/Email.html +1 -1
- data/doc/Compony/ModelFields/Float.html +1 -1
- data/doc/Compony/ModelFields/Integer.html +1 -1
- data/doc/Compony/ModelFields/Percentage.html +1 -1
- data/doc/Compony/ModelFields/Phone.html +1 -1
- data/doc/Compony/ModelFields/RichText.html +1 -1
- data/doc/Compony/ModelFields/String.html +1 -1
- data/doc/Compony/ModelFields/Text.html +1 -1
- data/doc/Compony/ModelFields/Time.html +1 -1
- data/doc/Compony/ModelFields/Url.html +1 -1
- data/doc/Compony/ModelFields.html +1 -1
- data/doc/Compony/ModelMixin.html +1 -1
- data/doc/Compony/NaturalOrdering.html +292 -0
- data/doc/Compony/RequestContext.html +72 -1
- data/doc/Compony/Version.html +1 -1
- data/doc/Compony/ViewHelpers.html +1 -1
- data/doc/Compony.html +3 -3
- data/doc/ComponyController.html +1 -1
- data/doc/_index.html +8 -1
- data/doc/class_list.html +1 -1
- data/doc/file.README.html +73 -16
- data/doc/index.html +73 -16
- data/doc/method_list.html +100 -100
- data/doc/top-level-namespace.html +1 -1
- data/lib/compony/component.rb +30 -54
- data/lib/compony/components/edit.rb +2 -4
- data/lib/compony/components/new.rb +4 -6
- data/lib/compony/components/with_form.rb +1 -1
- data/lib/compony/model_fields/anchormodel.rb +3 -1
- data/lib/compony/natural_ordering.rb +56 -0
- data/lib/compony/request_context.rb +8 -0
- data/lib/compony.rb +1 -0
- metadata +6 -4
@@ -102,7 +102,7 @@
|
|
102
102
|
</div>
|
103
103
|
|
104
104
|
<div id="footer">
|
105
|
-
Generated on Wed
|
105
|
+
Generated on Wed May 29 15:53:00 2024 by
|
106
106
|
<a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
|
107
107
|
0.9.34 (ruby-3.2.2).
|
108
108
|
</div>
|
data/lib/compony/component.rb
CHANGED
@@ -7,6 +7,7 @@ module Compony
|
|
7
7
|
|
8
8
|
attr_reader :parent_comp
|
9
9
|
attr_reader :comp_opts
|
10
|
+
attr_reader :content_blocks # needed in RequestContext for nesting
|
10
11
|
|
11
12
|
# root comp: component that is registered to be root of the application.
|
12
13
|
# parent comp: component that is registered to be the parent of this comp. If there is none, this is the root comp.
|
@@ -24,9 +25,9 @@ module Compony
|
|
24
25
|
@sub_comps = []
|
25
26
|
@index = index
|
26
27
|
@comp_opts = comp_opts
|
27
|
-
@
|
28
|
-
@content_blocks =
|
29
|
-
@actions =
|
28
|
+
@before_render_blocks = NaturalOrdering.new
|
29
|
+
@content_blocks = NaturalOrdering.new
|
30
|
+
@actions = NaturalOrdering.new
|
30
31
|
@skipped_actions = Set.new
|
31
32
|
|
32
33
|
init_standalone
|
@@ -111,46 +112,39 @@ module Compony
|
|
111
112
|
comp_cst.to_s.underscore
|
112
113
|
end
|
113
114
|
|
114
|
-
# @todo deprecate (check for usages beforehand)
|
115
|
-
def comp_class_for(...)
|
116
|
-
Compony.comp_class_for(...)
|
117
|
-
end
|
118
|
-
|
119
|
-
# @todo deprecate (check for usages beforehand)
|
120
|
-
def comp_class_for!(...)
|
121
|
-
Compony.comp_class_for!(...)
|
122
|
-
end
|
123
|
-
|
124
115
|
# DSL method
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
#
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
fail("`content` expects a block in #{inspect}.") unless block_given?
|
134
|
-
@content_blocks = [block]
|
116
|
+
# Adds or overrides a before_render block.
|
117
|
+
# You can use controller.redirect_to to redirect away and halt the before_render/content chain
|
118
|
+
# @param [Symbol,String] name The name of the before_render block, defaults to `:main`
|
119
|
+
# @param [nil,Symbol,String] before If nil, the block will be added to the bottom of the before_render chain. Otherwise, pass the name of another block.
|
120
|
+
# @param [Proc] block The block that should be run as part of the before_render pipeline. Will run in the component's context.
|
121
|
+
def before_render(name = :main, before: nil, **kwargs, &block)
|
122
|
+
fail("`before_render` expects a block in #{inspect}.") unless block_given?
|
123
|
+
@before_render_blocks.natural_push(name, block, before:, **kwargs)
|
135
124
|
end
|
136
125
|
|
137
126
|
# DSL method
|
138
|
-
# Adds a content block
|
139
|
-
#
|
140
|
-
#
|
141
|
-
|
127
|
+
# Adds or overrides a content block.
|
128
|
+
# @param [Symbol,String] name The name of the content block, defaults to `:main`
|
129
|
+
# @param [nil,Symbol,String] before If nil, the block will be added to the bottom of the content chain. Otherwise, pass the name of another block.
|
130
|
+
# @param [Hash] kwargs If hidden is true, the content will not be rendered by default, allowing you to nest it in another content block.
|
131
|
+
# @param [Proc] block The block that should be run as part of the content pipeline. Will run in the component's context. You can use Dyny here.
|
132
|
+
def content(name = :main, before: nil, **kwargs, &block)
|
142
133
|
fail("`content` expects a block in #{inspect}.") unless block_given?
|
143
|
-
@content_blocks
|
144
|
-
@content_blocks.insert(index, block)
|
134
|
+
@content_blocks.natural_push(name, block, before:, **kwargs)
|
145
135
|
end
|
146
136
|
|
147
137
|
# Renders the component using the controller passsed to it and returns it as a string.
|
148
138
|
# @param [Boolean] standalone pass true iff `render` is called from `render_standalone`
|
149
139
|
# Do not overwrite.
|
150
140
|
def render(controller, standalone: false, **locals)
|
151
|
-
# Call before_render
|
152
|
-
|
153
|
-
|
141
|
+
# Call before_render hooks (if any) and backfire instance variables back to the component
|
142
|
+
@before_render_blocks.each do |element|
|
143
|
+
RequestContext.new(self, controller, locals:).evaluate_with_backfire(&element.payload)
|
144
|
+
# Stop if a `before_render` block issued a body (e.g. through redirecting)
|
145
|
+
break unless controller.response_body.nil?
|
146
|
+
end
|
147
|
+
|
154
148
|
# Render, unless before_render has already issued a body (e.g. through redirecting).
|
155
149
|
if controller.response_body.nil?
|
156
150
|
fail "#{self.class.inspect} must define `content` or set a response body in `before_render`" if @content_blocks.none?
|
@@ -161,9 +155,9 @@ module Compony
|
|
161
155
|
if Compony.content_before_root_comp_block && standalone
|
162
156
|
Compony::RequestContext.new(component, controller, helpers: self, locals: render_locals).evaluate(&Compony.content_before_root_comp_block)
|
163
157
|
end
|
164
|
-
content_blocks.each do |
|
158
|
+
content_blocks.reject{ |el| el.hidden }.each do |element|
|
165
159
|
# Instanciate and evaluate a fresh RequestContext in order to use the buffer allocated by the ActionView (needed for `concat` calls)
|
166
|
-
Compony::RequestContext.new(component, controller, helpers: self, locals: render_locals).evaluate(&
|
160
|
+
Compony::RequestContext.new(component, controller, helpers: self, locals: render_locals).evaluate(&element.payload)
|
167
161
|
end
|
168
162
|
if Compony.content_after_root_comp_block && standalone
|
169
163
|
Compony::RequestContext.new(component, controller, helpers: self, locals: render_locals).evaluate(&Compony.content_after_root_comp_block)
|
@@ -179,25 +173,7 @@ module Compony
|
|
179
173
|
# Adds or replaces an action (for action buttons)
|
180
174
|
# If before: is specified, will insert the action before the named action. When replacing, an element keeps its position unless before: is specified.
|
181
175
|
def action(action_name, before: nil, &block)
|
182
|
-
action_name
|
183
|
-
before_name = before&.to_sym
|
184
|
-
action = MethodAccessibleHash.new(name: action_name, block:)
|
185
|
-
|
186
|
-
existing_index = @actions.find_index { |el| el.name == action_name }
|
187
|
-
if existing_index.present? && before_name.present?
|
188
|
-
@actions.delete_at(existing_index) # Replacing an existing element with a before: directive - must delete before calculating indices
|
189
|
-
end
|
190
|
-
if before_name.present?
|
191
|
-
before_index = @actions.find_index { |el| el.name == before_name } || fail("Action #{before_name} for :before not found in #{inspect}.")
|
192
|
-
end
|
193
|
-
|
194
|
-
if before_index.present?
|
195
|
-
@actions.insert(before_index, action)
|
196
|
-
elsif existing_index.present?
|
197
|
-
@actions[existing_index] = action
|
198
|
-
else
|
199
|
-
@actions << action
|
200
|
-
end
|
176
|
+
@actions.natural_push(action_name, block, before:)
|
201
177
|
end
|
202
178
|
|
203
179
|
# DSL method
|
@@ -213,7 +189,7 @@ module Compony
|
|
213
189
|
button_htmls = @actions.map do |action|
|
214
190
|
next if @skipped_actions.include?(action.name)
|
215
191
|
Compony.with_button_defaults(feasibility_action: action.name.to_sym) do
|
216
|
-
action_button = action.
|
192
|
+
action_button = action.payload.call(controller)
|
217
193
|
next unless action_button
|
218
194
|
button_html = action_button.render(controller)
|
219
195
|
next if button_html.blank?
|
@@ -50,10 +50,8 @@ module Compony
|
|
50
50
|
end
|
51
51
|
hsh? local_form_comp.schema_wrapper_key_for(local_data), &local_form_comp.schema_block_for(local_data)
|
52
52
|
end
|
53
|
-
schema.validate!(controller.request.params)
|
54
|
-
|
55
|
-
# TODO: Why are we not saving the validated params?
|
56
|
-
attrs_to_assign = controller.request.params[form_comp.schema_wrapper_key_for(@data)]
|
53
|
+
validated_params = schema.validate!(controller.request.params)
|
54
|
+
attrs_to_assign = validated_params[form_comp.schema_wrapper_key_for(@data)]
|
57
55
|
@data.assign_attributes(attrs_to_assign) if attrs_to_assign
|
58
56
|
end
|
59
57
|
|
@@ -32,10 +32,10 @@ module Compony
|
|
32
32
|
label(:short) { I18n.t('compony.components.new.label.short') }
|
33
33
|
icon { :plus }
|
34
34
|
|
35
|
-
|
35
|
+
content :label do
|
36
36
|
h2 component.label
|
37
37
|
end
|
38
|
-
|
38
|
+
content do
|
39
39
|
concat form_comp.render(controller, data: @data)
|
40
40
|
end
|
41
41
|
|
@@ -45,10 +45,8 @@ module Compony
|
|
45
45
|
schema = Schemacop::Schema3.new :hash, additional_properties: true do
|
46
46
|
hsh? local_form_comp.schema_wrapper_key_for(local_data), &local_form_comp.schema_block_for(local_data)
|
47
47
|
end
|
48
|
-
schema.validate!(controller.request.params)
|
49
|
-
|
50
|
-
# TODO: Why are we not saving the validated params?
|
51
|
-
attrs_to_assign = controller.request.params[form_comp.schema_wrapper_key_for(@data)]
|
48
|
+
validated_params = schema.validate!(controller.request.params)
|
49
|
+
attrs_to_assign = validated_params[form_comp.schema_wrapper_key_for(@data)]
|
52
50
|
@data.assign_attributes(attrs_to_assign) if attrs_to_assign
|
53
51
|
end
|
54
52
|
|
@@ -13,7 +13,7 @@ module Compony
|
|
13
13
|
# Returns an instance of the form component responsible for rendering the form.
|
14
14
|
# Feel free to override this in subclasses.
|
15
15
|
def form_comp
|
16
|
-
@form_comp ||= (form_comp_class || comp_class_for!(:form, family_cst)).new(
|
16
|
+
@form_comp ||= (form_comp_class || Compony.comp_class_for!(:form, family_cst)).new(
|
17
17
|
self,
|
18
18
|
submit_verb:,
|
19
19
|
# If applicable, Rails adds the route keys automatically, thus, e.g. :id does not need to be passed here, as it comes from the request.
|
@@ -13,8 +13,10 @@ module Compony
|
|
13
13
|
def simpleform_input_hidden(form, _component, name: nil, **input_opts)
|
14
14
|
if form.object
|
15
15
|
selected_cst = form.object.send(@name)
|
16
|
+
am_attr = form.object.class.anchormodel_attributes[@name]
|
17
|
+
am_serializer = (am_attr.multiple? ? ::Anchormodel::ActiveModelTypeValueMulti : ::Anchormodel::ActiveModelTypeValueSingle).new(am_attr)
|
16
18
|
input_opts[:input_html] ||= {}
|
17
|
-
input_opts[:input_html][:value] =
|
19
|
+
input_opts[:input_html][:value] = am_serializer.serialize(selected_cst)
|
18
20
|
end
|
19
21
|
return form.input name || @name, as: :hidden, **input_opts
|
20
22
|
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module Compony
|
2
|
+
# @api description
|
3
|
+
# This class provides an array-based data structure where elements have symbol names. New elements can be appended or placed at a location using `before:`.
|
4
|
+
# Important: do not mutate this class with any other method call than the natural_-prefixed methods defined below.
|
5
|
+
# Example:<br>
|
6
|
+
# ```ruby
|
7
|
+
# collection = Compony::NaturalOrdering.new
|
8
|
+
# collection.natural_push(:a, a_payload)
|
9
|
+
# collection.natural_push(:c, c_payload)
|
10
|
+
# collection.natural_push(:b, b_payload, before: :c)
|
11
|
+
# collection.natural_push(:d, d_payload, hidden: true)
|
12
|
+
# collection.natural_push(:a, a_new_payload) # overwrites :a
|
13
|
+
#
|
14
|
+
# collection.reject{|el| el.hidden}.map(&:name) # --> :a, :b, :c
|
15
|
+
# collection.map(&:payload) # --> a_new_payload, b_payload, c_payload, d_payload
|
16
|
+
# ```
|
17
|
+
class NaturalOrdering < Array
|
18
|
+
def natural_push(name, payload, before: nil, **kwargs)
|
19
|
+
name = name.to_sym
|
20
|
+
before_name = before&.to_sym
|
21
|
+
old_kwargs = {}
|
22
|
+
|
23
|
+
# Fetch existing element if any
|
24
|
+
existing_index = find_index { |el| el.name == name }
|
25
|
+
if existing_index.present?
|
26
|
+
# Copy all non-mentionned kwargs from the element we are about to overwrite
|
27
|
+
old_kwargs = self[existing_index].except(:name, :payload)
|
28
|
+
|
29
|
+
# Replacing an existing element with a before: directive - must delete before calculating indices
|
30
|
+
if before_name.present?
|
31
|
+
delete_at(existing_index)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# Fetch before element
|
36
|
+
if before_name.present?
|
37
|
+
before_index = find_index { |el| el.name == before_name } || fail("Element #{before_name.inspect} for :before not found in #{inspect}.")
|
38
|
+
end
|
39
|
+
|
40
|
+
# Create the element to insert
|
41
|
+
element = MethodAccessibleHash.new(name:, payload:, **old_kwargs.merge(kwargs))
|
42
|
+
|
43
|
+
# Insert new element
|
44
|
+
if before_index.present?
|
45
|
+
# Insert before another element
|
46
|
+
insert(before_index, element)
|
47
|
+
elsif existing_index.present?
|
48
|
+
# Override another element
|
49
|
+
self[existing_index] = element
|
50
|
+
else
|
51
|
+
# Append at the end
|
52
|
+
self << element
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -41,5 +41,13 @@ module Compony
|
|
41
41
|
return true if @local_assigns.key?(method)
|
42
42
|
return super
|
43
43
|
end
|
44
|
+
|
45
|
+
# Renders a content block from the current component.
|
46
|
+
def content(name)
|
47
|
+
name = name.to_sym
|
48
|
+
content_block = component.content_blocks.find { |el| el.name == name } || fail("Content block #{name.inspect} not found in #{component.inspect}.")
|
49
|
+
# A fresh RequestContext is needed due to Rails' buffer
|
50
|
+
concat Compony::RequestContext.new(component, controller, helpers:, locals: local_assigns).evaluate(&content_block.payload)
|
51
|
+
end
|
44
52
|
end
|
45
53
|
end
|
data/lib/compony.rb
CHANGED
@@ -300,6 +300,7 @@ require 'compony/components/new'
|
|
300
300
|
require 'compony/components/edit'
|
301
301
|
require 'compony/components/destroy'
|
302
302
|
require 'compony/method_accessible_hash'
|
303
|
+
require 'compony/natural_ordering'
|
303
304
|
require 'compony/model_mixin'
|
304
305
|
require 'compony/request_context'
|
305
306
|
require 'compony/version'
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: compony
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sandro Kalbermatter
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2024-
|
12
|
+
date: 2024-05-29 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: yard
|
@@ -143,14 +143,14 @@ dependencies:
|
|
143
143
|
requirements:
|
144
144
|
- - "~>"
|
145
145
|
- !ruby/object:Gem::Version
|
146
|
-
version: 0.
|
146
|
+
version: 0.2.0
|
147
147
|
type: :runtime
|
148
148
|
prerelease: false
|
149
149
|
version_requirements: !ruby/object:Gem::Requirement
|
150
150
|
requirements:
|
151
151
|
- - "~>"
|
152
152
|
- !ruby/object:Gem::Version
|
153
|
-
version: 0.
|
153
|
+
version: 0.2.0
|
154
154
|
- !ruby/object:Gem::Dependency
|
155
155
|
name: cancancan
|
156
156
|
requirement: !ruby/object:Gem::Requirement
|
@@ -233,6 +233,7 @@ files:
|
|
233
233
|
- doc/Compony/ModelFields/Time.html
|
234
234
|
- doc/Compony/ModelFields/Url.html
|
235
235
|
- doc/Compony/ModelMixin.html
|
236
|
+
- doc/Compony/NaturalOrdering.html
|
236
237
|
- doc/Compony/RequestContext.html
|
237
238
|
- doc/Compony/Version.html
|
238
239
|
- doc/Compony/ViewHelpers.html
|
@@ -297,6 +298,7 @@ files:
|
|
297
298
|
- lib/compony/model_fields/time.rb
|
298
299
|
- lib/compony/model_fields/url.rb
|
299
300
|
- lib/compony/model_mixin.rb
|
301
|
+
- lib/compony/natural_ordering.rb
|
300
302
|
- lib/compony/request_context.rb
|
301
303
|
- lib/compony/version.rb
|
302
304
|
- lib/compony/view_helpers.rb
|