Mo.
Mo.

Reputation: 304

More ruby-like way of writing simple ActiveRecord code

Here is some fairly standard Ruby on Rails 4 ActiveRecord code:

  def hide(user)    
    self.hidden = true
    self.hidden_on = DateTime.now
    self.hidden_by = user.id
  end

  def unhide
    self.hidden = false
    self.hidden_on = nil
    self.hidden_by = nil
  end

  def lock(user)
    self.locked = true
    self.locked_on = DateTime.now
    self.locked_by = user.id
  end

  def unlock
    self.locked = false
    self.locked_on = nil
    self.locked_by = nil
  end

  # In effect this is a soft delete
  def take_offline(user)
    hide(user)
    lock(user)
  end

The code is easy to understand and doesn't try to be clever. However it feels verbose. What would be a more succinct or canonical way of specifying this code/behaviour?

Upvotes: 4

Views: 60

Answers (2)

Joshua
Joshua

Reputation: 2109

It's a bit extreme unless you have a lot of these or you want to encapsulate a bit more logic. But you can do something like the following using composed_of

class Model < ActiveRecord::Base
  composed_of :hidden, class_name: 'State', mapping: %w(hidden, hidden_on, hidden_by)
  composed_of :locked, class_name: 'State', mapping: %w(locked, locked_on, locked_by)

  def hide(user)
    hidden.on
  end

  def unhide
    hidden.off
  end

  def lock(user)
    locked.on
  end

  def unlock
    locked.off
  end
end


class State < Struct.new(:state, :on, :by)
  def on(user)
    set(true, user)
  end

  def off
    set(false, nil, nil)
  end

  def on?
    state
  end

  def off?
    !on
  end

  private

  def set(state, by, on = Time.current)
    self.state = state
    self.by = by
    self.on = on
  end
end

Upvotes: 1

stoodfarback
stoodfarback

Reputation: 1349

Well, it's a trade-off, but if you want to be more clever, you can do something like:

def self.def_toggle(type, field)
  define_method(type) do |user|
    send("#{field}=", true)
    send("#{field}_on=", DateTime.now)
    send("#{field}_by=", user.id)
  end

  define_method("un#{type}") do
    send("#{field}=", false)
    send("#{field}_on=", nil)
    send("#{field}_by=", nil)
  end
end

def_toggle(:hide, :hidden)
def_toggle(:lock, :locked)

Upvotes: 3

Related Questions