kommandant 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +230 -12
  3. data/app/assets/builds/kommandant.css +413 -145
  4. data/app/assets/config/kommandant_manifest.js +2 -0
  5. data/app/assets/images/kommandant/logo.svg +13 -0
  6. data/app/controllers/kommandant/commands_controller.rb +69 -11
  7. data/app/controllers/kommandant/searches_controller.rb +8 -9
  8. data/app/models/kommandant/command.rb +12 -69
  9. data/app/models/kommandant/commands/search_result.rb +4 -11
  10. data/app/views/kommandant/commands/_command.html.erb +23 -0
  11. data/app/views/kommandant/commands/_form.html.erb +58 -0
  12. data/app/views/kommandant/commands/edit.html.erb +1 -0
  13. data/app/views/kommandant/commands/index.html.erb +29 -0
  14. data/app/views/kommandant/commands/new.html.erb +1 -0
  15. data/app/views/kommandant/commands/searches/show.html.erb +4 -4
  16. data/app/views/kommandant/commands/show.html.erb +2 -2
  17. data/app/views/kommandant/shared/_command_palette.html.erb +2 -3
  18. data/app/views/kommandant/shared/command_palette/_command.html.erb +11 -4
  19. data/app/views/kommandant/shared/command_palette/_result.html.erb +5 -1
  20. data/app/views/kommandant/shared/icons/_kommandant.html.erb +5 -0
  21. data/app/views/layouts/kommandant/application.html.erb +24 -9
  22. data/config/locales/en.yml +11 -0
  23. data/config/routes.rb +1 -1
  24. data/lib/generators/kommandant/USAGE +9 -0
  25. data/lib/generators/kommandant/install_generator.rb +9 -0
  26. data/lib/generators/kommandant/templates/initializer.rb +31 -0
  27. data/lib/kommandant/engine.rb +2 -2
  28. data/lib/kommandant/version.rb +1 -1
  29. data/lib/kommandant.rb +4 -0
  30. data/lib/tasks/kommandant_tasks.rake +31 -4
  31. data/vendor/assets/javascripts/command_palette.js +120 -0
  32. data/vendor/assets/javascripts/keyboard_navigation.js +72 -0
  33. data/vendor/assets/javascripts/kommandant.js +2 -0
  34. data/vendor/assets/javascripts/transition.js +57 -0
  35. metadata +36 -22
  36. /data/app/views/kommandant/shared/icons/{_command.erb → _command.html.erb} +0 -0
  37. /data/app/views/kommandant/shared/icons/{_search.erb → _search.html.erb} +0 -0
  38. /data/app/views/kommandant/shared/icons/{_spinner.erb → _spinner.html.erb} +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8b3c12cb6c51778cd6575684e3c818960bc1db275aa4f2f5bc4bf1c39c0c57f6
4
- data.tar.gz: 7cbc64969033ebf7170bce6e65fe414db2c2b9e4ccff31f645f6f385b20c27d5
3
+ metadata.gz: b1f17fe8d3852dd93097d84549ecf664857f1df3efd7ebc76daade654c307a7f
4
+ data.tar.gz: 7a7e8645dadeaa7c3fe256548d62bf4b6a3d4ea905e246798ccb199f6dda15ef
5
5
  SHA512:
6
- metadata.gz: 4cf20342a6a8a5520652fa2949dea0aef98556c052db6c05ee59cb7203bd192dc7a60020af8e7193857efbbf3d545ad42f2dd1f8fe29d9cbf43b269f02bdf78b
7
- data.tar.gz: 63115cdca2ea3fced0cb93466fd5a6941ed3bf3033fc82a0e03afa695b4be80c05b73ef13216d398a9e61bf606346dfbede225d233a3e30dd90f7213a5629ef9
6
+ metadata.gz: d9ff2ee5875cc6555fe63d415e4c2380f6c4e1342520241e9e1c2980ac9518925ea50987f8be578a2a9eb0520f56270bc67d3cab0926313165701528587a81c9
7
+ data.tar.gz: 682a6ce2857d4d500485622d1921bb199c429b04f115ef61b5c86284198ecaf6aa143e2e4100be7d9414b74758046c19a8c831ea7769d154fbb9a49bf28a427b
data/README.md CHANGED
@@ -1,28 +1,246 @@
1
- # Kommandant
2
- Short description and motivation.
1
+ ![Kommandant logo](app/assets/images/kommandant/logo.svg "Kommandant logo")
2
+
3
+ Do you need a command palette? Would you like to do as little work as possible to get one? Then you've come to the right place!
4
+
5
+ Kommandant is a command palette engine for Rails. It is built with Hotwire for the slick interaction and Meilisearch as the super-fast search engine.
3
6
 
