{"id":27164822,"url":"https://github.com/neighborland/pres","last_synced_at":"2025-04-09T02:34:02.034Z","repository":{"id":26604031,"uuid":"30059040","full_name":"neighborland/pres","owner":"neighborland","description":"A Simple Rails Presenter","archived":false,"fork":false,"pushed_at":"2024-12-02T21:55:48.000Z","size":60,"stargazers_count":16,"open_issues_count":0,"forks_count":1,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-03-04T20:46:28.089Z","etag":null,"topics":["presenters","rails","ruby"],"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/neighborland.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":"2015-01-30T06:15:23.000Z","updated_at":"2024-12-02T21:55:47.000Z","dependencies_parsed_at":"2022-09-14T03:41:19.159Z","dependency_job_id":null,"html_url":"https://github.com/neighborland/pres","commit_stats":null,"previous_names":[],"tags_count":12,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/neighborland%2Fpres","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/neighborland%2Fpres/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/neighborland%2Fpres/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/neighborland%2Fpres/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/neighborland","download_url":"https://codeload.github.com/neighborland/pres/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247656386,"owners_count":20974331,"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":["presenters","rails","ruby"],"created_at":"2025-04-09T02:33:06.581Z","updated_at":"2025-04-09T02:34:02.026Z","avatar_url":"https://github.com/neighborland.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"# `pres`\n\n[![Gem Version](https://badge.fury.io/rb/pres.svg)](http://badge.fury.io/rb/pres)\n[![Build Status](https://github.com/neighborland/pres/actions/workflows/ruby.yml/badge.svg)](https://github.com/neighborland/pres/actions/workflows/ruby.yml)\n\n## What?\n\nA Presenter is a rendering class. The `pres` gem is a lightweight presenter\nsolution with no runtime gem dependencies.\n\n`Pres` provides the following:\n\n1. `Pres::Presenter` is a presenter base class.\n1. `present` is a convenience method to create presenters.\n1. `Pres::ViewDelegation` is a delegation module, included in the `Presenter` base class.\n\n## How and Why?\n\nPresenters are an alternative to an unorganized pile of helper methods in your rails application.\n\nRails' `ViewContext` contains convenience methods for views, such as `link_to`,\n`url_for`, `truncate`, `number_to_currency`, etc. It's the thing that makes\nRails views nice to work with.\n\nOther presenter libraries mix in all the methods from the Rails `ViewContext` to\nmake it easy to call those methods in the Presenter class. `pres` instead injects\nthe `ViewContext` as a dependency into the Presenter class, and uses `method_missing`\nto delegate to `ViewContext` methods. `pres` produces small classes that contain and\ndelegate to an existing object that handles server-side rendering.\n\n## Install\n\nAdd it to your Gemfile:\n\n```ruby\ngem \"pres\"\n```\n\n## Setup with Rails\n\nInclude the `Pres::Presents` module:\n\n```ruby\nclass ApplicationHelper\n  include Pres::Presents\nend\n```\n\nThis will make the `present` method available in your views.\n\n## Use\n\nThere are two main approaches:\n\n(1) Follow the traditional rails way with view templates, but move your helper methods into a presenter class.\nYou'll probably want to start here if you have an existing rails app.\n\n(2) Create self-contained rendering components (see \"Components\" below).\n\nYou can use both techniques.\n\n### (1) With View Templates\n\nThe quickest way to get started is to use the `Pres::Presenter` base class.\n\nCreate a presenter class in `app/presenters`:\n\n```ruby\nclass DogePresenter \u003c Pres::Presenter\n  # explicitly delegate methods to the model\n  delegate :name, to: :object\n\n  def know_your_meme_link\n    # Rails helpers are available via the view context\n    link_to \"Know your meme\", \"http://knowyourmeme.com/memes/doge\"\n  end\n\n  def name_header\n    # object is the Doge used to initialize the presenter\n    content_tag(:h1, object.name)\n  end\n\n  def signed_in_status\n    # controller methods are accessible via the view context\n    if signed_in?\n      \"Signed in\"\n    else\n      \"Signed out\"\n    end\n  end\nend\n```\n\nStandard rails controller method:\n\n```ruby\nclass DogesController\n  def show\n    @doge = Doge.find(params[:id])\n  end\nend\n```\n\nWrap your model object in a presenter in your view with `present`:\n\n`doges/show.haml.html`\n\n```haml\n- present(@doge) do |doge|\n  = doge.name_header\n  .status\n    You are #{doge.signed_in_status}\n  .links\n    .meme-link= doge.know_your_meme_link\n```\n\n#### Collections\n\nStandard rails controller method:\n\n```ruby\nclass DogesController\n  def index\n    @doges = present(Doge.all)\n  end\nend\n```\n\nBuild an array of presenters in your view with `present`:\n\n`doges/index.haml.html`\n\nThis renders \"doges/_doge.html.haml\" for each item, following rails' usual conventions:\n\n```haml\n= render present(@doges)\n```\n\nOr use each:\n\n```haml\n- present(@doges).each do |doge|\n  = doge.name_header\n```\n\n## (2) Components\n\nYou can use `pres` to build components that directly render HTML:\n\n_Note: `#sanitize` is a method on the `view_context`._\n\n```ruby\nclass NameHeader \u003c Pres::Presenter\n  def render\n    return unless object\u0026.name\n    \u003c\u003c~HTML.html_safe\n      \u003ch1\u003e#{sanitize(object.name.titleize)}\u003c/h1\u003e\n    HTML\n  end\nend\n\nuser = User.new(name: \"joe cool \u003c\")\n\nNameHeader.new(user, view_context).render\n=\u003e \"\u003ch1\u003eJoe Cool \u0026lt;\u003c/h1\u003e\"\n\npresent(user, presenter: NameHeader).render\n=\u003e \"\u003ch1\u003eJoe Cool \u0026lt;\u003c/h1\u003e\"\n```\n\nYou may notice that you could do without `pres` altogether when you don't need\nthe `view_context` helper methods:\n\n```ruby\nclass PlusTwo\n  def initialize(object)\n    @object = object\n  end\n\n  def render\n    return unless @object\n    \u003c\u003c~HTML.html_safe\n      \u003cp\u003e#{@object + 2}\u003c/p\u003e\n    HTML\n  end\nend\n\nPlusTwo.new(2).render\n=\u003e \"\u003cp\u003e4\u003c/p\u003e\"\n\npresent(2, presenter: PlusTwo).render\n=\u003e \"\u003cp\u003e4\u003c/p\u003e\"\n```\n\nIf `render` is confusing, name that method `#to_html` or something else.\n\n## Options\n\n#### Present with options\n\nPass additional options to a Presenter as a hash. The presenter class exposes the\n`options` hash as a method:\n\n```ruby\nuser = User.new\n\n# These two lines are the same:\n# 1. explicit\npresenter = UserPresenter.new(user, view_context, something: 123)\n\n# 2. using #present\npresenter = present(user, something: 123)\n=\u003e #\u003cUserPresenter object: #\u003cUser\u003e ...\u003e\n\npresenter.options[:something]\n=\u003e 123\n```\n\n#### Use a custom presenter class\n\nBy default, a presenter class corresponding to the model class name is\nconstructed in `present`. For example, if you present a `User`, a `UserPresenter`\nclass is constructed. An error is raised if the presenter class does not exist.\nTo specify a different class, use the `presenter:` key.\n\n```ruby\nuser = User.new\npresent(user, presenter: UserEditPresenter, cool: true)\n=\u003e #\u003cUserEditPresenter object: #\u003cUser\u003e ...\u003e\n```\n\nYou may also define a custom presenter class on any class you want to present:\n\n```ruby\nclass User\n  def presenter_class\n    MyPresenter\n  end\nend\n\npresent(User.new)\n# =\u003e #\u003cMyPresenter object: #\u003cUser\u003e ...\u003e\n```\n\n#### Presenters can create other presenters\n\nPresenters can wrap child objects in presenters of their own.\n\n```ruby\nclass DogePresenter \u003c Pres::Presenter\n  def cats\n    present(object.cats)\n  end\nend\n```\n\n```haml\n= render doge.cats\n```\n\n### Using mixins instead of inheritance\n\nIf you don't want to inherit from `Pres::Presenter`, you can include\n`Pres::ViewDelegation` and implement your own initializer (so the `present` helper\nwill work).\n\nThis technique is useful if you would like to delegate all methods in a model\nby default, instead of whitelisting methods on the wrapped model explicitly.\nDelegating everything to the model by default is how the `draper` gem works, for example.\n\n```ruby\nclass DogePresenter \u003c DelegateClass(Doge)\n  include Pres::ViewDelegation\n\n  def initialize(object, view_context, options = {})\n    super(object)\n    @view_context = view_context\n  end\n```\n\n```haml\n= doge.name\n```\n\nsee [DelegateClass](https://ruby-doc.org/stdlib-2.4.0/libdoc/delegate/rdoc/Object.html)\n\n## Updating to version 1.0\n\nModules and classes have been moved into the `Pres` namespace with version 1.0.\nChange your code references to `Pres::Presents` and `Pres::Presenter`.\n\n## References\n\n* http://nithinbekal.com/posts/rails-presenters/\n* https://github.com/drapergem/draper\n* http://thepugautomatic.com/2014/03/draper/\n* https://robots.thoughtbot.com/sandi-metz-rules-for-developers\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fneighborland%2Fpres","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fneighborland%2Fpres","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fneighborland%2Fpres/lists"}