compony 0.11.8 → 0.11.9

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.
Files changed (132) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +36 -1
  3. data/CHANGELOG.md +31 -0
  4. data/CLAUDE.md +85 -0
  5. data/Gemfile.lock +1 -1
  6. data/README.md +13 -3
  7. data/VERSION +1 -1
  8. data/compony.gemspec +3 -3
  9. data/doc/ComponentGenerator.html +1 -1
  10. data/doc/Components.html +1 -1
  11. data/doc/ComponentsGenerator.html +1 -1
  12. data/doc/Compony/Component.html +54 -54
  13. data/doc/Compony/ComponentMixins/Default/Labelling.html +1 -1
  14. data/doc/Compony/ComponentMixins/Default/Standalone/ResourcefulVerbDsl.html +1 -1
  15. data/doc/Compony/ComponentMixins/Default/Standalone/StandaloneDsl.html +109 -70
  16. data/doc/Compony/ComponentMixins/Default/Standalone/VerbDsl.html +64 -28
  17. data/doc/Compony/ComponentMixins/Default/Standalone.html +1 -1
  18. data/doc/Compony/ComponentMixins/Default.html +1 -1
  19. data/doc/Compony/ComponentMixins/Resourceful.html +213 -74
  20. data/doc/Compony/ComponentMixins.html +1 -1
  21. data/doc/Compony/Components/Buttons/CssButton.html +1 -1
  22. data/doc/Compony/Components/Buttons/Link.html +1 -1
  23. data/doc/Compony/Components/Buttons.html +1 -1
  24. data/doc/Compony/Components/Destroy.html +83 -29
  25. data/doc/Compony/Components/Edit.html +110 -38
  26. data/doc/Compony/Components/Form.html +551 -208
  27. data/doc/Compony/Components/Index.html +1 -1
  28. data/doc/Compony/Components/List.html +3 -3
  29. data/doc/Compony/Components/New.html +110 -38
  30. data/doc/Compony/Components/Show.html +1 -1
  31. data/doc/Compony/Components/WithForm.html +194 -47
  32. data/doc/Compony/Components.html +1 -1
  33. data/doc/Compony/ControllerMixin.html +1 -1
  34. data/doc/Compony/Engine.html +1 -1
  35. data/doc/Compony/Intent.html +2 -2
  36. data/doc/Compony/ManageIntentsDsl.html +1 -1
  37. data/doc/Compony/MethodAccessibleHash.html +1 -1
  38. data/doc/Compony/ModelFields/Anchormodel.html +1 -1
  39. data/doc/Compony/ModelFields/Association.html +1 -1
  40. data/doc/Compony/ModelFields/Attachment.html +1 -1
  41. data/doc/Compony/ModelFields/Base.html +1 -1
  42. data/doc/Compony/ModelFields/Boolean.html +1 -1
  43. data/doc/Compony/ModelFields/Color.html +1 -1
  44. data/doc/Compony/ModelFields/Currency.html +1 -1
  45. data/doc/Compony/ModelFields/Date.html +1 -1
  46. data/doc/Compony/ModelFields/Datetime.html +1 -1
  47. data/doc/Compony/ModelFields/Decimal.html +1 -1
  48. data/doc/Compony/ModelFields/Email.html +1 -1
  49. data/doc/Compony/ModelFields/Float.html +1 -1
  50. data/doc/Compony/ModelFields/Integer.html +1 -1
  51. data/doc/Compony/ModelFields/Percentage.html +1 -1
  52. data/doc/Compony/ModelFields/Phone.html +1 -1
  53. data/doc/Compony/ModelFields/RichText.html +1 -1
  54. data/doc/Compony/ModelFields/String.html +1 -1
  55. data/doc/Compony/ModelFields/Text.html +1 -1
  56. data/doc/Compony/ModelFields/Time.html +1 -1
  57. data/doc/Compony/ModelFields/Url.html +1 -1
  58. data/doc/Compony/ModelFields.html +1 -1
  59. data/doc/Compony/ModelMixin.html +1 -1
  60. data/doc/Compony/NaturalOrdering.html +1 -1
  61. data/doc/Compony/RequestContext.html +1 -1
  62. data/doc/Compony/Version.html +1 -1
  63. data/doc/Compony/ViewHelpers.html +1 -1
  64. data/doc/Compony/VirtualModel.html +1 -1
  65. data/doc/Compony.html +1 -1
  66. data/doc/ComponyController.html +1 -1
  67. data/doc/_index.html +97 -1
  68. data/doc/file.CHANGELOG.html +758 -0
  69. data/doc/file.README.html +25 -4
  70. data/doc/file.basic_component.html +314 -0
  71. data/doc/file.cookbook.html +189 -0
  72. data/doc/file.destroy.html +105 -0
  73. data/doc/file.dsl_reference.html +672 -0
  74. data/doc/file.edit.html +109 -0
  75. data/doc/file.example.html +291 -0
  76. data/doc/file.example_advanced.html +257 -0
  77. data/doc/file.feasibility.html +115 -0
  78. data/doc/file.form.html +195 -0
  79. data/doc/file.generators.html +89 -0
  80. data/doc/file.glossary.html +217 -0
  81. data/doc/file.gotchas.html +222 -0
  82. data/doc/file.index.html +135 -0
  83. data/doc/file.inheritance.html +136 -0
  84. data/doc/file.installation.html +115 -0
  85. data/doc/file.integrations.html +218 -0
  86. data/doc/file.intents.html +265 -0
  87. data/doc/file.internal_datastructures.html +129 -0
  88. data/doc/file.list.html +253 -0
  89. data/doc/file.maintaining.html +127 -0
  90. data/doc/file.model_fields.html +137 -0
  91. data/doc/file.nesting.html +237 -0
  92. data/doc/file.new.html +109 -0
  93. data/doc/file.ownership.html +98 -0
  94. data/doc/file.patterns.html +669 -0
  95. data/doc/file.pre_built_components.html +99 -0
  96. data/doc/file.resourceful.html +181 -0
  97. data/doc/file.show.html +158 -0
  98. data/doc/file.standalone.html +233 -0
  99. data/doc/file.virtual_models.html +117 -0
  100. data/doc/file.with_form.html +157 -0
  101. data/doc/file_list.html +160 -0
  102. data/doc/guide/cookbook.md +41 -0
  103. data/doc/guide/dsl_reference.md +155 -0
  104. data/doc/guide/example_advanced.md +209 -0
  105. data/doc/guide/generators.md +1 -1
  106. data/doc/guide/glossary.md +42 -0
  107. data/doc/guide/gotchas.md +125 -0
  108. data/doc/guide/maintaining.md +64 -0
  109. data/doc/guide/patterns.md +681 -0
  110. data/doc/guide/pre_built_components/edit.md +1 -1
  111. data/doc/guide/pre_built_components/index.md +64 -1
  112. data/doc/guide/pre_built_components/list.md +111 -7
  113. data/doc/guide/pre_built_components/show.md +57 -2
  114. data/doc/guide/pre_built_components/with_form.md +56 -9
  115. data/doc/guide/pre_built_components.md +7 -2
  116. data/doc/guide/standalone.md +16 -1
  117. data/doc/index.html +25 -4
  118. data/doc/integrations.md +61 -0
  119. data/doc/llms.txt +62 -0
  120. data/doc/top-level-namespace.html +1 -1
  121. data/lib/compony/component.rb +8 -3
  122. data/lib/compony/component_mixins/default/standalone/standalone_dsl.rb +32 -15
  123. data/lib/compony/component_mixins/default/standalone/verb_dsl.rb +11 -3
  124. data/lib/compony/component_mixins/resourceful.rb +30 -16
  125. data/lib/compony/components/destroy.rb +21 -1
  126. data/lib/compony/components/edit.rb +25 -1
  127. data/lib/compony/components/form.rb +63 -21
  128. data/lib/compony/components/list.rb +1 -1
  129. data/lib/compony/components/new.rb +25 -1
  130. data/lib/compony/components/with_form.rb +20 -5
  131. data/lib/compony/intent.rb +1 -1
  132. metadata +43 -1
