{"id":17925010,"url":"https://github.com/mwpastore/sinja-sequel","last_synced_at":"2025-07-05T18:13:04.491Z","repository":{"id":59155877,"uuid":"76577273","full_name":"mwpastore/sinja-sequel","owner":"mwpastore","description":"Sequel-specific helpers and DSL for Sinja","archived":false,"fork":false,"pushed_at":"2019-02-11T22:54:12.000Z","size":32,"stargazers_count":3,"open_issues_count":0,"forks_count":2,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-06-07T03:37:19.413Z","etag":null,"topics":["ember-data","json-api","ruby-framework","ruby-gem","sequel","sinatra"],"latest_commit_sha":null,"homepage":"http://sinja-rb.org","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/mwpastore.png","metadata":{"files":{"readme":"README.md","changelog":null,"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":"2016-12-15T16:37:19.000Z","updated_at":"2018-01-17T16:55:00.000Z","dependencies_parsed_at":"2022-09-13T20:03:48.869Z","dependency_job_id":null,"html_url":"https://github.com/mwpastore/sinja-sequel","commit_stats":null,"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"purl":"pkg:github/mwpastore/sinja-sequel","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mwpastore%2Fsinja-sequel","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mwpastore%2Fsinja-sequel/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mwpastore%2Fsinja-sequel/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mwpastore%2Fsinja-sequel/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mwpastore","download_url":"https://codeload.github.com/mwpastore/sinja-sequel/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mwpastore%2Fsinja-sequel/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":263783011,"owners_count":23510738,"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":["ember-data","json-api","ruby-framework","ruby-gem","sequel","sinatra"],"created_at":"2024-10-28T20:51:58.002Z","updated_at":"2025-07-05T18:13:04.305Z","avatar_url":"https://github.com/mwpastore.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Sinja::Sequel\n\n\u003c!--\n  Title: Sinja::Sequel\n  Description: Sequel-specific Helpers and DSL for Sinja\n  Author: Mike Pastore\n  Keywords: Ruby, Sinatra, Sinatra::JSONAPI, Sinja, Sequel\n  --\u003e\n\n[![Gem Version](https://badge.fury.io/rb/sinja-sequel.svg)](https://badge.fury.io/rb/sinja-sequel)\n[![Dependency Status](https://gemnasium.com/badges/github.com/mwpastore/sinja-sequel.svg)](https://gemnasium.com/github.com/mwpastore/sinja-sequel)\n\nSinja::Sequel configures your [Sinja][1] application to work with [Sequel][2]\nout of the box, and provides additional helpers to greatly simplify the process\nof writing the more complex action helpers (specifically `replace`, `merge`,\nand `subtract`). An optional extension enhances Sinja's DSL to generate basic\naction helpers that can be overridden, customized, or removed.\n\nThe core configuration and helpers are in pretty good shape (Sinja uses them in\nits [demo app][3] and test suite), but the extension could use some fleshing\nout. Testers and community contributions welcome!\n\n\u003c!-- START doctoc generated TOC please keep comment here to allow auto update --\u003e\n\u003c!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE --\u003e\n\n\n- [Installation](#installation)\n- [Best Practices](#best-practices)\n- [Usage](#usage)\n  - [Core](#core)\n  - [Helpers](#helpers)\n    - [`next_pk`](#next_pk)\n    - [`add_missing`](#add_missing)\n    - [`remove_present`](#remove_present)\n    - [`add_remove`](#add_remove)\n  - [Extension](#extension)\n  - [Pagination](#pagination)\n- [Development](#development)\n- [Contributing](#contributing)\n- [License](#license)\n\n\u003c!-- END doctoc generated TOC please keep comment here to allow auto update --\u003e\n\n## Installation\n\nAdd this line to your application's Gemfile:\n\n```ruby\ngem 'sinja-sequel'\n```\n\nAnd then execute:\n\n```sh\n$ bundle\n```\n\nOr install it yourself as:\n\n```sh\n$ gem install sinja-sequel\n```\n\n## Best Practices\n\nAlways return Sequel datasets (instead of arrays of objects) from your `index`\n(e.g. `Foo.dataset`) and `fetch` (e.g.  `resource.bars_dataset`) action\nhelpers. The `finalize` helper, described below, will ensure they are\n\"rasterized\" before being passed to [JSONAPI::Serializers][5].\n\nYou'll want to enable Sequel's `:tactical_eager_loading` plugin for the best\nperformance with JSONAPI::Serializers. I've [seen][6] it reduce complex\nserializations by a factor of 100 (i.e. quite literally one query instead of\n100).\n\nIf you want to use client-generated IDs, enable the `:update_primary_key`\nplugin on the model and call `unrestrict_primary_key` in the model definition\nto allow mass assignment (e.g. with `Sequel::Model#set_fields`).\n\nIf your model has foreign keys and you want to enforce a non-nullable\nconstraint at the application level, consider enabling the\n`:validation_helpers` plugin on the model and using `validates_not_null` in\nconjuction with the `validate!` helper described below:\n\n```ruby\nclass Bar \u003c Sequel::Model\n  plugin :validation_helpers\n\n  def validate\n    super\n    validates_not_null :foo\n  end\nend\n```\n\nSee \"Avoiding Null Foreign Keys\" in the [Sinja][1] documentation for more\ninformation.\n\nFinally, enable the `:pagination` extension on your connection (before\nprepending Core) to enable pagination!\n\n## Usage\n\nProgressively opt-in to Sinja::Sequel's features by enabling (1) Core, (2)\nHelpers and Core (the most common use-case), or (3) Extension, Helpers, and\nCore. (Pagination is automatically enabled with Core, but may need to be\nmanually enabled under certain circumstances, detailed below.)\n\n### Core\n\nPrepend [Sinja::Sequel::Core](/lib/sinja/sequel/core.rb) after registering\nSinja:\n\n```ruby\nrequire 'sinja'\nrequire 'sinja/sequel/core'\n\nclass MyApp \u003c Sinatra::Base\n  register Sinja\n\n  helpers do\n    prepend Sinja::Sequel::Core\n  end\n\n  # ..\n\n  freeze_jsonapi\nend\n```\n\nNote that you must use `prepend` (instead of including Sinja::Sequel::Core like\na normal module of Sinatra helpers) in order to ensure that the included\nmethods take precedence over Sinja's method stubs (e.g. `transaction`).\n[This][4] will hopefully be fixed in a future version of Sinatra.\n\nPrepending Core has the following effects on your application:\n\n* Configures `conflict_`, `not_found_`, and `validation_exceptions`, and\n  `validation_formatter`.\n* Defines a `database` helper that delegates to `Sequel::Model.db`.\n* Defines a `transaction` helper that delegates to `database.transaction`.\n* Defines a `validate!` helper that raises an error if `resource` is invalid\n  after a `create` or `update` action helper invocation.\n* Defines a simple equality-based `filter` helper that passes the filter params\n  to `Sequel::Dataset#where`.\n* Defines a `sort` helper that applies `Sequel.asc` and `Sequel.desc` to the\n  sort terms and passes them to `Sequel::Dataset#order`.\n* Defines a `finalize` helper that simply calls `Sequel::Dataset#all`.\n\nIf the `:pagination` Sequel extension is loaded, it also does the following:\n\n* Configures `page_using` for page number- and size-based pagination, with an\n  additional record count parameter to avoid repetitive `SELECT COUNT` queries\n  while paging.\n* Defines a `page` helper that calls `Sequel::Dataset#paginate` and computes a\n  hash of page params that Sinja will use to construct the root pagination\n  links and add to the root metadata of the response.\n\nYou may override any of the installed helpers by defining your own. Please see\nthe [Sinja][1] documentation for more information about Sinja hooks and\nconfigurables, and the [Sequel][2] documentation for more information about\nSequel plugins and features.\n\n### Helpers\n\nInclude\n[Sinja::Sequel::Helpers](/lib/sinja/sequel/helpers.rb) after\nregistering Sinja:\n\n```ruby\nrequire 'sinja'\nrequire 'sinja/sequel/helpers'\n\nclass MyApp \u003c Sinatra::Base\n  register Sinja\n\n  helpers Sinja::Sequel::Helpers\n\n  # ..\n\n  freeze_jsonapi\nend\n```\n\n**Note that including Helpers will automatically prepend Core!**\n\n#### `next_pk`\n\nA convenience method to always return the primary key of the resource and the\nresource from your `create` action helpers. Simply use it instead of `next`!\n\n```ruby\ncreate do |attr|\n  next_pk Foo.create(attr)\nend\n```\n\n#### `add_missing`\n\nTake the key of a Sequel \\*_to_many association and an array of resource\nidentifier objects and add the \"missing\" records to the collection. Makes\nwriting your `merge` action helpers a breeze!\n\n```ruby\nhas_many :bars do\n  merge do |rios|\n    add_missing(:bars, rios)\n  end\nend\n```\n\nIt will try to cast the ID of each resource identifier object by sending it the\n`:to_i` method; pass in a third argument to specify a different method (e.g. if\nthe primary key of the `bars` table is a `varchar`, pass in `:to_s` instead).\n\nThis helper also takes an optional block that can be used to filter\nsubresources during processing. Simply return a truthy or falsey value from the\nblock (or raise an error to abort the entire transaction):\n\n```ruby\nhas_many :bars do\n  merge do |rios|\n    add_missing(:bars, rios) do |bar|\n      role?(:admin) || bar.owner == resource.owner\n    end\n  end\nend\n```\n\n#### `remove_present`\n\nLike `add_missing`, but removes the \"present\" records from the collection.\nMakes writing your `subtract` action helpers a breeze!\n\n#### `add_remove`\n\nLike `add_missing` and `remove_present`, but performs an efficient delta\noperation on the collection. Makes writing your `replace` action helpers a\nbreeze!\n\nAn optional block passed to this method will be used to filter both adds and\nremoves. To use different filters for the two operations, pass a hash of\ncallables (with keys `:add` and/or `:remove`).\n\n### Extension\n\nRegister [Sinja::Sequel](/lib/sinja/sequel.rb) after\nregistering Sinja:\n\n```ruby\nrequire 'sinja'\nrequire 'sinja/sequel'\n\nclass MyApp \u003c Sinatra::Base\n  register Sinja\n  register Sinja::Sequel\n\n  # ..\n\n  freeze_jsonapi\nend\n```\n\n**Note that registering the extension will automatically include Helpers and\nprepend Core!**\n\nAfter registering the extension, the `resource`, `has_many`, and `has_one` DSL\nkeywords will generate basic action helpers.\n\n* `resource` and `has_many`, and `has_one` take an optional second argument\n  that specifies the method to use to cast the ID of the resource or resource\n  identifier object(s) (`:to_i` by default).\n\n* The generated action helpers will be unrestricted by default.\n\n* The generated `create` action helper does not support client-generated IDs.\n\nThese action helpers can be subsequently overridden, customized by setting\naction helper options (i.e. `:roles`) and/or defining `before_\u003caction\u003e` hooks,\nor removed entirely with `remove_\u003caction\u003e`.\n\nGiven a database connection and Foo, Bar, and Qux models and serializers,\nhere's an example \"classic\"-style application using the extension:\n\n```ruby\nrequire 'sinatra/jsonapi/sequel'\n\nresource :foos do\n  has_many :bars\n  has_one :qux\nend\n\nresource :bars do\n  has_one :foo\nend\n\nresource :quxes do\n  has_many :foos\nend\n\nfreeze_jsonapi\n```\n\nPretty neat, huh?\n\n### Pagination\n\nSinja::Sequel inspects the first Sequel database to determine whether or not to\nenable pagination. If (and only if!) you have _multiple_ databases in your\napplication and only _some_ support pagination, you _may_ need to prepend\n[Sinja::Sequel::Pagination](/lib/sinja/sequel/pagination.rb) after prepending\nCore, including Helpers, or registering Sinja:\n\n```ruby\nrequire 'sinja'\nrequire 'sinja/sequel'\nrequire 'sinja/sequel/pagination'\n\nDB = Sequel.connect ENV['DB_URL']\n\nOTHER_DB = Sequel.connect ENV['OTHER_DB_URL']\nOTHER_DB.extension :pagination\n\n# Sequel::Model.db now points to DB, which does not support pagination, so\n# pagination will not be automatically enabled. We'll point Sinja::Sequel at\n# OTHER_DB instead, and manually enable pagination...\n\nclass MyApp \u003c Sinatra::Base\n  register Sinja\n  register Sinja::Sequel\n\n  helpers do\n    prepend Sinja::Sequel::Pagination\n\n    def database\n      OTHER_DB\n    end\n  end\n\n  # ..\n\n  freeze_jsonapi\nend\n```\n\n## Development\n\nAfter checking out the repo, run `bin/setup` to install dependencies. Then, run\n`rake spec` to run the tests. You can also run `bin/console` for an interactive\nprompt that will allow you to experiment.\n\nTo install this gem onto your local machine, run `bundle exec rake install`. To\nrelease a new version, update the version number in `version.rb`, and then run\n`bundle exec rake release`, which will create a git tag for the version, push\ngit commits and tags, and push the `.gem` file to\n[rubygems.org](https://rubygems.org).\n\n## Contributing\n\nBug reports and pull requests are welcome on GitHub at\nhttps://github.com/mwpastore/sinja-sequel.\n\n## License\n\nThe gem is available as open source under the terms of the [MIT\nLicense](http://opensource.org/licenses/MIT).\n\n[1]: https://github.com/mwpastore/sinja\n[2]: http://sequel.jeremyevans.net\n[3]: https://github.com/mwpastore/sinja/tree/master/demo-app\n[4]: https://github.com/sinatra/sinatra/issues/1213\n[5]: https://github.com/fotinakis/jsonapi-serializers\n[6]: https://github.com/fotinakis/jsonapi-serializers/pull/31#issuecomment-148193366\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmwpastore%2Fsinja-sequel","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmwpastore%2Fsinja-sequel","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmwpastore%2Fsinja-sequel/lists"}