compony 0.7.1 → 0.8.0

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 (119) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +34 -0
  3. data/Gemfile.lock +1 -1
  4. data/README.md +10 -14
  5. data/VERSION +1 -1
  6. data/compony.gemspec +4 -4
  7. data/doc/ComponentGenerator.html +1 -1
  8. data/doc/Components.html +1 -1
  9. data/doc/ComponentsGenerator.html +1 -1
  10. data/doc/Compony/Component.html +193 -457
  11. data/doc/Compony/ComponentMixins/Default/Labelling.html +1 -1
  12. data/doc/Compony/ComponentMixins/Default/Standalone/ResourcefulVerbDsl.html +1 -1
  13. data/doc/Compony/ComponentMixins/Default/Standalone/StandaloneDsl.html +3 -3
  14. data/doc/Compony/ComponentMixins/Default/Standalone/VerbDsl.html +1 -1
  15. data/doc/Compony/ComponentMixins/Default/Standalone.html +187 -1
  16. data/doc/Compony/ComponentMixins/Default.html +1 -1
  17. data/doc/Compony/ComponentMixins/Resourceful.html +2 -2
  18. data/doc/Compony/ComponentMixins.html +1 -1
  19. data/doc/Compony/Components/Button.html +2 -2
  20. data/doc/Compony/Components/Buttons/CssButton.html +282 -0
  21. data/doc/Compony/Components/Buttons/Link.html +252 -0
  22. data/doc/Compony/Components/Buttons.html +126 -0
  23. data/doc/Compony/Components/Destroy.html +11 -11
  24. data/doc/Compony/Components/Edit.html +14 -14
  25. data/doc/Compony/Components/Form.html +100 -100
  26. data/doc/Compony/Components/Index.html +2 -2
  27. data/doc/Compony/Components/List.html +3 -3
  28. data/doc/Compony/Components/New.html +2 -2
  29. data/doc/Compony/Components/Show.html +24 -24
  30. data/doc/Compony/Components/WithForm.html +3 -3
  31. data/doc/Compony/Components.html +5 -3
  32. data/doc/Compony/ControllerMixin.html +2 -2
  33. data/doc/Compony/Engine.html +1 -1
  34. data/doc/Compony/ExposedIntentsDsl.html +403 -0
  35. data/doc/Compony/Intent.html +1503 -0
  36. data/doc/Compony/MethodAccessibleHash.html +1 -1
  37. data/doc/Compony/ModelFields/Anchormodel.html +1 -1
  38. data/doc/Compony/ModelFields/Association.html +2 -2
  39. data/doc/Compony/ModelFields/Attachment.html +1 -1
  40. data/doc/Compony/ModelFields/Base.html +1 -1
  41. data/doc/Compony/ModelFields/Boolean.html +1 -1
  42. data/doc/Compony/ModelFields/Color.html +1 -1
  43. data/doc/Compony/ModelFields/Currency.html +1 -1
  44. data/doc/Compony/ModelFields/Date.html +1 -1
  45. data/doc/Compony/ModelFields/Datetime.html +1 -1
  46. data/doc/Compony/ModelFields/Decimal.html +1 -1
  47. data/doc/Compony/ModelFields/Email.html +1 -1
  48. data/doc/Compony/ModelFields/Float.html +1 -1
  49. data/doc/Compony/ModelFields/Integer.html +1 -1
  50. data/doc/Compony/ModelFields/Percentage.html +1 -1
  51. data/doc/Compony/ModelFields/Phone.html +1 -1
  52. data/doc/Compony/ModelFields/RichText.html +1 -1
  53. data/doc/Compony/ModelFields/String.html +1 -1
  54. data/doc/Compony/ModelFields/Text.html +1 -1
  55. data/doc/Compony/ModelFields/Time.html +1 -1
  56. data/doc/Compony/ModelFields/Url.html +1 -1
  57. data/doc/Compony/ModelFields.html +1 -1
  58. data/doc/Compony/ModelMixin.html +1 -1
  59. data/doc/Compony/NaturalOrdering.html +1 -1
  60. data/doc/Compony/RequestContext.html +177 -14
  61. data/doc/Compony/Version.html +1 -1
  62. data/doc/Compony/ViewHelpers.html +15 -272
  63. data/doc/Compony/VirtualModel.html +1 -1
  64. data/doc/Compony.html +303 -837
  65. data/doc/ComponyController.html +1 -1
  66. data/doc/_index.html +30 -2
  67. data/doc/class_list.html +1 -1
  68. data/doc/file.README.html +11 -18
  69. data/doc/guide/basic_component.md +12 -8
  70. data/doc/guide/example.md +17 -17
  71. data/doc/guide/feasibility.md +4 -2
  72. data/doc/guide/generators.md +4 -2
  73. data/doc/guide/inheritance.md +4 -2
  74. data/doc/guide/installation.md +4 -2
  75. data/doc/guide/intents.md +167 -0
  76. data/doc/guide/internal_datastructures.md +4 -2
  77. data/doc/guide/model_fields.md +4 -2
  78. data/doc/guide/nesting.md +5 -3
  79. data/doc/guide/ownership.md +5 -3
  80. data/doc/guide/pre_built_components/destroy.md +3 -3
  81. data/doc/guide/pre_built_components/edit.md +1 -1
  82. data/doc/guide/pre_built_components/form.md +1 -1
  83. data/doc/guide/pre_built_components/index.md +1 -1
  84. data/doc/guide/pre_built_components/list.md +1 -1
  85. data/doc/guide/pre_built_components/new.md +2 -2
  86. data/doc/guide/pre_built_components/show.md +1 -1
  87. data/doc/guide/pre_built_components/with_form.md +1 -1
  88. data/doc/guide/pre_built_components.md +4 -3
  89. data/doc/guide/resourceful.md +5 -3
  90. data/doc/guide/standalone.md +10 -2
  91. data/doc/guide/virtual_models.md +4 -2
  92. data/doc/index.html +11 -18
  93. data/doc/method_list.html +273 -161
  94. data/doc/top-level-namespace.html +1 -1
  95. data/lib/compony/component.rb +19 -48
  96. data/lib/compony/component_mixins/default/standalone/standalone_dsl.rb +2 -2
  97. data/lib/compony/component_mixins/default/standalone.rb +16 -0
  98. data/lib/compony/component_mixins/resourceful.rb +1 -1
  99. data/lib/compony/components/buttons/css_button.rb +32 -0
  100. data/lib/compony/components/buttons/link.rb +31 -0
  101. data/lib/compony/components/destroy.rb +9 -8
  102. data/lib/compony/components/edit.rb +5 -4
  103. data/lib/compony/components/form.rb +7 -1
  104. data/lib/compony/components/index.rb +2 -2
  105. data/lib/compony/components/list.rb +4 -4
  106. data/lib/compony/components/new.rb +1 -1
  107. data/lib/compony/components/show.rb +8 -11
  108. data/lib/compony/components/with_form.rb +1 -1
  109. data/lib/compony/exposed_intents_dsl.rb +29 -0
  110. data/lib/compony/intent.rb +145 -0
  111. data/lib/compony/model_fields/association.rb +1 -1
  112. data/lib/compony/request_context.rb +21 -0
  113. data/lib/compony/view_helpers.rb +5 -48
  114. data/lib/compony.rb +63 -149
  115. metadata +12 -6
  116. data/doc/guide/helpers.md +0 -156
  117. data/doc/guide/pre_built_components/button.md +0 -8
  118. data/doc/guide/root_actions.md +0 -67
  119. data/lib/compony/components/button.rb +0 -61