@@ -16,16 +16,27 @@ module Compony
16
16
  @scope = scope
17
17
  @scope_args = scope_args
18
18
  @verbs = {}
19
- @skip_authentication = false
20
- @skip_forgery_protection = false
21
- @layout = true # can be overriden by false or a string
19
+ # These default to nil so that, on subsequent `standalone` calls (e.g. subclass overrides), they are stripped
20
+ # by `compact` and thus do NOT clobber values inherited via `deep_merge!`. The actual defaults are only injected
21
+ # when `provide_defaults` is true (i.e. the first `standalone` call). This mirrors VerbDsl#to_conf.
22
+ @skip_authentication = nil
23
+ @skip_forgery_protection = nil
24
+ @layout = nil # can be overriden by false or a string
22
25
  end
23
26
 
27
+ # Defaults injected only on the first `standalone` call. Kept out of subsequent calls so inherited values survive.
28
+ DEFAULT_CONFIG = {
29
+ skip_authentication: false,
30
+ skip_forgery_protection: false,
31
+ layout: true
32
+ }.freeze
33
+
24
34
  # For internal usage only, processes the block and returns a config hash.
25
35
  def to_conf(&block)
26
36
  evaluate(&block)
27
37
  @component = block.binding.eval('self') # Fetches the component holding this DSL call (via the block)
