Created
September 26, 2011 16:28
-
-
Save nwjsmith/1242659 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module Formattable | |
def format(opts) | |
# ... | |
end | |
end | |
class Basketball::PlayerRecord | |
extend Formattable | |
format :fields =>[:rebounds_total, :minutes, :assists, :points], :with => '-' | |
end |
I think you can use Array#wrap
to tighten this up a bit.
Also, we might want to write up some tests to understand how the class_eval
in a module interacts with a class that is extended from it. I'm not sure what that behaviour is (will the class_eval be run in the context of the module or the class?)
module Formattable
def format(options)
options.assert_valid_keys(:fields, :field, :with)
fields = Array.wrap(options[:fields] || options[:field])
default_value = options[:with]
fields.each do |field|
class_eval <<-METHOD, __FILE__, __LINE__ + 1
def format_#{field}
value = read_attribute(:#{field})
value.present? ? value : '#{default_value}'
end
METHOD
end
end
end
class Basketball::PlayerRecord
extend Formattable
format :field => :rebounds_total, :with => "-"
end
I just ran a test (see: https://gist.github.com/1242954) , and the above technique should work
Avoid name clashes:
module Formattable
def format(options)
options.assert_valid_keys(:fields, :field, :with)
fields = Array.wrap(options[:fields] || options[:field])
default_value = options[:with]
fields.each do |field|
class_eval <<-METHOD, __FILE__, __LINE__ + 1
unless defined?(format_#{field})
def format_#{field}
value = read_attribute(:#{field})
value.present? ? value : '#{default_value}'
end
end
METHOD
end
end
end
class Basketball::PlayerRecord
extend Formattable
format :field => :rebounds_total, :with => "-"
end
Or raise an exception:
module Formattable
def format(options)
options.assert_valid_keys(:fields, :field, :with)
fields = Array.wrap(options[:fields] || options[:field])
default_value = options[:with]
fields.each do |field|
class_eval <<-METHOD, __FILE__, __LINE__ + 1
unless defined?(format_#{field})
def format_#{field}
value = read_attribute(:#{field})
value.present? ? value : '#{default_value}'
end
end
METHOD
end
end
end
class Basketball::PlayerRecord
extend Formattable
format :field => :rebounds_total, :with => "-"
end
How about this code?
module ActiverecordExtensions
module Formattable
def format(options)
options.assert_valid_keys(:fields, :field, :with)
fields = Array.wrap(options[:fields] || options[:field])
default_value = options[:with]
fields.each do |field|
class_eval <<-METHOD, __FILE__, __LINE__ + 1
unless defined?(format_#{field})
def format_#{field}
value = read_attribute(:#{field})
value.present? ? value : '#{default_value}'
end
end
METHOD
end
end
end
def self.included(base)
base.extend Formattable
end
end
# some initializer file?
ActiveRecord::Base.include(ActiverecordExtensions)
module SleazyFormatter
class FormatterProxy < ActiveSupport::BasicObject
def initialize(delegatee)
@delegatee = delegatee
end
def method_missing(method_sym, *args)
return super unless @delegatee.respond_to?(method_sym)
default_value = args.first || "-"
actual_value = @delegatee.send(method_sym)
(actual_value.blank?) ? default_value : actual_value
end
def respond_to?(method_sym)
@delegatee.respond_to?(method_sym)
end
end
def sleazy_formatter
@sleazy_formatter ||= SleazyFormatter::FormatterProxy.new(self)
end
end
class Person
include SleazyFormatter
attr_reader :first_name, :last_name
def initialize(first_name)
@first_name = first_name
end
end
p = Person.new("Thuva")
p.first_name # Thuva
p.last_name # nil
p.sleazy_formatter.first_name # Thuva
p.sleazy_formatter.last_name # -
p.sleazy_formatter.last_name("@") # @
It is a nice solution... I was also going to explore the method missing
approach today...
On 11-09-27 12:08 PM, "Thuva Tharma" ***@***.*** wrote:
``` ruby
module SleazyFormatter
class FormatterProxy < ActiveSupport::BasicObject
def initialize(delegatee)
@delegatee = delegatee
end
def method_missing(method_sym, *args)
return super unless @delegatee.respond_to?(method_sym)
default_value = args.first || "-"
actual_value = @delegatee.send(method_sym)
(actual_value.blank?) ? default_value : actual_value
end
def respond_to?(method_sym)
@delegatee.respond_to?(method_sym)
end
end
def sleazy_formatter
@sleazy_formatter ||= SleazyFormatter::FormatterProxy.new(self)
end
end
class Person
include SleazyFormatter
attr_reader :first_name, :last_name
def initialize(first_name)
@first_name = first_name
end
end
p = Person.new("Thuva")
p.first_name # Thuva
p.last_name # nil
p.sleazy_formatter.first_name # Thuva
p.sleazy_formatter.last_name # -
p.sleazy_formatter.last_name("@") # @
```
This email and any files transmitted with it are confidential and intended solely for the recipient(s). If you are not the named addressee you should not disseminate, distribute, copy or alter this email. Any views or opinions presented in this email are solely those of the author and might not represent those of Score Media Inc. or any of its affiliates. Warning: Although Score Media Inc. has taken reasonable precautions to ensure no viruses are present in this email, the company cannot accept responsibility for any loss or damage arising from the use of this email or attachments.
To illustrate my idea, here is some more code exploring the method_missing, following a method protocol.
Advantage:
- We don't have to call sleazy_formatter first, so you save some typing
Drawback:
- We will have to override the active record method missing
module Formattable
SUFFIX_PROTOCOL_RE = /_with$/
def method_missing(method_sym, *args)
original_method = extract_original_method(method_sym)
return super unless follow_protocol?(method_sym, original_method)
default_value = args.first
actual_value = send(original_method)
actual_value.nil? ? default_value : actual_value
end
def extract_original_method(method_sym)
method_sym.to_s.gsub(SUFFIX_PROTOCOL_RE, '')
end
def follow_protocol?(calling_method, original_method)
SUFFIX_PROTOCOL_RE.match(calling_method.to_s) && respond_to?(original_method)
end
end
class Person
include Formattable
attr_reader :first_name, :last_name
def initialize(first_name)
@first_name = first_name
end
end
p = Person.new("Thuva")
p.first_name # Thuva
p.last_name # nil
p.first_name # Thuva
p.last_name_with('-') # -
p.last_name_with("@")
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Okay, let's fill this in with some code so that we can refine it.