4
- ## Usage
5
- How to use my plugin.
6
7
 
7
8
  ## Installation
8
- Add this line to your application's Gemfile:
9
9
 
10
+ You need to have Meilisearch installed on your system. They have some pretty nice docs describing how right [here.](https://www.meilisearch.com/docs/learn/getting_started/installation)
11
+
12
+ Kommandant depends on the [meilisearch-rails gem](https://github.com/meilisearch/meilisearch-rails), which needs to be configured:
10
13
  ```ruby
11
- gem "kommandant"
14
+ # config/initializers/meilisearch.rb
15
+ MeiliSearch::Rails.configuration = {
16
+ meilisearch_url: ENV.fetch("MEILI_HTTP_ADDR") { "http://localhost:7700" },
17
+ meilisearch_api_key: ENV.fetch("MEILI_MASTER_KEY") { "MASTER_KEY" },
18
+ per_environment: true
19
+ }
12
20
  ```
13
21
 
14
- And then execute:
15
- ```bash
16
- $ bundle
22
+ Now run
23
+ ```sh
24
+ bundle add kommandant
25
+ bin/rails kommandant:install:migrations
26
+ bin/rails db:migrate
27
+ ```
28
+
29
+
30
+ and add the following line `app/assets/application.css`
31
+
32
+ ```css
33
+ *= require kommandant
34
+ ```
35
+
36
+ Pin kommandant in `config/importmap.rb` to get access to the Stimulus controllers. Kommandant currently only support importmap, but PRs are very welcome.
37
+
38
+ ```ruby
39
+ pin "kommandant", to: "kommandant.js"
40
+ ```
41
+
42
+ Register the Stimulus controllers in `app/javascript/controllers/index.js`
43
+
44
+ ```js
45
+ import { CommandPalette, KeyboardNavigation } from "kommandant"
46
+ application.register("command-palette", CommandPalette)
47
+ application.register("keyboard-navigation", KeyboardNavigation)
48
+ ```
49
+
50
+ Mount the engine in your `config/routes.rb`
51
+
52
+ ```ruby
53
+ Rails.application.routes.draw do
54
+ [...]
55
+
56
+ mount Kommandant::Engine => "/kommandant"
57
+ end
17
58
  ```
18
59
 
19
- Or install it yourself as:
60
+ And finally run:
20
61
  ```bash
21
- $ gem install kommandant
62
+ bin/rails generate kommandant:install
63
+ ```
64
+
65
+ You might want to configure the gem. As Kommandant has been extracted from one of our apps, we have tailored the default configuration to this app. You might want to turn some stuff off.
66
+
67
+ ```ruby
68
+ # config/initializers/kommandant.rb
69
+ Kommandant.configure do |config|
70
+ # Commands are a central part of Kommandant. They are loaded from a json file at "config/kommandant/commands.json" by default. We will go into more detail about commands further down the page.
71
+ # config.commands_path = "your/custom/path"
72
+
73
+ # When meilisearch returns a result, it might include items, that the current user is not allowed to see. You can filter these results with a lamda. This setting has no default, but below is an example that works with cancancan.
74
+ # config.search_result_filter_lambda = ->(current_ability, resource) { current_ability.can?(:show, resource) }
75
+
76
+ # Another search result filter. We use this to allow admins, who are impersonating users, access to all their commands. This setting has no default.
77
+ # config.admin_only_filter_lambda = ->(current_user, current_admin) { current_user.admin? || current_admin }
78
+
79
+ # If you want Kommandant to use a different parent controller, this is the setting for you. Defaults to ApplicationController
80
+ # config :parent_controller = "YourVerySpecialController"
81
+
82
+ # We assume there is a logged in user and therefore a current_user method. The name of this method can be set here. It defaults to current_user.
83
+ # config.current_user_method = current_account
84
+
85
+ # Kommandant highlights search terms by default. Uncomment this to turn it off. This only turns highlighting off, when using the views provided by the gem. You can still highlight terms if providing your own partial.
86
+ # config.highligt_search_term = false
87
+
88
+ # If you use Kredis, Kommandant can display the current user's most recently used commands. It requires your user model to have kredis_unique_list called recent_commands. If you don't use Kredis or do not want this behavior, it can be disabled. It defaults to being enabled.
89
+ # class User < ApplicationRecord
90
+ # kredis_unique_list :recent_commands, limit: 5
91
+ # config.recent_commands.enabled = false
92
+
93
+ # When a search returns a lot of results, it can be useful to paginate them. We use Pagy by default to handle this. If you do not use Pagy, this functinality can be turned off or configured to suit your needs. It defaults to being enabled.
94
+ # config.pagination.enabled = false
95
+ # config.pagination.items_per_page = 10 # defaults to 10
96
+ # config.pagination.pagination_lambda = ->(results, items, controller) { controller.send(:pagy_array, results, items: items) }
97
+ # config.pagination.info_label_lambda = ->(pagination, controller) { controller.send(:pagy_info, pagination).html_safe }
98
+ # config.pagination.module = "Pagy::Frontend"
99
+ end
100
+ end
101
+ ```
102
+
103
+ #### Setting up the views
104
+ Kommandant supplies two Stimulus controllers, `command-palette` and `keyboard-navigation`, containing all the JavaScript necessary to show, hide and navigate the command palette. This requires a `data-controller` attribute that catches all the events nested under its element. We recommend setting this on the body tag of your layout, so that all events are caught.
105
+
106
+ Stimulus data-action attributes are used to trigger the command palette. There are no default keybindings for this. Instead we utilize the [keyboard event filters](https://stimulus.hotwired.dev/reference/actions#keyboardevent-filter) in Stimulus to trigger the toggle action. You can use this to set any keyboard shortcut you want - as well as trigger it by clicking a button or any other event, you want to display the palette.
107
+
108
+ ```
109
+ <body
110
+ data-controller="command-palette keyboard-navigation"
111
+ data-action="keydown.meta+k->command-palette#toggle keydown.ctrl+k->command-palette#toggle keydown.esc->command-palette#hide keydown.down->keyboard-navigation#down keydown.up->keyboard-navigation#up keydown.enter->keyboard-navigation#select">
112
+ ...
113
+ </body>
114
+ ```
115
+
116
+
117
+ ## Usage
118
+ ### Commands
119
+ ![An open command palette with a list of commands](docs/commands.png)
120
+ Commands are the building blocks of the command palette. Any action in the command palette is backed by a command, implemented in the `Kommandant::Command` class, which inherits from `ApplicationRecord`.
121
+
122
+ ![Screenshot of the web UI](docs/web_ui.png)
123
+
124
+ There is only the one model, but the commands act in two distinct manners referred to as the basic and the resource command. In the future these might be extended further and split into two (or more) different models.
125
+
126
+ #### The basic command
127
+ ![A log out command](docs/basic_command.png)
128
+
129
+ A basic command is meant to hit a controller without a specific resource. It will mostly be used for simple actions like logging out, toggling some setting or navigation. It requires all the fields below to work as expected.
130
+
131
+ `translations` use `I18n` under the hood. Kommandant uses `I18n.available_locales` to generate these form inputs. Only name needs to be filled out here.
132
+
133
+ `icon` is the name of an icon you want displayed with the command in the command palette. You must supply your own partial at `kommandant/shared/icons/#{name of your icon}.html.erb` to render the icon. This partial is given the icon name in a local variable called `icon`. See more details under [the Icons section](#icons)
134
+
135
+ `path` is the path, that the command palette goes to when selected. In the example above, selecting the command will fire a request to the app's logout route. The existing logic takes over and logs the user out in exactly the same way as clicking through the UI.
136
+
137
+ `http_method` should match http method expected of the controller action, you want to hit.
138
+
139
+
140
+ #### The resource command
141
+ ![A selected resource command](docs/resource_command.png)
142
+ ![A resource command search result](docs/resource_command_search_result.png)
143
+
144
+ Most of your commands will be resource commands. They operate on a database backed model that has been setup to be indexed by meilisearch.
145
+ ```ruby
146
+ class Customer < ApplicationRecord
147
+ include MeiliSearch::Rails
148
+
149
+ meilisearch do
150
+ # This makes the customer's id searchable
151
+ attribute :id
152
+ attribute :name do
153
+ # And this lets you find it by its legal_entity association's name
154
+ legal_entity.name
155
+ end
156
+ end
157
+
158
+ belongs_to :legal_entity
159
+ end
160
+ ```
161
+ *Basic example of indexing a model. For more details see the [meilisearch-rails docs](https://github.com/meilisearch/meilisearch-rails)*
162
+
163
+ Any time a new model gets a meilisearch configuration or an attribute is modified, meilisearch must be updated.
164
+ ```ruby
165
+ MeiliSearch::Rails::Utilities.reindex_all_models
166
+ ```
167
+
168
+ ![Resource command form](docs/resource_command_form.png)
169
+ Again all the fields below are required to make the command function as excpected. However this type of command has an extra step. Once the user has selected a resource command, they will be presented with a new search bar, that allows them to search all this resource.
170
+
171
+ `icon` and `http_method` work exactly as for the basic command.
172
+
173
+ `path` is inferred by Kommandant when left blank. This is the path the user is redirected to, when the command is selected. Only change this if you want to handle rendering the command palette's response yourself or you want to add another step to your command, that Kommandant does not support.
174
+
175
+ `resource_class` is the model whose attributes you want to search Meilisearch for. The select is populated with all descendants of ApplicationRecord.
176
+
177
+ `search_result_text_method` calls a method on an instance of the resource class (e.g. Customer#name) that returns the text you want displayed in a search result.
178
+
179
+ `translations` can have an extra key called placeholders. This does what it says on the tin. It is highly recommended to tell the users what they can search for here.
180
+
181
+ `redirect_path` is used when the path cannot be inferred from the model's class name. The path is inferred like this:
182
+ ```ruby
183
+ "/#{command.resource_class.downcase.pluralize}/#{resource.id}"
184
+ ```
185
+
186
+ `admin_only` is used to filter out commands that only the admin is allowed to use. The `admin_only_filter_lambda` in the configuration is used here.
187
+
188
+ #### Icons
189
+ Kommandant assumes the presence of a partial in `app/views/kommandant/shared/icons/_command.html.erb`. This partial has the responsibility for rendering the icons named in each command. An example implementation could be as follows.
190
+
191
+ ```erb
192
+ <% if lookup_context.exists?("kommandant/shared/icons/#{icon.underscore}", [], true) %>
193
+ <%= render "kommandant/shared/icons/#{icon.underscore}" %>
194
+ <% end %>
195
+ ```
196
+
197
+ This renders any partial named after the icon (e.g. the partial `kommandant/shared/icons/_user.html.erb` if the command has defined user as its icon).
198
+ You can also use this to use a ViewComponent for your icons:
199
+
200
+ ```erb
201
+ <%= render UI::IconComponent.new(name: icon, width: :fixed, size: :large) %>
202
+ ```
203
+
204
+ ## Tasks
205
+ The gem comes with several tasks for reindexing and clearing the search engine. You will probably use `rails kommandant:reindex` the most. We recommend you run this on every deploy to make sure the search engine contains the data you expect.
206
+
207
+ ```
208
+ rails kommandant:clear_command_index # Clears the Kommandant commands from the search engine
209
+ rails kommandant:clear_indexes # Clears the search engine of all commands and models
210
+ rails kommandant:clear_model_index # Clears the search engine of all models set up with Kommandant
211
+ rails kommandant:install:migrations # Copy migrations from kommandant to application
212
+ rails kommandant:reindex # Reindexes all commands and models
213
+ rails kommandant:reindex_commands # Reindexes all commmands defined in the commands json file (or w...
214
+ rails kommandant:reindex_models # Reindexes all models set up with Kommandant
215
+ ```
216
+
217
+ ## Future improvements
218
+ We will continue to improve this gem. Some of the things on the planning board are:
219
+ - Better support for navigation commands
220
+ - Build own macros for indexing models in meilisearch
221
+ - Generate views task
222
+
223
+ ## Things that (probably) won't happen
224
+ - Different search backends, like ElasticSearch. We don't need it ourselves, so we don't want the maintenance overhead. But we will welcome PRs that supply this as a plugin somehow!
225
+ - Optional translations. We use translations in all our projects. If you hate having to supply translations, PRs are welcome.
226
+
227
+
228
+ ## Development
229
+ You either need to install and run meilisearch and redis or use the included `docker-compose.yml`.
230
+
231
+ ```sh
232
+ docker compose up -d
233
+ ```
234
+ `-d` runs the container detached, so you can use your terminal for other things.
235
+
236
+ Kommandant uses TailwindCSS. When developing you can run the following command to watch views for new classes to be added:
237
+ ```sh
238
+ tailwindcss -i app/assets/stylesheets/kommandant/application.tailwind.css -o app/assets/builds/kommandant.css -w
22
239
  ```
240
+ Any class needs to be prefixed with `kommandant` (e.g. `kommandant-bg-black`) to prevent collisions with changes in the parent app's Tailwind configuration.
23
241
 
24
242
  ## Contributing
25
- Contribution directions go here.
243
+ Contributions are welcome. We expect you to treat others well.
26
244
 
27
245
  ## License
28
246
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).