28
- return {
38
+ base_config = @provide_defaults ? DEFAULT_CONFIG.dup : {}
39
+ return base_config.merge({
29
40
  name: @name,
30
41
  path: @path,
31
42
  constraints: @constraints,
@@ -37,13 +48,16 @@ module Compony
37
48
  skip_authentication: @skip_authentication,
38
49
  skip_forgery_protection: @skip_forgery_protection,
39
50
  layout: @layout
40
- }.compact
51
+ }.compact)
41
52
  end
42
53
 
43
54
  protected
44
55
 
45
- # DSL call for defining a config for a verb. The block runs within the verb DSL, positional and named arguments are passed to the verb DSL.
46
- # @param verb [Symbol] The HTTP verb the config is for (e.g. :get, :post etc.)
56
+ # DSL method. Defines the config for one HTTP verb. The block runs within the verb DSL; positional and named
57
+ # arguments are forwarded to it. Call at most once per verb per standalone.
58
+ # @param verb [Symbol] The HTTP verb (one of `:get :head :post :put :delete :connect :options :trace :patch`).
59
+ # @return [void]
60
+ # @api public
47
61
  # @see Compony::ComponentMixins::Default::Standalone::VerbDsl
48
62
  def verb(verb, *, **nargs, &)
49
63
  verb = verb.to_sym
@@ -61,22 +75,25 @@ module Compony
61
75
  end
62
76
  end
63
77
 
64
- # DSL
65
- # Defines that for this component, no authentication should be performed.
78
+ # DSL method. Disables app authentication for this standalone (an `authorize` block is still mandatory).
79
+ # @return [void]
80
+ # @api public
66
81
  def skip_authentication!
67
82
  @skip_authentication = true
68
83
  end
69
84
 
70
- # DSL
71
- # Defines that for this component, no forgery protection (CSRF) should be performed.
85
+ # DSL method. Disables forgery protection (CSRF) for this standalone's controller action.
86
+ # @return [void]
87
+ # @api public
72
88
  def skip_forgery_protection!
73
89
  @skip_forgery_protection = true
74
90
  end
75
91
 
76
- # DSL
77
- # Speficies the Rails layout (under `app/views/layouts`) that should be used to render this component.
78
- # Defaults to Rails' default (`layouts/application`) if the method is never called.
79
- # @param layout [String] name of the layout as you would give it to a Rails controller's `render` method
92
+ # DSL method. Sets the Rails layout (under `app/views/layouts`) used to render this component.
93
+ # Defaults to Rails' default (`layouts/application`) if never called.
94
+ # @param layout [String,Symbol] Layout name, as passed to a Rails controller's `render`.
95
+ # @return [void]
96
+ # @api public
80
97
  def layout(layout)
81
98
  @layout = layout.to_s
82
99
  end
@@ -35,14 +35,22 @@ module Compony
35
35
  protected
36
36
 
37
37
  # DSL
38
- # This block is expected to return true if and only if current_ability has the right to access the component over the given verb.
38
+ # Mandatory. The block must return truthy iff `current_ability` may access the component over this verb;
39
+ # a falsy result raises `CanCan::AccessDenied`.
40
+ # @yield Runs in the component's request context; returns truthy to grant access.
41
+ # @return [void]
42
+ # @api public
39
43
  def authorize(&block)
40
44
  @authorize_block = block
41
45
  end
42
46
 
43
47
  # DSL
44
- # This is the last step in the life cycle. It may redirect or render. If omitted, the default is standalone_render.
45
- # @param format [String, Symbol] Format this block should respond to, defaults to `nil` which means "all other formats".
48
+ # Last step in the lifecycle. May redirect or render. If omitted, the default is `render_standalone`.
49
+ # NOTE: overriding `respond` replaces the default, which is where `authorize` is evaluated - re-check authorization yourself.
50
+ # @param format [String,Symbol,nil] Format this block responds to; `nil` means "all other formats".
51
+ # @yield Runs in the component's request context; renders or redirects.
52
+ # @return [void]
53
+ # @api public
46
54
  def respond(format = nil, &block)
47
55
  @respond_blocks[format&.to_sym] = block
