GithubHelp home page GithubHelp logo

timber's Introduction

Timber helps you create flexible, unobtrusive activity logs using Rails notifications. Timber is best for making the type of logs you normally see on an administrative dashboard:

Sally drafted "A snowy night in the woods" (about 2 hours ago)
Jim edited "A snowy night in the woods" (about 6 hours ago)
Frank published "Autumnal Bliss" (2 days ago)
Andy... and so on

Installation

Include the gem to your Gemfile:

gem 'timber'

Install and run the necessary migrations:

rake timber_engine:install:migrations
rake db:migrate

Usage

Let’s say your application has authors, posts, and publishers:

class Author < ActiveRecord::Base
  belongs_to :publisher
  has_many :posts
end

class Post < ActiveRecord::Base
  belongs_to :publisher
  belongs_to :author
end

class Publisher < ActiveRecord::Base
  has_many :posts
  has_many :authors
end

And you want to create a log item whenever a post is created or updated.

First, you need to set up an initializer at config/initializers/timber.rb.

Timber.register do
  subscribe "posts#create" do |event|
    post = Post.find_by_title(event.params[:post][:title])
    event.log(
      trackable: post,
      owner: post.publisher,
      key: "timber.posts.create",
      parameters: { link_to_author: link_to(event.current_user.name, author_path(event.current_user)) }
    )
  end
end

Inside the Timber.registration block, you can subscribe to as many controller#action events as you like. Also, notice that you have access to your applications custom url helpers (e.g. author_path) as well as built-in helpers (e.g. link_to). You can also access the current user off the event object (i.e. event.current_user).

In order to create an activity record, each subscription should include a call to event.log:

event.log(
  # The object being acted on (i.e. created, updated, etc.). Polymorphic association.
  trackable: post,

  # The owner (i.e. often the person doing the work). Polymorphic association.
  owner: post.publisher,

  # The key string used to lookup the text template in +timber.en.yml+. If omitted, it will
  # default to `timber.#{event.controller}.#{event.action}`
  key: "timber.posts.create",

  # A serialized hash of arbitrary data. This is a good place to stuff miscellaneous
  # data in order to cut down on database queries when rendering the activity `text`.
  # But keep in mind that ActiveRecord doesn't make it easy to query serialized data
  # (see: http://stackoverflow.com/questions/9814622).
  parameters: { link_to_author: link_to(event.current_user.name, author_path(event.current_user)) }
)

Next, create a yaml file to specify your templates in config/locales/timber.en.yml. Within this file, you have access to the activity instance, allowing your messages to mix static and dynamic content:

timber:
  posts:
    create: "<%= parameters[:link_to_author] %> created a post titled <%= trackable.title %>"
    # You can use `p` as a shorthand for `parameters`
    update: "<%= p[:link_to_author] %> edited <%= trackable.title %>"

Then you’ll need to somehow fetch a collection of activities. Timber deliberately adds no magical methods to you models: you’ll need to write them yourself. An activities association, for example, might look like:

has_many :activities, foreign_key: :owner_id, class_name: 'Timber::Activity'

Finally, you’ll want to display the activities in your view:

<ul>
  <% @publisher.activities.each do |activity| %>
    <li>
      <%= activity.text %>
      (<%= time_ago_in_words(activity.created_at) %> ago)
    </li>
  <% end %>
</ul>

Best Practices

Minimize database queries when rendering activity messages

The parameters hash should be used to store whatever data is required to render your activity message. Doing so will prevent slow page loads (especially if you’re preforming complicated joins, etc). For example, let’s say you’re logging the creation of a post:

event.log(
  trackable: post,
  owner: event.current_user,
  parameters: {
    link_to_user_profile: link_to(event.current_user.name, event.current_user.id),
    link_to_post: link_to(post.title, post.id)
  }
)

Then your timber.yml should look like:

<%= p[:link_to_user_profile] %> drafted a post titled: <%= p[:link_to_post] %>

We’re pulling everything we need directly off the activity.parameters. No additional lookups, joins, etc.

Keep view rendering logic out of your yaml template

If you need links and whatnot, you should construct them in your initializer (i.e. timber.rb) and definitely not in your template (i.e. timber.yml).

# Good
<%= p[:link_to_user_profile] %> drafted a post titled: <%= p[:link_to_post] %>

# Awful
<a href=<%= "/users/#{owner.id}"%>><%= owner.name %></a> drafted a post titled: <a href=<%= "/posts/#{trackable.id}"%>><%= trackable.title %></a>

Use numerical ids rather than pretty urls when adding links to your activity messages

This doesn’t apply unless you’re using something like friendly_id to generate pretty urls. Generally, however, you should use ordinary numerical ids to reference objects within your activity messages. This will help protect your links from breaking due to data changes over time.

Creating an activity outside of the timber initializer

In the timber initializer, you can easily create activities whenever an action is completed. Sometimes, however, you need to create activities that are not tied to controller logic (e.g. in an after_save).

It’s easy enough to create a timber activity directly:

Timber::Activity.create(
  trackable: @lumberjack,
  owner: @lumberjack.saw_mill,
  key: "timber.path.to.template",
  parameters: {
    user_name: 'Paul Bunyan',
    user_friends: ['Blue Ox']
  }
)

Testing your activities

Testing activities is sorta tricky since it crosses the entire stack. Integration tests, however, are not a good option since a large application may subscribe to scores of activities. In most cases then, writing an integration test for each activity is just not feasible.

Instead, I suggest you devise a way to unit test your subscriptions. To make things a bit easier, you can include Timber::NotificationHelpers in your RSpec.configure block:

# Include +publish_notification+ method for unit testing Timber activities
config.include Timber::NotificationHelpers

This module provides a publish_notification method which allows you to publish a notification – specifying a controller, action, current_user and request params. You can find example usage in the test suite.

Contributing

Fork the project, make your changes, and submit a pull request. Please ensure the tests pass:

rspec .

This gem is a based on public_activity. Thanks to @pokonski for his good ideas, especially the use of i18n for generating the activity text.

TODO

  • Currently, timber expects your current user method to be current_user. This should be configurable.

  • When editing an object, it would be really nice if the event contained information about what changed, allowing you to do things like: Jim updated the post title from “Snowy Night” to “Snowing Evening”.

  • Support for HAML style interpolation within timber.en.yml in addition to ERB.

License

Timber is an open source project built by Scholastica under the MIT-LICENSE.

timber's People

Contributors

coryschires avatar nickl avatar

Watchers

 avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.