{"id":25625296,"url":"https://github.com/felipeelias/resubject","last_synced_at":"2025-07-22T15:07:04.658Z","repository":{"id":6015989,"uuid":"7239446","full_name":"felipeelias/resubject","owner":"felipeelias","description":"Resubject makes easy to decorate your objects","archived":false,"fork":false,"pushed_at":"2021-04-29T19:32:19.000Z","size":70,"stargazers_count":94,"open_issues_count":3,"forks_count":2,"subscribers_count":9,"default_branch":"master","last_synced_at":"2025-06-17T10:07:20.404Z","etag":null,"topics":["decorator","rails","ruby","simpledelegator"],"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/felipeelias.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","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":"2012-12-19T11:17:13.000Z","updated_at":"2024-12-06T21:14:10.000Z","dependencies_parsed_at":"2022-08-21T00:20:45.589Z","dependency_job_id":null,"html_url":"https://github.com/felipeelias/resubject","commit_stats":null,"previous_names":[],"tags_count":10,"template":false,"template_full_name":null,"purl":"pkg:github/felipeelias/resubject","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/felipeelias%2Fresubject","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/felipeelias%2Fresubject/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/felipeelias%2Fresubject/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/felipeelias%2Fresubject/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/felipeelias","download_url":"https://codeload.github.com/felipeelias/resubject/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/felipeelias%2Fresubject/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":265721824,"owners_count":23817493,"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":["decorator","rails","ruby","simpledelegator"],"created_at":"2025-02-22T14:56:54.480Z","updated_at":"2025-07-22T15:07:04.621Z","avatar_url":"https://github.com/felipeelias.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Resubject\n\n[![Build Status](https://travis-ci.org/felipeelias/resubject.svg?branch=master)](https://travis-ci.org/felipeelias/resubject)\n[![Gem Version](https://badge.fury.io/rb/resubject.svg)](https://badge.fury.io/rb/resubject)\n[![Code Climate](https://codeclimate.com/github/felipeelias/resubject/badges/gpa.svg)](https://codeclimate.com/github/felipeelias/resubject)\n\nUber simple presenters using Ruby's SimpleDelegator.\n\n## Installation\n\nAdd this line to your application's Gemfile:\n\n```ruby\ngem 'resubject', '~\u003e 0.3.0'\n```\n\nAnd then execute:\n\n    $ bundle\n\n## Ruby/Rails versions\n\nThis gem is compatible with Ruby 1.9 and 2.0 and Rails 3 and 4.\n\n## Documentation\n\nCheckout the documentation in [rdoc.info/resubject](http://rdoc.info/github/felipeelias/resubject/master/frames)\n\n## Introduction\n\nResubject uses Ruby's [SimpleDelegator](http://apidock.com/ruby/SimpleDelegator) class to create its presenters.\n\nSimpleDelegator is a concrete implementation of the Delegator class. Basically, it delegates any method calls to the object passed into the constructor:\n\n```ruby\nrequire 'delegate'\n\narray = SimpleDelegator.new([1, 2, 3])\n\narray.count # =\u003e 3\narray.map   # =\u003e #\u003cEnumerator: ...\u003e\n```\n\nIt doesn't override the class name, but you still can access the original object.\n\n```ruby\narray.class\n# =\u003e SimpleDelegator\narray.__getobj__.class\n# =\u003e Array\n```\n\nThis means you can create a class that inherits from SimpleDelegator and customize its behaviour:\n\n```ruby\nclass ForeverZeroArray \u003c SimpleDelegator\n  def omg!\n    \"OMG!\"\n  end\n\n  def count\n    0\n  end\nend\n```\n\nYou can define new methods or override existing ones:\n\n```ruby\nForeverZeroArray.new([1,2,3]).count\n# =\u003e 0\nForeverZeroArray.new([1,2,3]).omg!\n# =\u003e OMG!\n```\n\n## Usage\n\nUsing the `Resubject::Presenter` class, you can create Presenters from it. Example:\n\n```ruby\nclass Box \u003c Struct.new(:name, :items)\nend\n\nclass BoxPresenter \u003c Resubject::Presenter\n  def contents\n    items.join(', ')\n  end\nend\n```\n\nThen use the delegator:\n\n```ruby\nbox = Box.new('Awkward Package', ['platypus', 'sloth', 'anteater'])\n\npresentable = BoxPresenter.new(box)\npresentable.contents\n# =\u003e platypus, sloth, anteater\n```\n\nIf you have a collection of objects and want to add a presenter to each one, use `Resubject.all`\n\n```ruby\nboxes = [box1, box2, box3]\nBoxPresenter.all boxes\n# =\u003e [\u003cBoxPresenter\u003e, \u003cBoxPresenter\u003e, \u003cBoxPresenter\u003e]\n```\n\n## Rails\n\nIf you're using rails, Resubject automatically includes helpers in your controllers and views\n\n```ruby\nclass BoxesController \u003c ActionController::Base\n  def index\n    @boxes = present Boxes.all\n  end\nend\n```\n\nThe `#present` method will automatically identify the presenter class by the object's class name. If you want to customize the class:\n\n```ruby\ndef index\n  @boxes = present Boxes.all, SpecialBoxPresenter\nend\n```\n\nIt also accepts multiple presenters:\n\n```ruby\ndef index\n  @boxes = present Boxes.all, BoxPresenter, ExtendedBoxPresenter\nend\n```\n\nOr if you prefer, you can use the `#present` method directly into your views\n\n```ruby\n\u003c%= present(@box).contents %\u003e\n```\n\n## Helpers\n\nYou can define presentable attributes:\n\n```ruby\nclass PostPresenter \u003c Resubject::Presenter\n  presents :title\n  presents :comments # or =\u003e presents :comments, CommentPresenter\nend\n```\n\nThen the attributes will return an instance of those presenters:\n\n```ruby\npost.title\n# =\u003e \u003cTitlePresenter\u003e\n\npost.comments\n# =\u003e [\u003cCommentPresenter\u003e, \u003cCommentPresenter\u003e, \u003cCommentPresenter\u003e]\n```\n\nOr if you wish, you can use the `present` method inside your class\n\n```ruby\nclass PostPresenter \u003c Resubject::Presenter\n  def comments\n    present(to_model.comments, SomePresenter)\n  end\nend\n```\n\n### Helpers on Rails\n\n`Resubject` can generate some rails helpers for your attributes:\n\n```ruby\nclass ProductPresenter \u003c Resubject::Presenter\n  currency :price, precision: 2\nend\n```\n\nWill generate:\n\n```ruby\nproduct.price\n# =\u003e $10.00\n```\n\n#### Available helpers\n\n```text\nPresenter           Maps to                     Class/Module\n\ncurrency            number_to_currency          ActionView::Helpers::NumberHelper\npercentage          number_to_percentage        ActionView::Helpers::NumberHelper\ntime_ago            time_ago_in_words           ActionView::Helpers::DateHelper\ndate_format         to_s                        ActiveSupport::TimeWithZone\n```\n\nMore helpers will be added. Feel free to contribute with yours! Also, Check out [the extensions file](https://github.com/felipeelias/resubject/blob/master/lib/resubject/extensions/template_methods.rb).\n\n### Helpers without rails\n\nIf you'd like to use the generated helpers but you're not using Rails, you can either use `ActionView` or create your own template handler:\n\n```ruby\nrequire 'action_view'\n\npost = PostPresenter.new(post, ActionView::Base.new)\n# Or\npost = PostPresenter.new(post, MyTemplateHandler.new)\n```\n\nIf you want to use your own template handler and still use Resubject helpers, you may want to define the same `ActionView` helpers in your handler (or only the ones you will actually use).\n\n## Testing\n\nResubject introduces a new context for RSpec that helps testing your presenters:\n\n```ruby\n# spec/presenters/my_presenter_spec.rb\n# require 'action_view' # require this first if you want to test action view helpers\nrequire 'resubject/rspec'\nrequire 'presenters/my_presenter'\n\ndescribe UserPresenter do\n  let(:object) { double :user }\n\n  it 'has full name' do\n    object.stub(first: 'User', last: 'Name')\n    expect(subject.name).to eq 'User Name'\n  end\nend\n```\n\nBy placing the file into `spec/presenters`, Resubject automatically includes the `subject` and `template` variables into your spec, so you don't need to define them on every spec.\n\n**NOTE:** Please note that the presenter is tested on isolation. It's not required but very recommended.\n\n## Maintainers\n\n- Felipe Elias Philipp - [coderwall.com/felipeelias](http://coderwall.com/felipeelias)\n- Piotr Jakubowski - [coderwall.com/piotrj](http://coderwall.com/piotrj)\n\n## Contributing\n\n1. Fork it\n2. Create your feature branch (`git checkout -b my-new-feature`)\n3. Commit your changes (`git commit -am 'Add some feature'`)\n4. Push to the branch (`git push origin my-new-feature`)\n5. Create new Pull Request\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffelipeelias%2Fresubject","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffelipeelias%2Fresubject","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffelipeelias%2Fresubject/lists"}