Just example, real code is written in C. def attr_accessor(name) define_method(name) do instance_variable_get("@#{name}") end define_method("#{name}=") do |val| instance_variable_set("@#{name}", val) end end
Define methods dynamically. define_method(:title) do instance_variable_get("@title") end define_method("title=") do |val| instance_variable_set("@title", val) end
Manipulate @ivar based on its name define_method(:title) do instance_variable_get("@title") end define_method("title=") do |val| instance_variable_set("@title", val) end
“ the ability to examine and modify the structure and behavior of the program at runtime Extracted from http://en.wikipedia.org/wiki/Reflection_(computer_programming) Reflection APIs
DON'T READ: Overuse of metaprogramming Book = Class.new do define_method(:initialize) do |attrs| attrs.each do |key, value| if respond_to?("#{key}=") send("#{key}=", value) else instance_variable_set("@#{key}", value) end end end define_method(:price=) do |price_str| instance_variable_set( '@price', price_str.delete(',').to_i ) end end
Standard Ruby code class Book def initialize(attrs) @author = attrs[:author] self.price = attrs[:price] end def price=(price_str) @price = Integer(price_str.delete(',')) end end Book.new(author: 'moro', price: '1,980') #=> #@price=1980>
def has_many(name, opts = {}) class_eval <<-RUBY def #{name} klass = #{name.to_s.classify} klass.where(#{fk}: id) end def #{name}=(value) values.each do |value| #{name.to_s.classify}.create!(...) end end RUBY en Seems difficult internal (not actual code)
= Active Record Associations This is the root class of all associations ('+ Foo' signifies an included module Foo): Association SingularAssociation HasOneAssociation HasOneThroughAssociation + ThroughAssociation BelongsToAssociation BelongsToPolymorphicAssociation CollectionAssociation HasAndBelongsToManyAssociation HasManyAssociation HasManyThroughAssociation + ThroughAssociation
class Comment < AR::Base ... def submit_to_censoring_service content = body ... end end class Photo < AR::Base ... def submit_to_censoring_service content = "http://img.example.com/#{id}" ... end end
class Post < AR::Base after_save :submit_to_censoring_service private def submit_to_censoring_service CensorRequest.new( id: to_global_id, text: [title, body].join("\n\n") ).submit end end
class CensorRequest @@url = URI(Rails.config.censoring_endpoint) def initialize(content) @content = content end def submit req = Net::HTTP::Post.new(@@url.path) req.set_form_data(content) Net::HTTP.start(@@url.hostname, @@url.port) do http.request(req) end end end
class Post < AR::Base after_save :submit_to_censoring_service private def submit_to_censoring_service CensorRequest.new( id: to_global_id, text: [title, body].join("\n\n") ).submit end end
class ContentCensor def initialize(type, &builder) @type = type @builder = builder end def submit(record) CensorRequest.new( id: record.global_id, @type => @builder.call(record) ).submit end end # ---------------- censor = ContentCensor.new(:text) do |post| [post.title, post.body].join("\n\n") end censor.submit(post)
class Post < AR::Base @@censor = ContentCensor.new(:text) do |post| [post.title, post.body].join("\n\n") end after_save :submit_to_censoring_service private def submit_to_censoring_service @@censor.submit(self) end end
class Comment < AR::Base @@censor = ContentCensor.new(:text) do |comment| comment.body end after_save :submit_to_censoring_service private def submit_to_censoring_service @@censor.submit(self) end end
class Photo < AR::Base @@censor = ContentCensor.new(:image) do |photo| "http://img.example.com/#{photo.id}" end after_save :submit_to_censoring_service private def submit_to_censoring_service @@censor.submit(self) end end