Compare commits

..

3 Commits

Author SHA1 Message Date
Charlie Somerville
dbe129af02 compile helpers without trace instruction 2013-10-16 19:54:42 -04:00
Charlie Somerville
bf71aa30d2 fast 2013-10-15 15:07:23 -04:00
Charlie Somerville
0928c2bf4f automatically infer file/line of module eval from caller 2013-10-15 15:05:32 -04:00
9 changed files with 279 additions and 9 deletions

View File

@@ -138,8 +138,13 @@ module ActionController
end
end
def named_helper_module_eval(code, *args)
@module.module_eval(code, *args)
def named_helper_module_eval(code)
file, line = caller.first.split(":")
compile_option = RubyVM::InstructionSequence.compile_option
RubyVM::InstructionSequence.compile_option = compile_option.merge(:trace_instruction => false)
@module.module_eval(code, file, line.to_i + 1)
ensure
RubyVM::InstructionSequence.compile_option = compile_option
end
def define_hash_access(route, name, kind, options)
@@ -196,6 +201,40 @@ module ActionController
protected :#{selector} # protected :users_url
end_eval
helpers << selector
if kind == :path
define_fast_url_helper(selector, route, name)
end
end
def define_fast_url_helper(selector, route, name)
dynamic_segments = route.segments.grep(DynamicSegment)
return if dynamic_segments.any?(&:optional?) # TODO - maybe exclude optional format
helper_arguments = dynamic_segments.map { |route_argument|
"arg_#{route_argument.key}"
}
route_segments = route.segments.dup
if route_segments.size > 1 && route_segments.last.is_a?(DividerSegment) && route_segments.last.optional?
route_segments.pop
end
string_segments = route_segments.map { |segment|
if segment.is_a?(StaticSegment)
segment.value.inspect
else
'"#{URI::DEFAULT_PARSER.escape(arg_%s.to_s, ActionController::Routing::Segment::UNSAFE_PCHAR)}"' % segment.key
end
}
named_helper_module_eval <<-"RUBY"
def fast_#{selector}(#{helper_arguments.join(", ")})
#{string_segments.join(" ")}
end
RUBY
end
end

View File

@@ -261,7 +261,7 @@ module ActiveRecord
key_records = key_records.inject({}) { |hsh, r| hsh.merge(r.id => r) }
end
calculated_data.inject({}) do |all, row|
calculated_data.inject(ActiveSupport::OrderedHash.new) do |all, row|
key = group_aliases.map{|group_alias| type_cast_calculated_value(row[group_alias], group_columns[group_alias])}
key = key.first if key.size == 1
key = key_records[key] if associated

View File

@@ -295,7 +295,7 @@ module ActiveRecord
# Removes all errors that have been added.
def clear
@errors = {}
@errors = ActiveSupport::OrderedHash.new
end
# Returns the total number of errors added. Two errors added to the same attribute will be counted as such.

View File

@@ -552,7 +552,7 @@ module NestedAttributesOnACollectionAssociationTests
end
def test_should_sort_the_hash_by_the_keys_before_building_new_associated_models
attributes = {}
attributes = ActiveSupport::OrderedHash.new
attributes['123726353'] = { :name => 'Grace OMalley' }
attributes['2'] = { :name => 'Privateers Greed' } # 2 is lower then 123726353
@pirate.send(association_setter, attributes)
@@ -562,6 +562,7 @@ module NestedAttributesOnACollectionAssociationTests
def test_should_raise_an_argument_error_if_something_else_than_a_hash_is_passed
assert_nothing_raised(ArgumentError) { @pirate.send(association_setter, {}) }
assert_nothing_raised(ArgumentError) { @pirate.send(association_setter, ActiveSupport::OrderedHash.new) }
assert_raise_with_message ArgumentError, 'Hash or Array expected, got String ("foo")' do
@pirate.send(association_setter, "foo")

View File