48
56
  end
@@ -25,8 +25,13 @@ module Compony
25
25
  super(*, **nargs, &)
26
26
  end
27
27
 
28
+ # @!group DSL
29
+
28
30
  # DSL method
29
- # Sets or calculates the model class based on the component's family name
31
+ # Sets or calculates the model class. Defaults to the component's family name, singularized and constantized.
32
+ # @param new_data_class [Class,nil] If given, the model class to use (e.g. a {Compony::VirtualModel} subclass).
33
+ # @return [Class] The resolved data class.
34
+ # @api public
30
35
  def data_class(new_data_class = nil)
31
36
  @data_class ||= new_data_class || family_name.singularize.camelize.constantize
32
37
  end
@@ -38,27 +43,32 @@ module Compony
38
43
  protected
39
44
 
40
45
  # DSL method
41
- # Sets a default load_data block for all standalone paths and verbs.
42
- # Can be overwritten for a specific path and verb in the
43
- # {Compony::ComponentMixins::Default::Standalone::VerbDsl}.
44
- # The block is expected to assign `@data`.
46
+ # Sets the default `load_data` block for all standalone paths and verbs (overridable per verb in the VerbDsl).
47
+ # Runs before authorization. The block is expected to assign `@data`.
48
+ # @yield Runs in the component's request context; must assign `@data`.
49
+ # @return [void]
50
+ # @api public
45
51
  # @see Compony::ComponentMixins::Default::Standalone::VerbDsl#load_data
46
52
  def load_data(&block)
47
53
  @global_load_data_block = block
48
54
  end
49
55
 
50
56
  # DSL method
51
- # Runs after loading data and before authorization for all standalone paths and verbs.
52
- # Example use case: if `load_data` produced an AR collection proxy, can still refine result here before `to_sql` is called.
57
+ # Runs after `load_data` and before authorization for all standalone paths and verbs.
58
+ # Example: refine an AR collection produced by `load_data` before it is read.
59
+ # @yield Runs in the component's request context; may refine `@data`.
60
+ # @return [void]
61
+ # @api public
53
62
  def after_load_data(&block)
54
63
  @global_after_load_data_block = block
55
64
  end
56
65
 
57
66
  # DSL method
58
- # Sets a default default assign_attributes block for all standalone paths and verbs.
59
- # Can be overwritten for a specific path and verb in the
60
- # {Compony::ComponentMixins::Default::Standalone::VerbDsl}.
61
- # The block is expected to assign suitable `params` to attributes of `@data`.
67
+ # Sets the default `assign_attributes` block for all standalone paths and verbs (overridable per verb in the VerbDsl).
68
+ # The block is expected to assign validated `params` to attributes of `@data`.
69
+ # @yield Runs in the component's request context; assigns params onto `@data`.
70
+ # @return [void]
71
+ # @api public
62
72
  # @see Compony::ComponentMixins::Default::Standalone::VerbDsl#assign_attributes
63
73
  def assign_attributes(&block)
64
74
  @global_assign_attributes_block = block
@@ -66,16 +76,20 @@ module Compony
66
76
 
67
77
  # DSL method
68
78
  # Runs after `assign_attributes` and before `store_data` for all standalone paths and verbs.
69
- # Example use case: prefilling some fields for a form
79
+ # Example: prefill or derive fields before validation.
80
+ # @yield Runs in the component's request context; may mutate `@data`.
81
+ # @return [void]
82
+ # @api public
70
83
  def after_assign_attributes(&block)
71
84
  @global_after_assign_attributes_block = block
72
85
  end
73
86
 
74
87
  # DSL method
75
- # Sets a default store_data block for all standalone paths and verbs.
76
- # Can be overwritten for a specific path and verb in the
77
- # {Compony::ComponentMixins::Default::Standalone::VerbDsl}.
78
- # The block is expected save `@data` to the database.
88
+ # Sets the default `store_data` block for all standalone paths and verbs (overridable per verb in the VerbDsl).
89
+ # The block is expected to persist `@data` (override e.g. for virtual models or custom persistence).
90
+ # @yield Runs in the component's request context; persists `@data`.
91
+ # @return [void]
92
+ # @api public
79
93
  # @see Compony::ComponentMixins::Default::Standalone::VerbDsl#store_data
80
94
  def store_data(&block)
81
95
  @global_store_data_block = block
@@ -78,21 +78,41 @@ module Compony
78
78
  end
79
79
  end
80
80
 
81
+ # @!group DSL
82
+
81
83
  # DSL method
