Benito Serna Tips and tools for Ruby on Rails developers

(Refactor) Dynamic assignment with coffeescript

November 9, 2012

Today I was working in Commentator, a gem that was extracted from Aventones, and I had a problem trying to assign a lot of configuration, variables in a class.

Before

This was the initial state:

I had one class that receives the params, and makes a kind of parse,

class window.CommentatorParams

  constructor: (@args) ->

  el: ->
    @args.el

  url: ->
    @args.url

  poster: ->
    @args.poster || new CommentatorPoster

  comments: ->
    @args.comments || @el.data "comments"

  reply_link_name: ->
    @args.reply_link_name || "Comment"

  display_form: ->
    if @args.display_form? then @args.display_form else true

  comment_template: ->
    @args.comment_template || CommentatorTemplates.comment

  reply_template: ->
    @args.reply_template   || CommentatorTemplates.reply

  comments_form_template: ->
    @args.comments_form_template || CommentatorTemplates.comments_form

  replies_form_template: ->
    @args.replies_form_template  || CommentatorTemplates.replies_form

  on_comment: ->
    @args.on_comment || @_no_op

  on_reply: ->
    @args.on_comment || @_no_op

  _no_op: ->

an then the class that needs that parsed data.

#= require "commentator_templates"
#= require "commentator_poster"
#= require "commentator_params"

class window.Commentator

  constructor: (args) ->
    params = new CommentatorParams(args)
    @el  = params.el()
    @url = params.url()
    @poster   = params.poster()
    @comments = params.comments()
    @reply_link_name  = params.reply_link_name()
    @display_form     = params.display_form()
    @comment_template = params.comment_template()
    @reply_template   = params.reply_template()
    @comments_form_template = params.comments_form_template()
    @replies_form_template  = params.replies_form_template()
    @on_comment = params.on_comment()
    @on_reply   = params.on_reply()

    @el.delegate ".comments_form", "submit", @add_comment

    @render_comments()
    @render_form()

  render_comments: ->
    @comments_view = new Commentator.CommentsView(this, @comments)
    @el.append @comments_view.render()

  render_form: ->
    if @display_form and @url?
      @form_view = new Commentator.CommentFormView(this)
      @el.append @form_view.render()

  add_comment: (e) =>
    e.preventDefault()
    if @form_view.is_comment_valid()
      @_save_comment()

  _save_comment: ()->
    data =
      message: @form_view.comment()

    @poster.post @url, data, (json) =>
      @comments.push json
      @comments_view.add_comment(json)
      @form_view.clean()

As you can see in the constructor of Commentator, I made a lot of assignments, from data that comes from the CommentatorParams object.

ugly =(

After

In order to refactor this I made a kind of dynamic assignment in the Commentator class, with the help of another method in the CommentatorParams object.

class window.CommentatorParams

  constructor: (@args) ->

  all_params: ["el", "url", "poster", "comments", "reply_link_name", "display_form",
    "comment_template", "reply_template", "comments_form_template",
    "replies_form_template", "on_comment_render", "on_reply_render"]

  el: ->
    @args.el

  url: ->
    @args.url

# ..... the same code ......
class window.Commentator

  constructor: (args) ->
    params = new CommentatorParams(args)

    #@el  = params.el()
    #@url = params.url()
    #@poster   = params.poster()
    #@comments = params.comments()
    #@reply_link_name  = params.reply_link_name()
    #@display_form     = params.display_form()
    #@comment_template = params.comment_template()
    #@reply_template   = params.reply_template()
    #@comments_form_template = params.comments_form_template()
    #@replies_form_template  = params.replies_form_template()
    #@on_comment_render = params.on_comment_render()
    #@on_reply_render   = params.on_reply_render()
    # All this lines are no more needed =)

    for param in params.all_params
      @[param] = params[param]()

    @el.delegate ".comments_form", "submit", @add_comment

    @render_comments()
    @render_form()

  render_comments: ->
    @comments_view = new Commentator.CommentsView(this, @comments)
    @el.append @comments_view.render()


# .... the same code ....

I think this is cool, because I am receiving just the params that I want, and I am defining the methods in one place =)

Related articles

Weekly tips and tools for Ruby on Rails developers

I send an email each week, trying to share knowledge and fixes to common problems and struggles for ruby on rails developers, like How to fetch the latest-N-of-each record or How to test that an specific mail was sent or a Capybara cheatsheet. You can see more examples on Most recent posts or All post by topic.