For two years now I've been using my mass_assignment plugin to improve the security and readability of my Ruby on Rails code. Today I decided to get with the program and make it a gem. Since it's been stable and production-tested for a long time now I think it deserves the 1.0 version.
What It Is
A robust mass assignment method with a small and obvious syntax.
The Traditional Approach
The normal mass assignment protection comes from attr_protected and attr_accessible. There are a few problems with this approach:
- Often never implemented, leaving a wide-open system. Rails blogs are full of dire warnings about forgetting your attr_protected.
- Once implemented, easy to forget when adding new attributes, leading to bugs (in an attr_accessible system) or security holes (in an attr_protected system).
- Restricts coding syntax. You can’t easily use update_attributes() or attributes= because your whitelist/blacklist gets in your own way.
- Not contextual. The list of allowed attributes can’t change to accomodate different user permissions or situations.
The MassAssignment Approach
This plugin’s solution is to let you specify an obvious and explicit list of allowed attributes when you mass assign attributes.
- The list of allowed attributes is in your controller at calltime, so it’s easier to remember and update (it’s not a hidden, magical system).
- The list of allowed attributes is optional, so it doesn’t get in your way. You can use update_attributes() and attributes= for your own code again.
- Assignment permissions are enforced by the controller, where permissions belong. You can evaluate the current user or current situation and write the whitelist on the fly.
See the README on github for examples and more.
I’m needing to do more analysis of my production logs, and at every turn I see “requires Hodel 3000 Complaint blah blah”. But that’s not what I have — I have standard logs! And thanks to buffered logging, they are quite parseable as they are.
http://github.com/cainlevy/timber
This fresh plugin provides simple objects to parse a production log into requests and extract whatever data you need. By default it extracts controller, method, and request time. But if you have special data in your log files that you want to analyze, it’s quite easy to add.
For example, I want to use Oink to analyze which requests most frequently bloat my worker thread sizes. Here’s a snip that adds Oink parsing to the Request class:
module Timber
module Plugins
module Oink
# create simple :pid attribute
attr_reader :pid
# create memory attribute, but return as integer
def memory
@_memory ||= @memory ? @memory.to_i : nil
end
end
end
# add to Request
Request.class_eval {include Plugins::Oink}
# add the regexp => vars pattern for the Oink log format
Request::PATTERNS[/Memory usage: (\d+) \| PID: (\d+)/] = [:memory, :pid]
end
With that, I can rewrite the Oink log parser to offload the log parsing to Timber and simplify it down to its own reporting logic:
module Oink
class MemoryUsageReporter < Base
def print(output)
output.puts "---- MEMORY THRESHOLD ----"
output.puts "THRESHOLD: #{@threshold/1024} MB\n"
output.puts "\n-- REQUESTS --\n" if @format == :verbose
@inputs.each do |input|
logfile = Timber::Log.new(input)
while request = logfile.shift
if pid = @pids[request.pid] and request.memory
delta = request.memory - pid[:last_memory_reading]
if delta > @threshold
@bad_actions[request.action] ||= 0
@bad_actions[request.action] += 1
@bad_requests.push(OinkedMemoryRequest.new(request.action, request.time, request.lines, delta))
if @format == :verbose
request.lines.each { |b| output.puts b }
output.puts "---------------------------------------------------------------------"
end
end
else
@pids[request.pid] = {:last_memory_reading => request.memory}
end
end
end
print_summary(output)
end
end
end