@@ -1,6 +1,40 @@
require 'active_support/ordered_hash'
module Enumerable
# Ruby 1.8.7 introduces group_by, but the result isn't ordered. Override it.
remove_method(:group_by) if [].respond_to?(:group_by) && RUBY_VERSION < '1.9'
# Collect an enumerable into sets, grouped by the result of a block. Useful,
# for example, for grouping records by date.
#
# Example:
#
# latest_transcripts.group_by(&:day).each do |day, transcripts|
# p "#{day} -> #{transcripts.map(&:class).join(', ')}"
# end
# "2006-03-01 -> Transcript"
# "2006-02-28 -> Transcript"
# "2006-02-27 -> Transcript, Transcript"
# "2006-02-26 -> Transcript, Transcript"
# "2006-02-25 -> Transcript"
# "2006-02-24 -> Transcript, Transcript"
# "2006-02-23 -> Transcript"
def group_by
assoc = ActiveSupport::OrderedHash.new
each do |element|
key = yield(element)
if assoc.has_key?(key)
assoc[key] << element
else
assoc[key] = [element]
end
end
assoc
end unless [].respond_to?(:group_by)
# Calculates a sum from the elements. Examples:
#
# payments.sum { |p| p.price * p.tax_rate }
@@ -28,6 +62,26 @@ module Enumerable
end
end
# Iterates over a collection, passing the current element *and* the
# +memo+ to the block. Handy for building up hashes or
# reducing collections down to one object. Examples:
#
# %w(foo bar).each_with_object({}) { |str, hsh| hsh[str] = str.upcase } #=> {'foo' => 'FOO', 'bar' => 'BAR'}
#
# *Note* that you can't use immutable objects like numbers, true or false as
# the memo. You would think the following returns 120, but since the memo is
# never changed, it does not.
#
# (1..5).each_with_object(1) { |value, memo| memo *= value } # => 1
#
def each_with_object(memo, &block)
memo.tap do |m|
each do |element|
block.call(element, m)
end
end
end unless [].respond_to?(:each_with_object)
# Convert an enumerable to a hash. Examples:
#
# people.index_by(&:login)
@@ -49,6 +103,15 @@ module Enumerable
size > 1
end
# Returns true if none of the elements match the given block.
#
# success = responses.none? {|r| r.status / 100 == 5 }
#
# This is a builtin method in Ruby 1.8.7 and later.
def none?(&block)
!any?(&block)
end unless [].respond_to?(:none?)
# The negative of the Enumerable#include?. Returns true if the collection does not include the object.
def exclude?(object)

View File

@@ -1 +1,168 @@
ActiveSupport::OrderedHash = Hash
require 'yaml'
YAML.add_builtin_type("omap") do |type, val|
ActiveSupport::OrderedHash[val.map(&:to_a).map(&:first)]
end
# OrderedHash is namespaced to prevent conflicts with other implementations
module ActiveSupport
class OrderedHash < ::Hash #:nodoc:
def to_yaml_type
"!tag:yaml.org,2002:omap"
end
def to_yaml(opts = {})
YAML.quick_emit(self, opts) do |out|
out.seq(taguri, to_yaml_style) do |seq|
each do |k, v|
seq.add(k => v)
end
end
end
end
# Hash is ordered in Ruby 1.9!
if RUBY_VERSION < '1.9'
def initialize(*args, &block)
super
@keys = []
end
def self.[](*args)
ordered_hash = new
if (args.length == 1 && args.first.is_a?(Array))
args.first.each do |key_value_pair|
next unless (key_value_pair.is_a?(Array))
ordered_hash[key_value_pair[0]] = key_value_pair[1]
end
return ordered_hash
end
unless (args.size % 2 == 0)
raise ArgumentError.new("odd number of arguments for Hash")
end
args.each_with_index do |val, ind|
next if (ind % 2 != 0)
ordered_hash[val] = args[ind + 1]
end
ordered_hash
end
def initialize_copy(other)
super
# make a deep copy of keys
@keys = other.keys
end
def []=(key, value)
@keys << key if !has_key?(key)
super
end
def delete(key)
if has_key? key
index = @keys.index(key)
@keys.delete_at index
end
super
end
def delete_if
super
sync_keys!
self
end
def reject!
super
sync_keys!
self
end
def reject(&block)
dup.reject!(&block)
end
def keys
@keys.dup
end
def values
@keys.collect { |key| self[key] }
end
def to_hash
self
end
def to_a
@keys.map { |key| [ key, self[key] ] }
end
def each_key
@keys.each { |key| yield key }
end
def each_value
@keys.each { |key| yield self[key]}
end
def each
@keys.each {|key| yield [key, self[key]]}
end
alias_method :each_pair, :each
def clear
super
@keys.clear
self
end
def shift
k = @keys.first
v = delete(k)
[k, v]
end
def merge!(other_hash)
if block_given?
other_hash.each { |k, v| self[k] = key?(k) ? yield(k, self[k], v) : v }
else
other_hash.each { |k, v| self[k] = v }
end
self
end
alias_method :update, :merge!
def merge(other_hash, &block)
dup.merge!(other_hash, &block)
end
# When replacing with another hash, the initial order of our keys must come from the other hash -ordered or not.
def replace(other)
super
@keys = other.keys
self
end
def invert
OrderedHash[self.to_a.map!{|key_value_pair| key_value_pair.reverse}]
end
def inspect
"#<OrderedHash #{super}>"
end
private
def sync_keys!
@keys.delete_if {|k| !has_key?(k)}
end
end
end
end

View File

@@ -1,5 +1,5 @@
module ActiveSupport #:nodoc:
class OrderedOptions < Hash #:nodoc:
class OrderedOptions < OrderedHash #:nodoc:
def []=(key, value)
super(key.to_sym, value)
end

View File

@@ -20,7 +20,7 @@ module RailsGuides
def process(string, current_level=3, counters=[1])
s = StringScanner.new(string)
level_hash = {}
level_hash = ActiveSupport::OrderedHash.new
while !s.eos?
re = %r{^h(\d)(?:\((#.*?)\))?\s*\.\s*(.*)$}

View File

@@ -34,7 +34,7 @@ module Rails
def initialize(app)
@app = app
@metals = {}
@metals = ActiveSupport::OrderedHash.new
self.class.metals.each { |app| @metals[app] = true }
freeze
end