{"id":17962190,"url":"https://github.com/alexander-senko/magic-presenter","last_synced_at":"2026-05-06T00:02:12.843Z","repository":{"id":258521859,"uuid":"874787323","full_name":"Alexander-Senko/magic-presenter","owner":"Alexander-Senko","description":"Presentation layer for Rails models to replace Draper","archived":false,"fork":false,"pushed_at":"2025-08-12T05:09:56.000Z","size":111,"stargazers_count":3,"open_issues_count":1,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-10-20T05:31:05.363Z","etag":null,"topics":["decorators","presenters","rails","ruby"],"latest_commit_sha":null,"homepage":"","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/Alexander-Senko.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2024-10-18T13:11:10.000Z","updated_at":"2025-06-27T22:05:55.000Z","dependencies_parsed_at":"2024-11-28T03:26:15.414Z","dependency_job_id":"56a2ef5c-7a1b-4044-b172-43a491398956","html_url":"https://github.com/Alexander-Senko/magic-presenter","commit_stats":null,"previous_names":["alexander-senko/magic-presenter"],"tags_count":7,"template":false,"template_full_name":null,"purl":"pkg:github/Alexander-Senko/magic-presenter","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Alexander-Senko%2Fmagic-presenter","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Alexander-Senko%2Fmagic-presenter/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Alexander-Senko%2Fmagic-presenter/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Alexander-Senko%2Fmagic-presenter/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Alexander-Senko","download_url":"https://codeload.github.com/Alexander-Senko/magic-presenter/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Alexander-Senko%2Fmagic-presenter/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32672682,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-05T11:29:49.557Z","status":"ssl_error","status_checked_at":"2026-05-05T11:29:48.587Z","response_time":54,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["decorators","presenters","rails","ruby"],"created_at":"2024-10-29T11:14:35.163Z","updated_at":"2026-05-06T00:02:12.828Z","avatar_url":"https://github.com/Alexander-Senko.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"# 🧙 Magic Presenter\n\n![GitHub Actions Workflow Status](\n\thttps://img.shields.io/github/actions/workflow/status/Alexander-Senko/magic-presenter/ci.yml\n)\n[![Maintainability](\n\thttps://qlty.sh/gh/Alexander-Senko/projects/magic-presenter/maintainability.svg\n)](\n\thttps://qlty.sh/gh/Alexander-Senko/projects/magic-presenter\n)\n[![Code Coverage](\n\thttps://qlty.sh/gh/Alexander-Senko/projects/magic-presenter/coverage.svg\n)](\n\thttps://qlty.sh/gh/Alexander-Senko/projects/magic-presenter\n)\n\nA bit of history: this gem was inspired by digging deeper into [Draper](https://github.com/drapergem/draper) with an eye on a refactoring.\n\nBased on [Magic Decorator](\n\thttps://github.com/Alexander-Senko/magic-decorator\n), it implements a presenter logic.\n\n## Installation\n\nInstall the gem and add to the application's Gemfile by executing:\n\n\t$ bundle add magic-presenter\n\nIf bundler is not being used to manage dependencies, install the gem by executing:\n\n\t$ gem install magic-presenter\n\nAfter all the gems are `bundle`d run the installer in the project directory to generate the necessary files:\n\n\t$ bin/rails generate magic:presenter:install\n\n## Usage\n\n`Magic::Presenter::Base` is a basic presenter class to be inherited by any other presenter.\nIt further inherits from [`SimpleDelegator`](\n\thttps://docs.ruby-lang.org/en/master/SimpleDelegator.html\n).\n\n```ruby\nclass PersonPresenter \u003c Magic::Presenter::Base\n  def name = \"#{first_name} #{last_name}\"\nend\n\nclass Person\n  include ActiveModel::Model\n  attr_accessor :first_name, :last_name\nend\n\nperson = Person.new(first_name: 'John', last_name: 'Smith').decorate\nperson.name # =\u003e \"John Smith\"\n```\n\n### `Magic::Presentable`\n\nThis module includes `Magic::Decoratable` and allows a descendant to be decorated by presenters only.\nPresenter class is being inferred automatically.\nWhen no presenter is found,\n- `#decorate`  returns `nil`,\n- `#decorate!` raises `Magic::Lookup::Error`,\n- `#decorated` returns the original object.\n\n### Generators\n\nA generator can be used to generate a presenter:\n\n\t$ bin/rails generate presenter Person\n\nSee the help for more info:\n\n\t$ bin/rails generate presenter --help\n\n### View helpers\n\nA presenter can use any helpers via `#helpers` (aliased as `#h`) both in class and instance methods:\n\n```ruby\nclass PersonPresenter \u003c Magic::Presenter::Base\n  def self.links\n    [ h.link_to('All', model_class) ]\n  end\n  \n  def link(...) \n    helpers.link_to(name, self, ...)\n  end\nend\n```\n\nA view context must be set to enable helpers.\nIt’s done automagically [wherever possible](#view-context).\nHowever, one can set it explicitly anywhere:\n\n```ruby\nMagic::Presenter.with view_context: ApplicationController.new.view_context do\n  # put the code that uses helpers within presenters here\nend\n```\n\n\u003e [!NOTE]\n\u003e A valid `request` may be needed for URL helpers to get host info.\n\n## 🧙 Magic\n\n\u003e [!IMPORTANT]\n\u003e It’s based on [Magic Decorator](\n\u003e \thttps://github.com/Alexander-Senko/magic-decorator#magic\n\u003e ), so get familiar with that one as well.\n\n### Presentable scope\n\n`Magic::Presentable` is mixed into `ActiveModel::API` by default.\nIt means that any model, be it `ActiveRecord::Base`, `Mongoid::Document` or whatever else, is _magically presentable_.\n\n### Presenter class inference\n\nPresenters provide automatic class inference for any model based on its class name powered by [Magic Lookup](\n\thttps://github.com/Alexander-Senko/magic-lookup\n).\n\nFor example, `MyNamespace::MyModel.new.decorate` looks for `MyNamespace::MyPresenter` first.\nWhen missing, it further looks for presenters for its ancestor classes, up to `ObjectPresenter`.\n\n#### Mapping rules\n\n- `MyObject` → `MyObjectPresenter`\n- `MyModel`  → `MyPresenter`\n- `MyRecord` → `MyPresenter`\n\n\u003e [!TIP]\n\u003e That’s why `ApplicationPresenter` presents `ApplicationRecord`  alongside all its descendants automagically with no extra code.\n\nWhen in doubt, one can use `Magic::Presenter.name_for`:\n\n```ruby\nMagic::Presenter.name_for Person # =\u003e \"PersonPresenter\"\n```\n\n#### Preloading\n\n\u003e [!NOTE]\n\u003e Magic Lookup doesn’t try to autoload any classes, it searches among already loaded ones instead.\n\u003e Thus, presenters should be preloaded to be visible via [lookups](#presenter-class-inference).\n\nThis is done automatically in both _test_ and _production_ environments by Rails.\nAll the application’s presenters and models are eagerly loaded before normal and reverse lookups by Magic Presenter as well.\nSo, normally one shouldn’t worry about that.\n\n\u003e [!IMPORTANT]\n\u003e When developing a Rails engine that defines its own presenters, one should take care of the preloading themselves.\n\nThat could be done in an initializer with a helper method provided:\n\n```ruby\nRails.application.config.to_prepare do\n  Magic.eager_load :presenters, engine: MyLib::Engine\nend\n```\n\n### Class methods delegation\n\nMissing class methods of a presenter are delegated to a matching model class if the latter can be inferred unambiguously.\n`Magic::Lookup::Error` is raised otherwise.\n\n### In views\n\n\u003e [!NOTE]\n\u003e Every object passed to views is decorated automagically.\n\u003e This involves both implicit instance variables and `locals` passed explicitly.\n\n### Helpers\n\nOne can call helpers directly without explicit `helper` or `h`:\n\n```ruby\nclass PersonPresenter \u003c Magic::Presenter::Base\n  def self.links\n    [ link_to('All', model_class) ]\n  end\n\n  def link(...) = link_to(name, self, ...)\nend\n```\n\n#### View context\n\nView context is set automagically to enable helpers:\n- in views,\n- in controller actions,\n- in mailer actions.\n\n## Generators\n\n\u003e [!NOTE]\n\u003e The built-in `helper` generator is overridden with `presenter` one to generate presenters instead of helpers.\n\n## Testing presenters\n\nMagic Presenter supports RSpec and Test::Unit.\nThe appropriate tests are generated alongside a presenter.\n\nTesting presenters is much like [testing Rails helpers](\n\thttps://guides.rubyonrails.org/testing.html#testing-helpers\n).\nSince the test class inherits from `ActionView::TestCase`, Rails’ helper methods such as `link_to`, `localize` and many others are available in tests.\n\nAs any presenter is a decorator, see also [how to test decorators](\n\thttps://github.com/Alexander-Senko/magic-decorator#testing-decorators\n).\n\n### RSpec\n\nPresenter specs are expected to live in `spec/presenters`.\nIf a different path is used, `type: :presenter` metadata should be set explicitly.\n\n### Test::Unit\n\nTests related to the presenters are located under the `test/presenters` directory and inherit from `Magic::Presenter::TestCase`.\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`.\n\n## Contributing\n\nBug reports and pull requests are welcome on GitHub at https://github.com/Alexander-Senko/magic-presenter. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/Alexander-Senko/magic-presenter/blob/main/CODE_OF_CONDUCT.md).\n\n## License\n\nThe gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).\n\n## Code of Conduct\n\nEveryone interacting in the Magic Presenter project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/Alexander-Senko/magic-presenter/blob/main/CODE_OF_CONDUCT.md).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falexander-senko%2Fmagic-presenter","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Falexander-senko%2Fmagic-presenter","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falexander-senko%2Fmagic-presenter/lists"}