Well I have good news for you!
This file contains 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
# app/models/user.rb | |
Dryer.def_class(__FILE__) do | |
def hi | |
"hello world" | |
end | |
end | |
User.new.hi | |
=> "hello world" | |
# app/models/instruments/horn.rb | |
Dryer.def_class(__FILE__) do | |
def play | |
"toot!" | |
end | |
end | |
Instruments::Horn.new.play | |
=> "toot!" | |
# app/models/address.rb | |
Dryer.def_class(__FILE__, ActiveRecord::Base) do | |
end | |
Address.new.respond_to? :save | |
=> true |
Here is the code! This class allows you to condsolidate the declaration of class names into the file name. It also handles namespacing based on the file path if you are using Rails autoloading.
This works with the single exception of constant lookup via nesting, so you will need to replace constants with variables and methods.
This file contains 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
class Dryer = Struct.new(:file_path, :super_class, :blk) do | |
def self.def_class(file_path, super_class=Object, &blk) | |
new(file_path, super_class, blk).def_class | |
end | |
def def_class | |
constant_names.each.with_index.inject(Object) do |const, (next_const, index)| | |
if constant_names.length == index + 1 | |
# this is needed to set the class name so that activerecord | |
# works properly. | |
klass = const.const_set(next_const, Class.new(super_class)) | |
# here we eval the class block to define methods and everything | |
klass.class_eval &blk | |
else | |
const.const_get(next_const) | |
end | |
end | |
end | |
def constant_names | |
get_namespaces.split('/').map(&:camelcase) | |
end | |
def get_namespaces | |
# this assumes you are following ruby convention of paths corresponding to | |
# namespaces. If you do not, this can't work. | |
file_path.gsub(most_likely_relative_path, '').gsub(".rb", '') | |
end | |
def most_likely_relative_path | |
# sort by length selects the path closest to the original file path. | |
possible_relative_paths.sort_by(&:length).last + '/' | |
end | |
def possible_relative_paths | |
$LOAD_PATH.select{|p| file_path[/^#{p}/]} | |
end | |
end |