@@ -0,0 +1,167 @@
1
+ [Back to the guide](/README.md#guide--documentation)
2
+
3
+ # Intents
4
+
5
+ An intent is a gateway to a component, along with relevant context. It encapsulates tools used to generate paths, checking [feasibility](/doc/guide/feasibility.md) and rendering links and buttons pointing other components within your application.
6
+
7
+ To instanciate an intent, use either the raw `Compony.intent` method or one of the [helpers](#helpers). All methods that use intents under the hood have a similar interface, especially regarding the first two positional arguments. The most commonly used forms are:
8
+
9
+ - `Compony.intent(:index, :users)`: point to a component by its comp and family name
10
+ - `Compony.intent(:show, User.first)`: pass the intent a single model and let it figure out the family name from `model_name`
11
+ - `Compony.intent(:list, current_user.quotes)`: pass the intent an active record collection and let it figure out the family name from `model_name`
12
+ - `Compony.intent(Components::Users::Index)`: point to a component by giving its class as a single argument
13
+
14
+ The returned intent can then be used to:
15
+
16
+ - Retrieve the target component class using `intent.comp_class`
17
+ - Build an instance of the target component using `intent.comp`
18
+ - If a model was given when building the intent, such as in the second form above, the comp instance will contain it as `@data`.
19
+ - Retrieve the `path` to the target component (only works for standalone components; will automatically set ID parameter if a model was given)
20
+ - Retrieve the `name` that can be used to store the intent in a hash or similar
21
+ - Retrieve the `label` that can be used to refer to the component (if a model was given, passes it to the target component's label block)
22
+ - Check for [feasibility](/doc/guide/feasibility.md) using `feasible?`
23
+ - Render a [button](#buttons-and-styles) to the target component which automatically includes all of the above along with the suitable behavior using `render` and passing a controller
24
+
25
+ An intent's behavior can be customized by passing some of the following keyword arguments when building it:
26
+
27
+ - `standalone_name` allows you to point to another endpoint within your target component, which is especially useful for generating paths. Keep in mind that within each standalone name, multiple HTTP methods can exist. This argument defaults to `nil`, which is the main endpoint created by `standalone`.
28
+ - `method` defines the HTTP verb within the standalone configuration that should be addressed. Defaults to `:get`, but can be overriden to be `:patch`, `:put`, `:post` or `:delete`. When generating a button, the `turbo_method` will be automatically be derived from this argument.
29
+ - `name` overrides the auto-generated name of the intent, affecting the result of the reader of the same name.
30
+ - `label` accepts two forms:
31
+ - When passing a String, it will be returned as-is when returning the label, also affecting buttons generated by this intent.
32
+ - When passing a Hash, any included key-value pair will be used as keyword arguments to the `label` reader method.
33
+ - `path` allows altering generated paths and accepts either a String or a Hash just like `label`.
34
+ - `data` and `data_class` will be given to the target component if/when it gets instanciated. This is used to point to [resourceful](/doc/guide/resourceful.md) components. If a model was passed as the second positional argument (instead of a family name), it will become `data` and this argument can be omitted. If `data` responds to `model_name`, it will be considered a model-like class.
35
+ - `feasibility_target` and `feasibility_action` can be given to alter the behavior of the `feasible?` reader.
36
+ - Any further arguments are passed to the initializer of the button if/when the intent gets rendered.
37
+
38
+ ## Helpers
39
+
40
+ In practice, you will rarely call `Compony.intent` directly, and likely never `Compony::Intent.new`. Instead, you will be interacting with one of the following helpers that will instanciate an intent under the hood and thus all accept similar arguments as those described above:
41
+
42
+ ### `Compony.path`
43
+
44
+ This helper is useful when generating paths without rendering any HTML, such as when redirecting. Internally, this builds an intent which will in turn use the target component's `path` block to generate a Rails path (String) pointing to the correct location. Any keyword arguments are passed to the intent's `path` method, allowing you to override the model (to refer to [resourceful](/doc/guide/resourceful.md) target components), as well as specifying a `standalone_name` or pass extra arguments to the target component's `path` block.
45
+
46
+ Examples:
47
+
48
+ ```ruby
49
+ redirect_to Compony.path(:index, :users) # Redirects to /users
50
+ redirect_to Compony.path(:show, @data.author) # Redirects to something like /authors/42
51
+ redirect_to Compony.path(:thank_you, @data) # Redirects to something like /feedbacks/42/thank_you
52
+ redirect_to Compony.path(:step_2, :registrations, accept_terms: :yes) # Redirects to something like /registrations/step_2?accept_terms=yes
53
+ ```
54
+
55
+ ### `render_intent`
56
+
57
+ This is the preferred way of quickly rendering [links or buttons to components](#buttons-and-styles) from other parts of your application. The method is implemented twice:
58
+
59
+ - When called from within a `content` block of a component, the method implemented in the `RequestContext` is used, which automatically detects the component from which it is called and passes it as `parent_comp` to the rendered button.
60
+ - When called from a regular Rails view, the Rails helper method is used, which instanciates a button without passing a `parent_comp`.
61
+
62
+ Use the `button` argument to customize the generated button. Example:
63
+
64
+ ```ruby
65
+ setup do
66
+ content do
67
+ div render_intent(:show, User.first, button: { style: :link, label: { format: :short } })`
68
+ end
69
+ end
70
+ ```
71
+
72
+ In the example above, there is a lot more going on than it seems. Due to the [specified style](#buttons-and-styles), a plain HTML link will be generated, pointing to the component `Components::Users::Show` and instanciating it with the first user as `@data`. Assuming that component inherits from `Compony::Components::Show` and did not override label or path, the link will be labelled "Show" (which is the default short format label generated by Compony's default [pre-built Show component](/doc/guide/pre_built_components/show.md)), and the `href` will be `/users/:id` where ID will automatically be `User.first`'s ID (e.g. `/users/1`). However, if `:show` was [prevented](/doc/guide/feasibility.md), the link will be strikethrough, non-clickable, greyed out and have a title explaining why it can't be clicked. Further, if the current user does not have [authorization](/doc/guide/standalone.md) to display the target user, the link will not show up at all, and no HTML will be generated within the `div`.
73
+
74
+ ### `render_sub_comp`
75
+
76
+ This is used within a component's `content` block to instanciate another component and [nest it within](/doc/guide/nesting.md). Internally, the current component's `sub_comp` method is used and all arguments are passed to that.
77
+
78
+ For example, let us consider you want to build your user management's Show component to include the list of quotes belonging to the displayed user. Assuming you have already built `Components::Quotes::List` for quotes' Index component, this helper allows you to simply write:
79
+
80
+ ```ruby
81
+ class Components::Users::Show < Compony::Components::Show
82
+ setup do
83
+ # ...
84
+ content :quotes do
85
+ concat render_sub_comp(:list, @data.quotes)
86
+ end
87
+ end
88
+ end
89
+ ```
90
+
91
+ This implicitely builds an intent which auto-detects the family name `:quotes` from `@data.quotes`, which is an active record collection and thus implements `model_name`. The List component is then instanciated with its `@data` being that very collection and the users's Show component as `parent_comp`, resulting in the proper nesting and display of the desired resources.
92
+
93
+ ### `Compony.comp_class_for`
94
+
95
+ This helper is useful for checking whether a component is implemented. For instance, when implementing [abstract components to inherit from later](/doc/guide/inheritance.md), you can check for `if Compony.comp_class_for(:destroy, family_name)` to only provide some functionality of a Destroy component exists for the current family.
96
+
97
+ This method also has its sibling `Compony.comp_class_for!`, which will fail if no such component could be found. It is however mostly used internally.
98
+
99
+ ## Buttons and styles
100
+
101
+ Button components are used as presenters for intents, hyperlinks to other components or submit buttons. They are a central way to define how buttons all over the application should look like. Their interface is adapted to intents, creating a standardized "slim waist" that greatly simplifies linking between components. Note that the term "button" refers to how they look and not how they are actually implemented - actual HTML buttons have several disadvantages (e.g. requiring drop-in forms and not responding to Ctrl+Click or middle-click when the user would prefer a new tab).
102
+
103
+ Compony comes with two button styles:
104
+
105
+ - `:css_button` is the default button style and creates a div that looks similar to a HTML button rendered in Firefox. If the class `disabled` is given, it is greyed out.
106
+ - `:link` is mostly just a regular `<a>` tag, but will appear greyed out and strikethrough if the class `disabled` is given.
107
+
108
+ All button styles support the following keyword arguments to their initializer:
109
+
110
+ - `label` is a String which will be displayed as the text of the link or button.
111
+ - `href` is the url/path that the button points to. If `nil`, will change to `javascript:void(0)`.
112
+ - `method` takes a HTTP verb will generate a suitable `turbo_method` data attribute.
113
+ - `class` will mostly let be as-is, but checked for the `disabled` class - if given, the style will be ovewritten.
114
+
115
+ Note that since buttons are full components, they can be [nested](/doc/guide/nesting.md) into another component by providing `parent_comp`. If called from within a component's `content` block, the helper `render_intent` does this automatically.
116
+
117
+ As implicitely mentioned above, Compony buttons are referenced to by a name called a style (`:css_button` actually points to `Compony::Components::Buttons::CssButton`). When rendering an intent, the style can be passed as an argument: `render_intent(:show, User.first, button: { style: :link })` and the intent will automatically instanciate the desired component class.
118
+
119
+ Note: it is possible to use a button component to submit a form. In order to achieve this, you must implement a hidden submit button (for handling keyboard Enter and Return), as well as pass `onclick: "this.closest('form').requestSubmit(); return false;"` as an argument. See the pre-built Form component's implementation for an example.
120
+
121
+ ### Adding your own styles
122
+
123
+ In your application, you will likely want to implement your own button styles. Create a component (e.g. `Components::Commons::MyButton`) and inherit from `Compony::Components::Buttons::Link`. Override the method `prepare_opts!` and don't forget to call `super` first. Then, go through any `@comp_args` that might be of interest to you and mutate `@comp_args[:style]` and/or `@comp_args[:class]` to suit your needs. Make sure to handle the class `disabled`, as intents will set them if the intent is not feasible. Note that if a user is lacking authorization to perform an intent, the intent will not even instanciate the button.
124
+
125
+ Once your button class is ready, register it in `config/initializers/compony.rb` with: `Compony.register_button_style :my_button, '::Components::Commons::MyButton'`. You can also change the default button style there using: `Compony.default_button_style = :my_button`.
126
+
127
+ If you have multiple kinds of buttons (e.g. dropdown items, pill-style buttons, compact forms etc.), you should create a separate style and button component class for every kind. This will make it easy to refer to them by supplying something like `button: { style: :dropdown_item }` in `render_intent`.
128
+
129
+ ## Exposed intents
130
+
131
+ Components can expose a set of intents to be displayed elsewhere. Those can either be rendered by the parent comp, or by the application layout itself in case the component exposing them is currently `root_comp` (see the chapter about [standalone](/doc/guide/standalone.md)). This is useful if you have something like an actions toolbar that changes depending on the currently contained component.
132
+
133
+ To expose an intent, proceed as shown in the following example:
134
+
135
+ ```ruby
136
+ # ...
137
+ class Components::Quotes::Show < Compony::Components::Show
138
+ setup do
139
+ exposed_intents do
140
+ add :index, family_name, label: 'Show all', name: :index
141
+ remove :destroy
142
+ end
143
+ end
144
+ end
145
+ ```
146
+
147
+ In this example, the shown component exposes an intent pointing to the Index component of it's own family (`:users`). The `:name` argument causes the new intent to be just named `:index` rather than `:index_users`. This is for the sake of the example and would allow a component inheriting from this component to use `exposed_intents { remove :index }` rather than `exposed_intents { remove :index_users }`.
148
+
149
+ Similarly, this component removes the exposed intent `:destroy` which it whould otherwise inherit from `Comopony::Components::Show`. Every call to `exposed_intents` overrides properties set in previous calls by the same component, primarly useful for [reusing components by inheritance](/doc/guide/inheritance.md).
150
+
151
+ In order to replace an existing intent defined by a previous call to `exposed_intents` (e.g. in a parent class), simply call `add` again and make sure the intent's name matches that of the one to override. `add` also accepts the `before:` keyword, allowing you to reorder intents or insert a new one into a specific place in the intent list.
152
+
153
+ Note that the `add` method has full intent argument support and thus also accepts parameters related to the button (e.g. `style`), path generation, feasibility etc.
154
+
155
+ ### Rendering exposed intents
156
+
157
+ You can render exposed intents in the parent component or in the application layout. To do so, call `component.exposed_intents`, loop across them and call `.render(controller)` on each (perhaps inside a `div` tag or whatever suits your needs).
158
+
159
+ Example in `layouts/application.html.erb`
160
+
161
+ ```erb
162
+ <% Compony.root_comp&.exposed_intents&.each do |intent| %>
163
+ <div class="root-intent"><%= intent.render(controller) %>
164
+ <% end %>
165
+ ```
166
+
167
+ [Guide index](/README.md#guide--documentation)
@@ -1,4 +1,4 @@
1
- [Back to the guide](/README.md#guide)
1
+ [Back to the guide](/README.md#guide--documentation)
2
2
 
3
3
  # Internal datastructures
4
4
 
@@ -42,4 +42,6 @@ RequestContext further provides the following methods on its own:
42
42
  - `evaluate_with_backfire` is `evaluate` with enabled backfiring.
43
43
  - `component` returns the component the RequestContext was instantiated with.
44
44
  - `request_context` returns self. This is for disambiguation purposes.
45
- - Any call to an unknown method will first be evaluated as a potential hit in `locals`. Only if no matching local is found, Dslblend takes over.
45
+ - Any call to an unknown method will first be evaluated as a potential hit in `locals`. Only if no matching local is found, Dslblend takes over.
46
+
47
+ [Guide index](/README.md#guide--documentation)
@@ -1,4 +1,4 @@
1
- [Back to the guide](/README.md#guide)
1
+ [Back to the guide](/README.md#guide--documentation)
2
2
 
3
3
  # Model fields
4
4
 
@@ -59,4 +59,6 @@ Example:
59
59
  Compony.model_field_namespaces = ['MyCustomModelFields', 'Compony::ModelFields']
60
60
  ```
61
61
 
62
- You can then implement `MyCustomModelFields::Animal`, `MyCustomModelFields::String` etc. You can then use `field :fav_animal, :animal` in your model.
62
+ You can then implement `MyCustomModelFields::Animal`, `MyCustomModelFields::String` etc. You can then use `field :fav_animal, :animal` in your model.
63
+
64
+ [Guide index](/README.md#guide--documentation)
data/doc/guide/nesting.md CHANGED
@@ -1,4 +1,4 @@
1
- [Back to the guide](/README.md#guide)
1
+ [Back to the guide](/README.md#guide--documentation)
2
2
 
3
3
  # Nesting
4
4
 
@@ -12,7 +12,7 @@ Nesting occurs when a component is being rendered. It is perfectly feasible to u
12
12
 
13
13
  Note that only the root component runs authentication and authorization. Thus, be careful which components you nest.
14
14
 
15
- To create a sub-component, use `sub_comp` in a component's content block. Any keyword arguments given will be passed to the sub-component. It is strictly recommended to exclusively use `sub_comp` (or its [resourceful](./resourceful.md) pendent) to nest components, as this method makes a component aware of its exact nesting.
15
+ To create a sub-component, use `render_sub_comp` in a component's content block. Any keyword arguments given will be passed to the sub-component. It is strictly recommended to exclusively use `render_sub_comp`, `sub_comp` or its [resourceful](./resourceful.md#nesting-resourceful-components) pendent to nest components, as this method makes a component aware of its exact nesting.
16
16
 
17
17
  Here is a simple example of a component that displays numbers as binary:
18
18
 
@@ -129,4 +129,6 @@ The number 4 has the binary form 100. Enter a number and press ENTER: [4]
129
129
  The number 8 has the binary form 1000. Enter a number and press ENTER: [8]
130
130
  ```
131
131
 
132
- Note that this example is completely stateless, as all the info is encoded in the URL.
132
+ Note that this example is completely stateless, as all the info is encoded in the URL.
133
+
134
+ [Guide index](/README.md#guide--documentation)
@@ -1,4 +1,4 @@
1
- [Back to the guide](/README.md#guide)
1
+ [Back to the guide](/README.md#guide--documentation)
2
2
 
3
3
  # Ownership
4
4
 
@@ -11,11 +11,13 @@ In Compony, if a model class is owned by another, it means that:
11
11
 
12
12
  - The owned model has a non-optional `belongs_to` relation ship to its owner.
13
13
  - The owned model class has no Index component.
14
- - Pre-built components (more on them later) offer root actions to the owner model and redirect to its Show component instead of to the current object's Index component.
14
+ - [Pre-built components](/doc/guide/pre_built_components.md) offer [exposed intents](/doc/guide/intents.md#exposed-intents) to the owner model and redirect to its Show component instead of to the current object's Index component.
15
15
 
16
16
  To mark a model as owned by another, write the following code **in the model**:
17
17
 
18
18
  ```ruby
19
19
  # app/models/permission.rb
20
20
  owned_by :user
21
- ```
21
+ ```
22
+
23
+ [Guide index](/README.md#guide--documentation)
@@ -1,4 +1,4 @@
1
- - [Back to the guide](/README.md#guide)
1
+ - [Back to the guide](/README.md#guide--documentation)
2
2
  - [List of pre-built components](/doc/guide/pre_built_components.md)
3
3
 
4
4
  # Pre-built components: Destroy
@@ -12,11 +12,11 @@ This component is the Compony equivalent to a typical Rails controller's `destro
12
12
  - if present: the data's Show component
13
13
  - otherwise: the data's Index component
14
14
 
15
- Authorization checks for `destroy` even in GET. The reason is that users that aren't able to destroy a resource shouldn't even arrive at the page asking them whether they want to do so, unable to click the only button due to lacking permissions. This also causes any `compony_link` and `compony_button` to Destroy components to be hidden if the user is unable to destroy the corresponding resource.
15
+ Authorization checks for `destroy` even in GET. The reason is that users that aren't able to destroy a resource shouldn't even arrive at the page asking them whether they want to do so, unable to click the only button due to lacking permissions. This also causes any [intents](/doc/guide/intents.md) to Destroy components to be hidden if the user is unable to destroy the corresponding resource.
16
16
 
17
17
  This component largely follows the [resourceful lifecycle](/doc/guide/resourceful.md#complete-resourceful-lifecycle). As can be expected, the resource is loaded by `Resourceful`'s default load block and `store_data` is implemented to destroy the resource.
18
18
 
19
- If the resource is [owned](/doc/guide/ownership.md), the component provides a `:back_to_owner` root action in the form of a cancel button.
19
+ If the resource is [owned](/doc/guide/ownership.md), the component provides a `:back_to_owner` [exposed intent](/doc/guide/intents.md#exposed-intents) in the form of a cancel button.
20
20
 
21
21
  The following DSL methods are implemented to allow for convenient overrides of default logic:
22
22
 
@@ -1,4 +1,4 @@
1
- - [Back to the guide](/README.md#guide)
1
+ - [Back to the guide](/README.md#guide--documentation)
2
2
  - [List of pre-built components](/doc/guide/pre_built_components.md)
3
3
 
4
4
  # Pre-built components: Edit
@@ -1,4 +1,4 @@
1
- - [Back to the guide](/README.md#guide)
1
+ - [Back to the guide](/README.md#guide--documentation)
2
2
  - [List of pre-built components](/doc/guide/pre_built_components.md)
3
3
 
4
4
  # Pre-built components: Form
@@ -1,4 +1,4 @@
1
- - [Back to the guide](/README.md#guide)
1
+ - [Back to the guide](/README.md#guide--documentation)
2
2
  - [List of pre-built components](/doc/guide/pre_built_components.md)
3
3
 
4
4
  # Pre-built components: Index
@@ -1,4 +1,4 @@
1
- - [Back to the guide](/README.md#guide)
1
+ - [Back to the guide](/README.md#guide--documentation)
2
2
  - [List of pre-built components](/doc/guide/pre_built_components.md)
3
3
 
4
4
  # Pre-built components: List
@@ -1,4 +1,4 @@
1
- - [Back to the guide](/README.md#guide)
1
+ - [Back to the guide](/README.md#guide--documentation)
2
2
  - [List of pre-built components](/doc/guide/pre_built_components.md)
3
3
 
4
4
  # Pre-built components: New
@@ -13,7 +13,7 @@ This component is the Compony equivalent to a typical Rails controller's `new` a
13
13
  - otherwise, if the resource is owned by another resource class: the owner's Show component
14
14
  - otherwise, the data's Index component
15
15
 
16
- Authorization checks for `create` even in GET. The reason is that it makes no sense to present an empty form to a user who cannot create a new record. This also causes any `compony_link` and `compony_button` to New components to be hidden to users lacking the permission.
16
+ Authorization checks for `create` even in GET. The reason is that it makes no sense to present an empty form to a user who cannot create a new record. This also causes any [intents](/doc/guide/intents.md) to New components to be hidden to users lacking the permission.
17
17
 
18
18
  This component follows the [resourceful lifecycle](/doc/guide/resourceful.md#complete-resourceful-lifecycle). `load_data` is set to create a new record and `store_data` attempts to create it. Parameters are validated in `assign_attributes` using a Schemacop schema that is generated from the form. The schema corresponds to Rail's typical strong parameter structure for forms. For example, a user's New component would look for a parameter `user` holding a hash of attributes (e.g. `user[first_name]=Tom`).
19
19
 
@@ -1,4 +1,4 @@
1
- - [Back to the guide](/README.md#guide)
1
+ - [Back to the guide](/README.md#guide--documentation)
2
2
  - [List of pre-built components](/doc/guide/pre_built_components.md)
3
3
 
4
4
  # Pre-built components: Show
@@ -1,4 +1,4 @@
1
- - [Back to the guide](/README.md#guide)
1
+ - [Back to the guide](/README.md#guide--documentation)
2
2
  - [List of pre-built components](/doc/guide/pre_built_components.md)
3
3
 
4
4
  # Pre-built components: WithForm
@@ -1,4 +1,4 @@
1
- [Back to the guide](/README.md#guide)
1
+ [Back to the guide](/README.md#guide--documentation)
2
2
 
3
3
  # Pre-built components shipped with Compony
4
4
 
@@ -8,7 +8,6 @@ The pre-built components can be found in the module `Compony::Components`. As yo
8
8
 
9
9
  In the following, the pre-built components currently shipped with Compony are presented:
10
10
 
11
- - [Button](./pre_built_components/button.md): This component class gets instanciated whenever using `Compony.button` or `compony_button`.
12
11
  - [Show](./pre_built_components/show.md): Compony's equivalent to Rail's `show` controller action
13
12
  - [Index](./pre_built_components/index.md): Compony's equivalent to Rail's `index` controller action
14
13
  - [List](./pre_built_components/list.md): Compony's equivalent to Rail's `_list` partial
@@ -16,4 +15,6 @@ In the following, the pre-built components currently shipped with Compony are pr
16
15
  - [WithForm](./pre_built_components/with_form.md): A base class for components containing and submitting forms
17
16
  - [Form](./pre_built_components/form.md): Compony's equivalent to Rail's `_form` partial
18
17
  - [New](./pre_built_components/new.md): Compony's equivalent to Rail's `new` and `create` controller action
19
- - [Edit](./pre_built_components/new.md): Compony's equivalent to Rail's `edit` and `update` controller action
18
+ - [Edit](./pre_built_components/new.md): Compony's equivalent to Rail's `edit` and `update` controller action
19
+
20
+ [Guide index](/README.md#guide--documentation)
@@ -1,4 +1,4 @@
1
- [Back to the guide](/README.md#guide)
1
+ [Back to the guide](/README.md#guide--documentation)
2
2
 
3
3
  # Resourceful components
4
4
 
@@ -66,7 +66,7 @@ class Components::Users::Destroy < Compony::Component
66
66
  label(:long) { |data| "Delete #{data.label}" }
67
67
  content do
68
68
  h1 "Are you sure to delete #{@data.label}?"
69
- div compony_button(:destroy, @data, label: 'Yes, delete', method: :delete)
69
+ div render_intent(:destroy, @data, label: 'Yes, delete', method: :delete)
70
70
  end
71
71
  end
72
72
  end
@@ -98,4 +98,6 @@ The rule of thumb thus becomes:
98
98
 
99
99
  - When a resourceful component instantiates a resourceful sub-component, use `resourceful_sub_comp` in the parent component.
100
100
  - When a resourceful component instantiates a non-resourceful sub-component, use `sub_comp`.
101
- - The situation where a non-resourceful component instantiates a resourceful component should not occur. Instead, make your parent component resourceful, even if it doesn't use the data itself. By housing a resourceful sub-comp, the parent component's nature inherently becomes resourceful and you should use the Resourceful mixin.
101
+ - The situation where a non-resourceful component instantiates a resourceful component should not occur. Instead, make your parent component resourceful, even if it doesn't use the data itself. By housing a resourceful sub-comp, the parent component's nature inherently becomes resourceful and you should use the Resourceful mixin.
102
+
103
+ [Guide index](/README.md#guide--documentation)
@@ -1,4 +1,4 @@
1
- [Back to the guide](/README.md#guide)
1
+ [Back to the guide](/README.md#guide--documentation)
2
2
 
3
3
  # Standalone (routing to components)
4
4
 
@@ -133,4 +133,12 @@ end
133
133
  scope '(:lang)', lang: /([a-z]{2})?/i do
134
134
  get 'welcome', to: 'compony#your_component'
135
135
  end
136
- ```
136
+ ```
137
+
138
+ ## Customizing path generation
139
+
140
+ By implementing `path do ... end` inside the `setup` method of a component, you can override the way paths to that component are generated. Customizing the path generation will affect all mentioned methods mentioned here involving paths, such as `Compony.path`, `render_intent` etc.
141
+
142
+ This is an advanced usage. Refer to the default implementation of `Component`'s `path_block` to see an example.
143
+
144
+ [Guide index](/README.md#guide--documentation)
@@ -1,4 +1,4 @@
1
- [Back to the guide](/README.md#guide)
1
+ [Back to the guide](/README.md#guide--documentation)
2
2
 
3
3
  # Unleashing virtual models through Compony's `ActiveType` integration
4
4
 
@@ -26,4 +26,6 @@ Why this works: As your `Components::Reports::Request` inherits from Compony's `
26
26
 
27
27
  Note: it is even possible to combine this pattern with Rails' `accepts_nested_attributes_for` and `simple_form`'s `f.simple_fields_for` call, where the nested object is a real database-backed model. Even though the component's resource is purely virtual, Rails will create or update the nested model when Compony calls `save` on the parent resource. This allows for very fast implementation of business logic creating multiple objects from a single form post by wrapping the resources in a virtual model.
28
28
 
29
- If you intend to use this technique in combination with `ActiveStorage`, you must also override the `store_data` block to just validate the model instead of saving it, as the hook creating the attachment is bound to fail (the virtual model does not exist in the database and thus cannot be referenced from `ActiveStorage::Attachment`). For the same reason, you cannot call `blob.download`, but must find the file's tempfile in the request parameters in order to process the file attached by the user.
29
+ If you intend to use this technique in combination with `ActiveStorage`, you must also override the `store_data` block to just validate the model instead of saving it, as the hook creating the attachment is bound to fail (the virtual model does not exist in the database and thus cannot be referenced from `ActiveStorage::Attachment`). For the same reason, you cannot call `blob.download`, but must find the file's tempfile in the request parameters in order to process the file attached by the user.
30
+
31
+ [Guide index](/README.md#guide--documentation)
data/doc/index.html CHANGED
@@ -71,7 +71,7 @@
71
71
 
72
72
  <h1 id="label-About+Compony">About Compony</h1>
73
73
 
74
- <p>Compony is a Gem that allows you to write your Rails application in component-style fashion. It combines a controller action and route along with its view into a single Ruby class. This allows writing much DRYer code, using inheritance even in views and much easier refactoring for your Rails applications, helping you to keep the code clean as the application evolves.</p>
74
+ <p>Compony is a Gem that allows you to write your Rails application in <strong>component-style</strong> fashion. It combines a controller action and route along with its view into a single Ruby class. Along with the DSL approach and a powerful model mixin, Compony <strong>makes your application’s code more semantic</strong>, allows writing <strong>much DRYer code</strong>, using inheritance even in views and <strong>much easier refactoring</strong> for your Rails applications, helping you to <strong>keep the code clean as the application evolves</strong>.</p>
75
75
 
76
76
  <p>Compony’s key aspects:</p>
77
77
  <ul><li>
@@ -79,17 +79,18 @@
79
79
  </li><li>
80
80
  <p>Refactor common logic into your own components and inherit from them to DRY up your code.</p>
81
81
  </li><li>
82
- <p>Compony’s powerful model mixin allows you to define metadata in your models and react to them. Examples:</p>
83
- </li><li>
82
+ <p>Compony’s model mixin allows you to define metadata in your models and react to them, resulting in more semantic code. Examples:</p>
83
+ <ul><li>
84
84
  <p>Compony fields capture attributes that should be made visible in your UI. They allow you to implement formatting behavior and parameter sanitization for various types, e.g. URLs, phone numbers, colors etc. ready to be used in your lists, detail panels, or forms.</p>
85
85
  </li><li>
86
86
  <p>Compony’s feasibility framework allows you to prohibit actions based on conditions, along with an error message. This causes all buttons pointing to that action to be disabled with a meaningful error message.</p>
87
+ </li></ul>
87
88
  </li><li>
88
89
  <p>Compony only structures your code, but provides no style whatsoever. It is like a bookshelf rather than a reader’s library. You still implement your own layouts, CSS and Javascript to define the behavior of your front-end.</p>
89
90
  </li><li>
90
- <p>Using Compony, you <strong>can</strong> write your application as components, but it is still possible to have regular routes, controllers and views side-to-side to it. This way, you can migrate your applications to Compony little by little and enter and leave the Compony world as you please. It is also possible to render Compony components from regular views and vice versa.</p>
91
+ <p>Compony seamlessly integrates with Rails and does not interfere with existing code. Using Compony, you <strong>can</strong> write your application as components, but it is still possible to have regular routes, controllers and views side-to-side to it. This way, you can migrate your applications to Compony little by little and enter and leave the Compony world as you please. It is also possible to render Compony components from regular views and vice versa.</p>
91
92
  </li><li>
92
- <p>Compony is built for Rails 7 and fully supports Stimulus and Turbo Drive. Turbo Frames and Streams are not yet targeted, so Compony is currently meant for websites where every click triggers a “full page load” (in quotes because they are not actually full page loads due to Turbo Drive).</p>
93
+ <p>Compony is built for Rails 7, 7.1 and 8, and fully supports Stimulus and Turbo Drive. Turbo Frames and Streams are not yet targeted, but can be used alongside, just like with any Rails application.</p>
93
94
  </li><li>
94
95
  <p>Compony uses <a href="https://github.com/CanCanCommunity/cancancan">CanCanCan</a> for authorization but does not provide an authentication mechanism. You can easily build your own by creating login/logout components that manage cookies, and configure Compony to enforce authentication using the <code>Compony.authentication_before_action</code> setter. I have also successfully tested Compony to work with <a href="https://github.com/heartcombo/devise">Devise</a>.</p>
95
96
  </li></ul>
@@ -98,7 +99,7 @@
98
99
 
99
100
  <p>I am actively using this framework in various applications and both performance and reliability are good. However, the project is experimental and lacking peer reviews and especially automatic testing, such as unit and integration tests. Also, expect there to be (<a href="/CHANGELOG_md.html">documented</a>) breaking changes in the future, as the API will likely be further refined, resulting in renamings and deprecation of various methods.</p>
100
101
 
101
- <h2 id="label-Related+projects">Related projects</h2>
102
+ <h2 id="label-Other+projects+exploring+similar+concepts">Other projects exploring similar concepts</h2>
102
103
 
103
104
  <p>A project with a similar aim, but a different approach, is <a href="https://github.com/phlex-ruby/phlex">Phlex</a>.</p>
104
105
 
@@ -122,11 +123,9 @@
122
123
  </li><li>
123
124
  <p><a href="./doc/guide/resourceful_md.html">Resourceful components</a>: How to create components that deal with Rails-style resources</p>
124
125
  </li><li>
125
- <p><a href="./doc/guide/helpers_md.html">Compony helpers, links and buttons</a>: Important tools for concise Compony programming</p>
126
- </li><li>
127
- <p><a href="./doc/guide/root_actions_md.html">Root actions</a>: How to provide context-sensitive buttons to your application</p>
126
+ <p><a href="./doc/guide/intents_md.html">Intents</a>: How to point to a component and provide custom components for rendering intents</p>
128
127
  </li><li>
129
- <p><a href="./doc/guide/feasibility_md.html">Feasibility</a>: Disabiling buttons based on context (contains <code>prevent</code>)</p>
128
+ <p><a href="./doc/guide/feasibility_md.html">Feasibility</a>: Disabiling intents based on context (contains <code>prevent</code>)</p>
130
129
  </li><li>
131
130
  <p><a href="./doc/guide/ownership_md.html">Ownership</a>: Informing Compony that a resource is conceptually part of another resource</p>
132
131
  </li><li>
@@ -143,8 +142,6 @@
143
142
  <ul><li>
144
143
  <p><a href="./doc/guide/pre_built_components_md.html">Introduction</a></p>
145
144
  </li><li>
146
- <p><a href="./doc/guide/pre_built_components/button_md.html">Button</a>: This component class gets instanciated whenever using <code>Compony.button</code> or <code>compony_button</code>.</p>
147
- </li><li>
148
145
  <p><a href="./doc/guide/pre_built_components/show_md.html">Show</a>: Compony’s equivalent to Rail’s <code>show</code> controller action</p>
149
146
  </li><li>
150
147
  <p><a href="./doc/guide/pre_built_components/index_md.html">Index</a>: Compony’s equivalent to Rail’s <code>index</code> controller action</p>
@@ -189,11 +186,7 @@
189
186
  </li><li>
190
187
  <p>At this point, I haven’t gotten into Turbo Streams and Turbo Frames. It would be interesting to extend Compony such it also makes writing applications using these features much easier.</p>
191
188
  </li><li>
192
- <p>Feasibility:</p>
193
- </li><li>
194
- <p>The feasibility framework does not yet enforce prevention, but only has effects on buttons. Actions should be structured more explicitly such that prevention becomes as tight as authorization.</p>
195
- </li><li>
196
- <p>Feasibility for links is not yet implemented.</p>
189
+ <p>The feasibility framework does not yet enforce prevention, but only has effects on buttons.</p>
197
190
  </li><li>
198
191
  <p>Compony is not compatible with <code>tailwindcss-rails</code>. This is likely due to Tailwind automatically removing any CSS that is not used by the application and the usage detection not picking up Compony components, as their content is not provided in views.</p>
199
192
  </li></ul>
@@ -206,7 +199,7 @@
206
199
  </div></div>
207
200
 
208
201
  <div id="footer">
209
- Generated on Thu Nov 20 12:44:23 2025 by
202
+ Generated on Thu Nov 27 16:02:21 2025 by
210
203
  <a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
211
204
  0.9.34 (ruby-3.3.5).
212
205
  </div>