Reputation: 304
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
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
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