Permalink
Browse files

extended the metaprogramming section

  • Loading branch information...
1 parent 9016f23 commit 95f7e7ed7d33de31444f09821b4caa8514fb6414 Bozhidar Batsov committed Mar 28, 2012
Showing with 58 additions and 1 deletion.
  1. +58 −1 README.md
View
@@ -1135,7 +1135,64 @@ syntax.
## Metaprogramming
* Do not mess around in core classes when writing libraries. (Do not monkey
- patch them.)
+patch them.)
+
+* The block form of `class_eval` is preferable to the string-interpolated form.
+ - when you use the string-interpolated form, always supply `__FILE__` and `__LINE__`, so that your backtraces make sense:
+
+ ```ruby
+ class_eval "def use_relative_model_naming?; true; end", __FILE__, __LINE__
+ ```
+
+ - `define_method` is preferable to `class_eval{ def ... }`
+
+* When using `class_eval` (or other `eval`) with string interpolation, add a comment block showing its appearance if interpolated (a practice I learned from the rails code):
+
+ ```ruby
+ # from activesupport/lib/active_support/core_ext/string/output_safety.rb
+ UNSAFE_STRING_METHODS.each do |unsafe_method|
+ if 'String'.respond_to?(unsafe_method)
+ class_eval <<-EOT, __FILE__, __LINE__ + 1
+ def #{unsafe_method}(*args, &block) # def capitalize(*args, &block)
+ to_str.#{unsafe_method}(*args, &block) # to_str.capitalize(*args, &block)
+ end # end
+
+ def #{unsafe_method}!(*args) # def capitalize!(*args)
+ @dirty = true # @dirty = true
+ super # super
+ end # end
+ EOT
+ end
+ end
+ ```
+
+* avoid using `method_missing` for metaprogramming. Backtraces become messy; the behavior is not listed in `#methods`; misspelled method calls might silently work (`nukes.luanch_state = false`). Consider using delegation, proxy, or `define_method` instead. If you must use `method_missing`,
+ - be sure to [also define `respond_to?`](http://devblog.avdi.org/2011/12/07/defining-method_missing-and-respond_to-at-the-same-time/)
+ - only catch methods with a well-defined prefix, such as `find_by_*` -- make your code as assertive as possible.
+ - call `super` at the end of your statement
+ - delegate to assertive, non-magical methods:
+
+ ```ruby
+ # bad
+ def method_missing?(meth, *args, &block)
+ if /^find_by_(?<prop>.*)/ =~ meth
+ # ... lots of code to do a find_by
+ else
+ super
+ end
+ end
+
+ # good
+ def method_missing?(meth, *args, &block)
+ if /^find_by_(?<prop>.*)/ =~ meth
+ find_by(prop, *args, &block)
+ else
+ super
+ end
+ end
+
+ # best of all, though, would to define_method as each findable attribute is declared
+ ```
## Misc

0 comments on commit 95f7e7e

Please sign in to comment.