82
- # Sets a block that is evaluated with backfire in the successful case after storing, but before responding.
84
+ # Sets an optional hook evaluated (with backfire) after a successful destroy but before responding.
85
+ # Suitable for post-destroy side effects (like an `after_destroy` that only fires when this component destroyed the record).
86
+ # Do not redirect or render here - use {#on_destroyed_respond} / {#on_destroyed_redirect_path} for that.
87
+ # @yield Runs in the component's request context after `@data` was destroyed.
88
+ # @return [void]
89
+ # @api public
83
90
  def on_destroyed(&block)
84
91
  @on_destroyed_block = block
85
92
  end
86
93
 
87
94
  # DSL method
95
+ # Overrides the response issued after a successful destroy. The default shows a flash and redirects to
96
+ # {#on_destroyed_redirect_path} with HTTP 303 (forces a GET, required for Turbo). If you override this,
97
+ # {#on_destroyed_redirect_path} is no longer called.
98
+ # @yield Runs in the component's request context; expected to render or redirect.
99
+ # @return [void]
100
+ # @api public
88
101
  def on_destroyed_respond(&block)
89
102
  @on_destroyed_respond_block = block
90
103
  end
91
104
 
92
105
  # DSL method
106
+ # Overrides the redirect target used by the default {#on_destroyed_respond} (keeping the default flash).
107
+ # Defaults to the owner's Show (if owned) or the data's Index.
108
+ # @yield Runs in the component's request context; expected to return a Rails path (e.g. via `Compony.path`).
109
+ # @return [void]
110
+ # @api public
93
111
  def on_destroyed_redirect_path(&block)
94
112
  @on_destroyed_redirect_path_block = block
95
113
  end
114
+
115
+ # @!endgroup
96
116
  end
97
117
  end
98
118
  end
@@ -90,26 +90,50 @@ module Compony
90
90
  end
91
91
  end
92
92
 
93
+ # @!group DSL
94
+
93
95
  # DSL method
94
- # Sets a block that is evaluated with backfire in the successful case after storing, but before responding.
96
+ # Sets an optional hook evaluated (with backfire) after a successful update but before responding.
97
+ # Suitable for post-update side effects (like an `after_update` that only fires when this component updated the record).
98
+ # Do not redirect or render here - use {#on_updated_respond} / {#on_updated_redirect_path} for that.
99
+ # @yield Runs in the component's request context after `@data` was saved successfully.
100
+ # @return [void]
101
+ # @api public
95
102
  def on_updated(&block)
96
103
  @on_updated_block = block
97
104
  end
98
105
 
99
106
  # DSL method
107
+ # Overrides the response issued after a successful update. The default shows a flash and redirects to
108
+ # {#on_updated_redirect_path}. If you override this, {#on_updated_redirect_path} is no longer called.
109
+ # @yield Runs in the component's request context; expected to render or redirect.
110
+ # @return [void]
111
+ # @api public
100
112
  def on_updated_respond(&block)
101
113
  @on_updated_respond_block = block
102
114
  end
103
115
 
104
116
  # DSL method
117
+ # Overrides the redirect target used by the default {#on_updated_respond} (keeping the default flash).
118
+ # Defaults to the data's Show, the owner's Show, or the data's Index.
119
+ # @yield Runs in the component's request context; expected to return a Rails path (e.g. via `Compony.path`).
120
+ # @return [void]
121
+ # @api public
105
122
  def on_updated_redirect_path(&block)
106
123
  @on_updated_redirect_path_block = block
107
124
  end
108
125
 
109
126
  # DSL method
127
+ # Overrides the response issued when the update failed (`@update_succeeded` is not true).
128
+ # The default logs the errors with level `warn` and re-renders the component with HTTP 422 so the form shows errors.
129
+ # @yield Runs in the component's request context; expected to render or redirect.
130
+ # @return [void]
131
+ # @api public
110
132
  def on_update_failed(&block)
111
133
  @on_update_failed_block = block
112
134
  end
135
+
136
+ # @!endgroup
113
137
  end
114
138
  end
115
139
  end
@@ -52,7 +52,13 @@ module Compony
52
52
  end
53
53
  end
54
54
 
55
- # DSL method, use to set the form content
55
+ # @!group DSL
56
+
57
+ # DSL method, use to set the form content (mandatory).
58
+ # The block holds the form inputs and is instance-exec'd in the form's request context where `field`, `pw_field` and `f` are available.
59
+ # @yield Builds the form body using Dyny + the form field helpers.
60
+ # @return [Proc,nil] When called without a block, returns the stored block.
61
+ # @api public
56
62
  def form_fields(&block)
