Reputation: 550
I'm experimenting with a pattern I'd like feedback on:
module Concerns
def AuthenticatedS3Concern(options)
AuthenticatedS3ConcernHelper.go(options)
end
module_function :AuthenticatedS3Concern
module AuthenticatedS3ConcernHelper
def self.go(options = {:attribute => :photo})
@@auth_attr = options[:attribute] # the photo clip reference
@@auth_attr_url = "#{@@auth_attr}_authenticated_url" # set this to do a one time download
Module.new do
def self.included(base)
base.send :include, AuthenticatedS3ConcernHelper::InstanceMethods
end
class_eval %(
def #{@@auth_attr}_authenticated_url(time_limit = 7.days)
authenticated_url_for('#{@@auth_attr}', time_limit)
end
)
end
end
module InstanceMethods
def authenticated_url_for(attached_file, time_limit)
AWS::S3::S3Object.url_for(self.send(attached_file).path('original'), self.send(attached_file).bucket_name, :expires_in => time_limit)
end
end
end
end
Which can be used like so:
require 'concerns/authenticated_s3_concern'
require 'concerns/remote_file_concern'
class Attachment
include Concerns.AuthenticatedS3Concern(:attribute => :attachment)
end
I'm curious if this is a good approach or a bad approach or what. Is there a better way to accomplish this kind of variably defined module stuff?
Thanks
Upvotes: 1
Views: 1718
Reputation: 9605
Aside from making your maintenance developers brains hurt, I don't see any advantage to doing this.
From what I can understand, all this code does is create an instance method in the including class called attribute_name_authenticated_url
– which is simply a wrapper for authenticated_url_for.
You could have easily done the same thing using method_missing
or defining and calling a class method that creates your instance method. IMO, this approach is much simpler and readable:
module Concerns
module AuthenticatedS3
def authenticated_url_for(attached_file, time_limit = 7.days)
AWS::S3::S3Object.url_for(self.send(attached_file).path('original'), self.send(attached_file).bucket_name, :expires_in => time_limit)
end
end
end
class Attachment
include Concerns::AuthenticatedS3
end
@attachment = Attachment.new
@attachment.authenticated_url_for(:attribute_name)
Metaprogramming techniques are best when they don't get in the way of what you're trying to do.
Upvotes: 1
Reputation: 44110
Not sure why do you need modules at all.
If all you need to do is dynamically add a dynamically named method, you can start with:
def make_me_a_method meth
define_method(meth){|param=7|
puts param
}
end
class C
make_me_a_method :foo
end
C.new.foo(3)
#=> 3
Upvotes: 0