{"id":13482535,"url":"https://github.com/apotonick/apotomo","last_synced_at":"2025-05-15T13:07:59.481Z","repository":{"id":406571,"uuid":"25348","full_name":"apotonick/apotomo","owner":"apotonick","description":"MVC Components for Rails.","archived":false,"fork":false,"pushed_at":"2020-02-18T04:47:31.000Z","size":2126,"stargazers_count":652,"open_issues_count":31,"forks_count":63,"subscribers_count":24,"default_branch":"master","last_synced_at":"2025-04-15T05:32:06.297Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"http://apotomo.de","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/apotonick.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGES.textile","contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2008-06-14T20:40:59.000Z","updated_at":"2024-11-28T16:27:29.000Z","dependencies_parsed_at":"2022-07-04T15:05:04.925Z","dependency_job_id":null,"html_url":"https://github.com/apotonick/apotomo","commit_stats":null,"previous_names":[],"tags_count":27,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/apotonick%2Fapotomo","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/apotonick%2Fapotomo/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/apotonick%2Fapotomo/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/apotonick%2Fapotomo/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/apotonick","download_url":"https://codeload.github.com/apotonick/apotomo/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254346624,"owners_count":22055808,"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-07-31T17:01:03.005Z","updated_at":"2025-05-15T13:07:54.474Z","avatar_url":"https://github.com/apotonick.png","language":"Ruby","funding_links":[],"categories":["Abstraction","Ruby"],"sub_categories":[],"readme":"# Apotomo\n\n**Web Components for Rails.**\n\n\n## Overview\n\nDo you need an _interactive user interface_ for your Rails application? A cool Rich Client Application with dashboards, portlets and AJAX, Drag\u0026Drop and jQuery?\n\nIs your controller gettin' fat? And your partial-helper-AJAX pile is getting out of control?\n\nDo you want a framework to make the implementation easier? _You want Apotomo._\n\n**Apotomo** is based on [Cells](http://github.com/apotonick/cells), the popular View Components framework for Rails.\n\nIt gives you widgets and encapsulation, bubbling events, AJAX page updates, rock-solid testing and more. Check out \u003chttp://apotomo.de\u003e for more information.\n\n## Installation\n\nEasy as hell. You just need Ruby 1.9.3/2.0.0 and Rails 3/4.\n\nAdd Apotomo to your `Gemfile`:\n\n```ruby\ngem 'apotomo'\n```\n\n## Example\n\nA _shitty_ example is worse than a _shitty_ framework, so let's choose wisely...\n\nSay you had a blog application. The page showing the post should have a comments block, with a list of comments and a form to post a new comment. Submitting should validate and send back the updated comments list, via AJAX.\n\nLet's wrap that comments block in a widget.\n\n## Generate\n\nGo and generate a widget stub.\n\n```shell\nrails generate apotomo:widget Comments display write -e haml\n```\n\n```\ncreate  app/widgets/\ncreate  app/widgets/comments_widget.rb\ncreate  app/widgets/comments/\ncreate  app/widgets/comments/display.html.haml\ncreate  app/widgets/comments/write.html.haml\ncreate  test/widgets/\ncreate  test/widgets/comments_widget_test.rb\n```\n\nNothing special.\n\n## Plug it in\n\nYou now tell your controller about the new widget.\n\n```ruby\nclass PostsController \u003c ApplicationController\n\n  has_widgets do |root|\n    root \u003c\u003c widget(:comments, :post =\u003e @post)\n  end\n```\n\nThis creates a widget instance called `comments_widget` from the class CommentsWidget.  We pass the current post into the widget - the block is executed in controller instance context, that's were `@post` comes from. Handy, isn't it?\n\n## Render the widget\n\nRendering usually happens in your controller view, `app/views/posts/show.html.haml`, for instance.\n\n```haml\n%h1= @post.title\n%p\n  = @post.body\n%p\n  = render_widget :comments, post: @post\n```\n\n## Write the widget\n\nA widget is like a cell which is like a mini-controller.\n\n```ruby\nclass CommentsWidget \u003c Apotomo::Widget\n  responds_to_event :post\n\n  def display(args)\n    @comments = args[:post].comments # the parameter from outside.\n\n    render\n  end\n```\n\nHaving `display` as the default state when rendering, this method collects comments to show and renders its view.\n\nAnd look at line 2 - if encountering a `:post` event we invoke `#post`, which is simply another state. How cool is that?\n\n```ruby\n  def post(event)\n    @comment = Comment.new :post_id =\u003e event[:post_id]\n    @comment.update_attributes event[:comment] # a bit like params[].\n\n    update :state =\u003e :display\n  end\nend\n```\n\nThe event is processed with three steps in our widget:\n\n* create the new comment\n* re-render the `display` state\n* update itself on the page\n\nApotomo helps you focusing on your app and takes away the pain of _action dispatching_ and _page updating_.\n\n## Triggering events\n\nSo how and where is the `:post` event triggered?\n\nTake a look at the widget's view `display.html.haml`.\n\n```haml\n= widget_div do\n  %ul\n    - for comment in @comments\n      %li= comment.text\n\n  = form_for :comment, :url =\u003e url_for_event(:post), :remote =\u003e true do |f|\n    = f.text_field :text\n    = f.submit\n```\n\nThat's a lot of familiar view code, almost looks like a _partial_.\n\nAs soon as the form is submitted, the form gets serialized and sent using the standard Rails mechanisms. The interesting part here is the endpoint URL returned by #url_for_event as it will trigger an Apotomo event.\n\n## Event processing\n\nNow what happens when the event request is sent? Apotomo - again - does three things for you, it\n\n* _accepts the request_ on a special event route it adds to your app\n* _triggers the event_ in your ruby widget tree, which will invoke the `#post` state in our comment widget\n* _sends back_ the page updates your widgets rendered\n\n## JavaScript Agnosticism\n\nIn this example, we use jQuery for triggering. We could  also use Prototype, RightJS, YUI, or a self-baked framework, that's up to you.\n\nAlso, updating the page is in your hands. Where Apotomo provides handy helpers as `#replace`, you could also _emit your own JavaScript_.\n\nLook, `replace` basically generates\n\n```ruby\njQuery(\"comments\").replaceWith(\u003cthe rendered view\u003e);\n```\n\nIf that's not what you want, do\n\n```ruby\ndef post(event)\n  if event[:comment][:text].explicit?\n    render :text =\u003e 'alert(\"Hey, you wanted to submit a pervert comment!\");'\n  end\nend\n```\n\nApotomo doesn't depend on _any_ JS framework - you choose!\n\n## Testing\n\nApotomo comes with its own test case and assertions to _build rock-solid web components_.\n\n```ruby\nclass CommentsWidgetTest \u003c Apotomo::TestCase\n  has_widgets do |root|\n    root \u003c\u003c widget(:comments, :post =\u003e @pervert_post)\n  end\n\n  def test_render\n    render_widget :comments\n    assert_select \"li#me\"\n\n    trigger :post, :comment =\u003e {:text =\u003e \"Sex on the beach\"}\n    assert_response 'alert(\"Hey, you wanted to submit a pervert comment!\");'\n  end\nend\n```\n\nYou can render your widgets, spec the markup, trigger events and assert the event responses, so far. If you need more, let us know!\n\n**Using rspec?** please check out [rspec-apotomo].\n\n## Bubbling events\n\nNote: Let's write this paragraph!\n\n## Bugs, Community\n\nPlease visit \u003chttp://apotomo.de\u003e, the official project page with _lots_ of examples.\n\nIf you have questions, visit us in the IRC channel #cells at irc.freenode.org.\n\nIf you wanna be cool, subscribe to our [feed](http://feeds.feedburner.com/Apotomo)!\n\n## License\n\nCopyright (c) 2007-2013 Nick Sutterer \u003capotonick@gmail.com\u003e\n\nReleased under the MIT License.\n\n  [rspec-apotomo]: https://github.com/apotonick/rspec-apotomo \"apotonick/rspec-apotomo\"\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fapotonick%2Fapotomo","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fapotonick%2Fapotomo","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fapotonick%2Fapotomo/lists"}