{"id":17558130,"url":"https://github.com/fractaledmind/actionset","last_synced_at":"2025-04-28T10:46:23.618Z","repository":{"id":25302530,"uuid":"103687312","full_name":"fractaledmind/actionset","owner":"fractaledmind","description":"Wire-up ActiveSet collections with a Rails controller","archived":false,"fork":false,"pushed_at":"2023-03-08T20:11:50.000Z","size":543,"stargazers_count":4,"open_issues_count":18,"forks_count":4,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-04-13T22:40:22.343Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/fractaledmind.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG","contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-09-15T18:09:37.000Z","updated_at":"2024-01-16T12:07:03.000Z","dependencies_parsed_at":"2023-02-14T05:15:48.408Z","dependency_job_id":null,"html_url":"https://github.com/fractaledmind/actionset","commit_stats":null,"previous_names":[],"tags_count":29,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fractaledmind%2Factionset","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fractaledmind%2Factionset/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fractaledmind%2Factionset/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fractaledmind%2Factionset/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fractaledmind","download_url":"https://codeload.github.com/fractaledmind/actionset/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":251297937,"owners_count":21566923,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-21T09:43:19.900Z","updated_at":"2025-04-28T10:46:23.597Z","avatar_url":"https://github.com/fractaledmind.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ActionSet\n\n[![Build Status](https://travis-ci.com/fractaledmind/actionset.svg?branch=master)](https://travis-ci.com/fractaledmind/actionset)\n[![codecov](https://codecov.io/gh/fractaledmind/actionset/branch/master/graph/badge.svg)](https://codecov.io/gh/fractaledmind/actionset)\n\n## Installation\n\nAdd this line to your application's Gemfile:\n\n```ruby\ngem 'actionset', require: 'action_set'\n```\n\nAnd then execute:\n\n    $ bundle\n\nOr install it yourself as:\n\n    $ gem install actionset\n\n## Usage\n\nIn order to make the **`ActionSet`** helper methods available to your application, you need to `include` the module into your `ApplicationController`.\n\n```ruby\nclass ApplicationController \u003c ActionController::Base\n    include ActionSet\nend\n```\n\nOr, if you only want or need **`ActionSet`** in certain controllers, you can `include` the module directly into those controllers.\n\nThe simplest setup is to use the `process_set` helper method in your `index` action. Typically, `index` actions look something like the following:\n\n```ruby\ndef index\n  @things = Thing.all\nend\n```\n\nIn order to wire up the filtering, sorting, and paginating behaviors, we simply need to update our `index` action to:\n\n```ruby\ndef index\n  @things = process_set(Thing.all)\nend\n```\n\nNow, `@things` will be properly filtered, sorted, and paginated according to the request parameters.\n\n\u003e **Note:** `process_set` applies pagination and will paginate your collection regardless of the request parameters. Unless there are request parameters overriding the defaults, your collection will be paginated to 25 items per page showing the first 25 items (page 1). If you want to only filter and sort _without_ paginating, simply use the `filter_set` and `sort_set` helper methods directly, e.g. `sort_set(filter_set(Thing.all))`\n\n\u003e **Note:** If you use some authorization library, like [`Pundit`](https://github.com/varvet/pundit), which applies authorization scoping to your `index` action, you can compose that behavior with **`ActionSet`** easily:\n\u003e ```ruby\n\u003e def index\n\u003e   @things = process_set(policy_scope(Thing.all))\n\u003e end\n\u003e ```\n\nIn addition to filtering, sorting, and paginating, **`ActionSet`** provides exporting functionality via the `export_set` helper method. One common use case is to have an `index` action that renders a filtered, sorted, and paginated collection, but allows for a CSV export as well. In such cases, you typically want the HTML collection to be paginated, but the CSV not to be. This behavior is also relatively simple to achieve:\n\n```ruby\ndef index\n  things = sort_set(filter_set(Thing.all))\n\n  respond_to do |f|\n    f.html  { @things = paginate_set(things) }\n    f.csv   { export_set(things) }\n  end\nend\n```\n\nWith our controller properly wired up, we now simply need to have our views submitting request parameters in the shape that **`ActionSet`** expects. **`ActionSet`** provides view helpers to simplify such work.\n\nSorting is perhaps the simplest to setup. To create an (ARIA accessible) anchor link to sort by some particular attribute, use the `sort_link_for` view helper. You pass the attribute name (or dot-separated path), and then can add the text for the link (defaults to title-casing your attribute) and/or any HTML attributes you'd like added to the anchor link. A notable feature of the `sort_link_for` helper is that it intelligently infers the sort direction from whatever the current request state is. That is, if no sorting has been applied for that attribute, the link will apply sorting to that attribute in the _ascending_ direction. If sorting is currently being applied for that attribute in the _ascending_ direction, the link will apply sorting to that attribute in the _descending_ direction, and vice versa.\n\nFiltering is somewhat more involved. **`ActionSet`** expects filters to be placed under the `filter` request parameter, aside from that one expectation, we leave all other view layer implementation details up to you. You can build your filtering interface however best fits your application. However, if you need a simple default, we suggest the following pattern: a simple form on your `index` action view that simply reloads that action with whatever filter params the user has submitted. In Rails, building such a form is relatively simple:\n\n```erb\n\u003c%= form_for(form_for_object_from_param(:filter),\n             method: :get,\n             url: things_path) do |form| %\u003e\n  \u003cdiv class=\"form-group\"\u003e\n    \u003c%= form.label(:attribute, class: 'control-label') %\u003e\n    \u003c%= form.text_field(:attribute, class: 'form-control') %\u003e\n  \u003c/div\u003e\n\n  \u003cdiv class=\"text-right\"\u003e\n    \u003c%= form.submit 'Save', class: 'btn btn-primary' %\u003e\n  \u003c/div\u003e\n\u003c% end %\u003e\n```\n\nWe tell the `form_for` helper to make a `GET` request back to our `index` action (`things_path` in this example). The only odd bit is what we pass as the object to `form_for`; you will note we pass `form_for_object_from_param(:filter)`. This `form_for_object_from_param` view helper is provided by **`ActionSet`** and does precisely what it says—it provides an object (an `OpenStruct` object to be precise) that encodes whatever request parameters are nested under the param name given, where that object will work properly with the `form_for` helper. This view helper allows us to build forms we the user's filter inputs will be retained across searches.\n\nFor pagination, like filtering, we don't enforce any view-layer specifics. You simply need to pass request parameters under the `paginate` param, specifically the `page` and `size` params. However, **`ActionSet`** does provide a simple default pagination UI component via the `pagination_links_for` view helper. You simply pass your processed set to this view helper, and it will render HTML in this structure:\n\n![alt text](https://raw.githubusercontent.com/fractaledmind/actionset/master/pagination.png)\n\n```html\n\u003cnav class=\"pagination\" aria-label=\"Page navigation\"\u003e\n    \u003ca class=\"page-link page-first\" href=\"/foos?paginate%5Bpage%5D=1\"\u003e« First\u003c/a\u003e\n    \u003ca rel=\"prev\" class=\"page-link page-prev\" href=\"/foos?paginate%5Bpage%5D=1\"\u003e‹ Prev\u003c/a\u003e\n    \u003cspan class=\"page-current\"\u003ePage\u0026nbsp;\u003cstrong\u003e2\u003c/strong\u003e\u0026nbsp;of\u0026nbsp;\u003cstrong\u003e3\u003c/strong\u003e\u003c/span\u003e\n    \u003ca rel=\"next\" class=\"page-link page-next\" href=\"/foos?paginate%5Bpage%5D=3\"\u003eNext ›\u003c/a\u003e\n    \u003ca class=\"page-link page-last\" href=\"/foos?paginate%5Bpage%5D=3\"\u003eLast »\u003c/a\u003e\n\u003c/nav\u003e\n```\n\n## Configuration\n\n### ActiveSet.configuration.on_asc_sort_nils_come\n\nExample usage in an initializer:\n\n```\n  ActiveSet.configure do |c|\n    c.on_asc_sort_nils_come = :first\n  end\n```\n\nWhen `ActiveSet.configuration.on_asc_sort_nils_come == :last` (this is the default), null values will be sorted as if larger than any non-null value.\n\n```\nASC =\u003e [-2, -1, 1, 2, nil]\nDESC =\u003e [nil, 2, 1, -1, -2]\n```\n\nOtherwise sort nulls as if smaller than any non-null value.\n\n```\nASC =\u003e [nil, -2, -1, 1, 2]\nDESC =\u003e [2, 1, -1, -2, nil]\n```\n\n## Development\n\nAfter checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.\n\nTo install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).\n\n## Contributing\n\nBug reports and pull requests are welcome on GitHub at https://github.com/fractaledmind/actionset.\n\n## License\n\nThe gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffractaledmind%2Factionset","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffractaledmind%2Factionset","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffractaledmind%2Factionset/lists"}