57
63
  return @form_fields unless block_given?
58
64
  @form_fields = block
@@ -97,9 +103,13 @@ module Compony
97
103
  @controller = nil
98
104
  end
99
105
 
100
- # Called inside the form_fields block. This makes the method `field` available in the block.
101
- # See also notes for `with_simpleform`.
102
- # If multilang is true, a suffixed field is generated for every available locale (useful with gem "mobility"). Render the array as you wish.
106
+ # DSL method (inside `form_fields`). Renders a simple_form input inferred from the model field `name`.
107
+ # Respects per-field CanCanCan authorization; skipped fields render nothing.
108
+ # @param name [Symbol,String] The model field (use the association name, not the `_id`, for associations).
109
+ # @param multilang [Boolean] If true, generates one suffixed input per available locale and returns the array (useful with the "mobility" gem).
110
+ # @param input_opts [Hash] Passed to simple_form. Notable keys: `as:` (input type), `hidden: true`, `autofocus:`.
111
+ # @return [String,Array<String>] The input HTML (array when `multilang`).
112
+ # @api public
103
113
  def field(name, multilang: false, **input_opts)
104
114
  fail("The `field` method may only be called inside `form_fields` for #{inspect}.") unless @simpleform
105
115
 
@@ -134,9 +144,12 @@ module Compony
134
144
  end
135
145
  end
136
146
 
137
- # Called inside the form_fields block. This makes the method pw_field available in the block.
138
- # This method should be called for the fields :password and :password_confirmation
139
- # Note that :hidden is not supported here, as this would make no sense in conjunction with :password or :password_confirmation.
147
+ # DSL method (inside `form_fields`). Renders a password input; should be used for `:password` and `:password_confirmation`.
148
+ # Checks the `:set_password` CanCanCan ability; `:hidden` is intentionally unsupported here.
149
+ # @param name [Symbol,String] The password field name.
150
+ # @param input_opts [Hash] Passed to simple_form.
151
+ # @return [String,nil] The input HTML, or nil if not permitted.
152
+ # @api public
140
153
  def pw_field(name, **input_opts)
141
154
  fail("The `pw_field` method may only be called inside `form_fields` for #{inspect}.") unless @simpleform
142
155
  name = name.to_sym
@@ -156,8 +169,10 @@ module Compony
156
169
  return @simpleform.input name, **input_opts
157
170
  end
158
171
 
159
- # Called inside the form_fields block. This makes the method `f` available in the block.
160
- # See also notes for `with_simpleform`.
172
+ # DSL method (inside `form_fields`). Returns the underlying simple_form builder, e.g. for `f.rich_text_area`
173
+ # or `f.simple_fields_for` (nested attributes).
174
+ # @return [SimpleForm::FormBuilder] The simple_form builder for the current form.
175
+ # @api public
161
176
  def f
162
177
  fail("The `f` method may only be called inside `form_fields` for #{inspect}.") unless @simpleform
163
178
  return @simpleform
@@ -168,27 +183,41 @@ module Compony
168
183
  Compony::ModelFields::Anchormodel.collect(...)
169
184
  end
170
185
 
171
- # DSL method, disables all inputs
186
+ # DSL method, disables all inputs.
187
+ # @return [void]
188
+ # @api public
172
189
  def disable!
173
190
  @form_disabled = true
174
191
  end
175
192
 
176
- # DSL method, allows to customize parameters given to simple_form_for
193
+ # DSL method, customizes the parameters given to `simple_form_for`.
194
+ # @param new_form_params [Hash] Extra kwargs forwarded to `simple_form_for`.
195
+ # @return [void]
196
+ # @api public
177
197
  def form_params(**new_form_params)
178
198
  @form_params = new_form_params
179
199
  end
180
200
 
201
+ # @!endgroup
202
+
181
203
  protected
182
204
 
183
- # DSL method, adds a new line to the schema whitelisting a single param inside the schema's wrapper
184
- # The block should be something like `str? :foo` and will run in a Schemacop3 context.
205
+ # @!group DSL
206
+
207
+ # DSL method, adds a Schemacop3 line whitelisting param(s) inside the schema's wrapper.
208
+ # @yield Runs in a Schemacop3 context, e.g. `str? :foo`.
209
+ # @return [void]
210
+ # @api public
185
211
  def schema_line(&block)
186
212
  @schema_lines_for_data << proc { |_data, _controller| block }
187
213
  end
188
214
 
189
- # DSL method, adds a new field to the schema whitelisting a single field of data_class
190
- # This auto-generates the correct schema line for the field.
191
- # If multilang is true, a suffixed field is generated for every available locale (useful with gem "mobility")
215
+ # DSL method, whitelists a single field of `data_class` in the param schema, auto-generating the correct schema line.
216
+ # Respects per-field CanCanCan authorization.
217
+ # @param field_name [Symbol,String] The model field (association name, not `_id`, for associations).
218
+ # @param multilang [Boolean] If true, whitelists one suffixed field per available locale (useful with the "mobility" gem).
219
+ # @return [void]
220
+ # @api public
192
221
  def schema_field(field_name, multilang: false)
193
222
  if multilang
194
223
  I18n.available_locales.each { |locale| schema_field("#{field_name}_#{locale}") }
@@ -209,8 +238,10 @@ module Compony
209
238
  end
210
239
  end
211
240
 
212
- # DSL method, adds a new password field to the schema whitelisting
213
- # This checks for the permission :set_password and auto-generates the correct schema line for the field.
241
+ # DSL method, whitelists a password param in the schema (checks the `:set_password` permission).
242
+ # @param field_name [Symbol,String] The password field name.
243
+ # @return [void]
244
+ # @api public
214
245
  def schema_pw_field(field_name)
215
246
  # This runs upon component setup.
216
247
  @schema_lines_for_data << proc do |data, controller|
@@ -226,12 +257,19 @@ module Compony
226
257
  end
227
258
  end
228
259
 
229
- # DSL method, mass-assigns schema fields
260
+ # DSL method, whitelists several fields at once (see {#schema_field}).
261
+ # @param field_names [Array<Symbol,String>] The model fields to whitelist.
262
+ # @return [void]
263
+ # @api public
230
264
  def schema_fields(*field_names)
231
265
  field_names.each { |field_name| schema_field(field_name) }
232
266
  end
233
267
 
234
- # DSL method, use to replace the form's schema and wrapper key for a completely manual schema
268
+ # DSL method, replaces the form's schema and wrapper key with a completely manual Schemacop3 schema.
269
+ # @param wrapper_key [Symbol,String] The top-level params wrapper key (e.g. the model's singular name).
270
+ # @yield Runs in a Schemacop3 context defining the wrapped params.
271
+ # @return [void]
272
+ # @api public
235
273
  def schema(wrapper_key, &block)
236
274
  if block_given?
237
275
  @schema_wrapper_key = wrapper_key
@@ -241,10 +279,14 @@ module Compony
241
279
  end
242
280
  end
243
281
 
244
- # DSL method, skips adding autofocus to the first field
282
+ # DSL method, skips adding autofocus to the first field.
283
+ # @return [void]
284
+ # @api public
245
285
  def skip_autofocus
246
286
  @skip_autofocus = true
247
287
  end
288
+
289
+ # @!endgroup
248
290
  end
249
291
  end
250
292
  end
@@ -163,7 +163,7 @@ module Compony
163
163
  end
164
164
 
165
165
  # DSL method
166
- # If a block is given: Enters the DSL where row intents can be added or removed (use from {Component#setup} within the component).
166
+ # If a block is given: Enters the DSL where row intents can be added or removed (use from {Compony::Component.setup} within the component).
167
167
  # If no block is given: Builds the declared intents for the given record and returns them (use in `content` or `before_render`, pass kwarg `:data`).
168
168
  def row_intents(**intent_opts, &block)
169
169
  if block_given?
@@ -78,26 +78,50 @@ module Compony
78
78
  end
79
79
  end
80
80
 
81
+ # @!group DSL
82
+
81
83
  # DSL method
82
- # Sets a block that is evaluated with backfire in the successful case after storing, but before responding.
84
+ # Sets an optional hook evaluated (with backfire) after a successful create but before responding.
85
+ # Suitable for post-create side effects (like an `after_create` that only fires when this component created the record).
86
+ # Do not redirect or render here - use {#on_created_respond} / {#on_created_redirect_path} for that.
87
+ # @yield Runs in the component's request context after `@data` was saved successfully.
88
+ # @return [void]
89
+ # @api public
83
90
  def on_created(&block)
84
91
  @on_created_block = block
85
92
  end
86
93
 
87
94
  # DSL method
95
+ # Overrides the response issued after a successful create. The default shows a flash and redirects to
96
+ # {#on_created_redirect_path}. If you override this, {#on_created_redirect_path} is no longer called.
97
+ # @yield Runs in the component's request context; expected to render or redirect.
98
+ # @return [void]
99
+ # @api public
88
100
  def on_created_respond(&block)
89
101
  @on_created_respond_block = block
90
102
  end
91
103
 
92
104
  # DSL method
105
+ # Overrides the redirect target used by the default {#on_created_respond} (keeping the default flash).
106
+ # Defaults to the data's Show, the owner's Show, or the data's Index.
107
+ # @yield Runs in the component's request context; expected to return a Rails path (e.g. via `Compony.path`).
108
+ # @return [void]
109
+ # @api public
93
110
  def on_created_redirect_path(&block)
94
111
  @on_created_redirect_path_block = block
95
112
  end
96
113
 
97
114
  # DSL method
115
+ # Overrides the response issued when the create failed (`@create_succeeded` is not true).
116
+ # The default logs the errors with level `warn` and re-renders the component with HTTP 422 so the form shows errors.
117
+ # @yield Runs in the component's request context; expected to render or redirect.
118
+ # @return [void]
119
+ # @api public
98
120
  def on_create_failed_respond(&block)
99
121
  @on_create_failed_respond_block = block
100
122
  end
123
+
124
+ # @!endgroup
101
125
  end
102
126
  end
103
127
  end
@@ -22,8 +22,13 @@ module Compony
22
22
  )
23
23
  end
24
24
 
25
+ # @!group DSL
26
+
25
27
  # DSL method
26
- # Sets or returns the previously set submit verb
28
+ # Sets or returns the HTTP verb the twinned form submits with (`:post` for New, `:patch` for Edit).
29
+ # @param new_submit_verb [Symbol,nil] If given, sets the submit verb; must be a known HTTP verb.
30
+ # @return [Symbol] The (possibly just set) submit verb.
31
+ # @api public
27
32
  def submit_verb(new_submit_verb = nil)
28
33
  if new_submit_verb.present?
29
34
  new_submit_verb = new_submit_verb.to_sym
@@ -35,13 +40,19 @@ module Compony
35
40
  end
36
41
 
37
42
  # DSL method
38
- # Overrides the form comp class that is instanciated to render the form
43
+ # Overrides the Form component class instantiated by {#form_comp} (defaults to the same-family `Form`).
44
+ # @param new_form_comp_class [Class,nil] The Form component class to use.
45
+ # @return [Class,nil] The configured form component class.
46
+ # @api public
39
47
  def form_comp_class(new_form_comp_class = nil)
40
48
  @form_comp_class ||= new_form_comp_class
41
49
  end
42
50
 
43
51
  # DSL method
44
- # Sets and gets the form's cancancan action (for cancancan's accessible_attributes)
52
+ # Sets and gets the form's CanCanCan action, used for per-field `permitted_attributes`. Pass `nil` to disable per-field auth.
53
+ # @param new_form_cancancan_action [Symbol,nil] The CanCanCan action (e.g. `:edit`); omit to read the current value.
54
+ # @return [Symbol,nil] The configured CanCanCan action.
55
+ # @api public
45
56
  def form_cancancan_action(new_form_cancancan_action = :missing)
46
57
  if new_form_cancancan_action != :missing
47
58
  @form_cancancan_action = new_form_cancancan_action
@@ -50,11 +61,15 @@ module Compony
50
61
  end
51
62
 
52
63
  # DSL method
53
- # Overrides the submit path which would otherwise default to this component
54
- # This takes a block that will be called and given a controller
64
+ # Overrides the submit path, which otherwise defaults to this component's own path.
65
+ # @yield [controller] Called with the controller; expected to return the Rails path the form submits to.
66
+ # @return [void]
67
+ # @api public
55
68
  def submit_path(&new_submit_path_block)
56
69
  @submit_path_block = new_submit_path_block
57
70
  end
71
+
72
+ # @!endgroup
58
73
  end
59
74
  end
60
75
  end
@@ -144,7 +144,7 @@ module Compony
144
144
  # Renders this intent into a button defined by `style`.
145
145
  # @param controller [ApplicationController] The controller from the request context, needed to render the button.
146
146
  # @param parent_comp [Compony::Component] If called from within a component, pass the component to inform the button that it is nested within.
147
- # @param style [Symbol] If present, overrides the class of the generated button component, defaults to {Compony#default_button_style}.
147
+ # @param style [Symbol] If present, overrides the class of the generated button component, defaults to {Compony.default_button_style}.
148
148
  # @param button_arg_overrides [Hash] Any further kwargs are passed to the button component's initializer.
149
149
  def render(controller, parent_comp = nil, style: nil, **button_arg_overrides)
150
150
  # Abort if not authorized