提交 f746d34b 编写于 作者: J Justin Collins

Merge branch 'rails3'

-Add experimental support for Rails 3
......@@ -27,6 +27,10 @@ OptionParser.new do |opts|
$VERBOSE = nil
end
opts.on "-3", "--rails3", "[Experimental] Rails 3 support" do
options[:rails3] = true
end
opts.separator ""
opts.separator "Scanning options:"
......
......@@ -6,7 +6,11 @@ class FindModelCall < FindCall
#Passes +targets+ to FindCall
def initialize targets
super(targets, /^(find.*|first|last|all|count|sum|average|minumum|maximum|count_by_sql)$/, true)
if OPTIONS[:rails3]
super(targets, /^(find.*|first|last|all|where|order|group|having)$/, true)
else
super(targets, /^(find.*|first|last|all)$/, true)
end
end
#Matches entire method chain as a target. This differs from
......
require 'processors/base_processor'
#Processes the Sexp from routes.rb. Stores results in tracker.routes.
#
#Note that it is only interested in determining what methods on which
#controllers are used as routes, not the generated URLs for routes.
class RoutesProcessor < BaseProcessor
include RouteHelper
attr_reader :map, :nested, :current_controller
def initialize tracker
super
@map = Sexp.new(:lvar, :map)
@nested = nil #used for identifying nested targets
@prefix = [] #Controller name prefix (a module name, usually)
@current_controller = nil
@with_options = nil #For use inside map.with_options
end
#Call this with parsed route file information.
#
#This method first calls RouteAliasProcessor#process_safely on the +exp+,
#so it does not modify the +exp+.
def process_routes exp
process RouteAliasProcessor.new.process_safely(exp)
end
#Looking for mapping of routes
def process_call exp
target = exp[1]
if target == map or target == nested
process_map exp
else
process_default exp
end
exp
end
#Process a map.something call
#based on the method used
def process_map exp
args = exp[3][1..-1]
case exp[2]
when :resource
process_resource args
when :resources
process_resources args
when :connect, :root
process_connect args
else
process_named_route args
end
exp
end
#Look for map calls that take a block.
#Otherwise, just do the default processing.
def process_iter exp
if exp[1][1] == map or exp[1][1] == nested
method = exp[1][2]
case method
when :namespace
process_namespace exp
when :resources, :resource
process_resources exp[1][3][1..-1]
process_default exp[3]
when :with_options
process_with_options exp
end
exp
else
super
end
end
#Process
# map.resources :x, :controller => :y, :member => ...
#etc.
def process_resources exp
controller = check_for_controller_name exp
if controller
self.current_controller = controller
process_resource_options exp[-1]
else
exp.each do |argument|
if sexp? argument and argument.node_type == :lit
self.current_controller = exp[0][1]
add_resources_routes
process_resource_options exp[-1]
end
end
end
end
#Process all the options that might be in the hash passed to
#map.resource, et al.
def process_resource_options exp
if exp.nil? and @with_options
exp = @with_options
elsif @with_options
exp = exp.concat @with_options[1..-1]
end
return unless exp.node_type == :hash
hash_iterate(exp) do |option, value|
case option[1]
when :controller, :requirements, :singular, :path_prefix, :as,
:path_names, :shallow, :name_prefix
#should be able to skip
when :collection, :member, :new
process_collection value
when :has_one
save_controller = current_controller
process_resource value[1..-1]
self.current_controller = save_controller
when :has_many
save_controller = current_controller
process_resources value[1..-1]
self.current_controller = save_controller
when :only
process_option_only value
when :except
process_option_except value
else
raise "Unhandled resource option: #{option}"
end
end
end
#Process route option :only => ...
def process_option_only exp
routes = @tracker.routes[@current_controller]
[:index, :new, :create, :show, :edit, :update, :destroy].each do |r|
routes.delete r
end
if exp.node_type == :array
exp[1..-1].each do |e|
routes << e[1]
end
end
end
#Process route option :except => ...
def process_option_except exp
return unless exp.node_type == :array
routes = @tracker.routes[@current_controller]
exp[1..-1].each do |e|
routes.delete e[1]
end
end
# map.resource :x, ..
def process_resource exp
controller = check_for_controller_name exp
if controller
self.current_controller = controller
process_resource_options exp[-1]
else
exp.each do |argument|
if argument.node_type == :lit
self.current_controller = pluralize(exp[0][1].to_s)
add_resource_routes
process_resource_options exp[-1]
end
end
end
end
#Process
# map.connect '/something', :controller => 'blah', :action => 'whatever'
def process_connect exp
controller = check_for_controller_name exp
self.current_controller = controller if controller
#Check for default route
if string? exp[0]
if exp[0][1] == ":controller/:action/:id"
@tracker.routes[:allow_all_actions] = exp[0]
elsif exp[0][1].include? ":action"
@tracker.routes[@current_controller] = :allow_all_actions
return
end
end
#This -seems- redundant, but people might connect actions
#to a controller which already allows them all
return if @tracker.routes[@current_controller] == :allow_all_actions
exp[-1].each_with_index do |e,i|
if symbol? e and e[1] == :action
@tracker.routes[@current_controller] << exp[-1][i + 1][1].to_sym
return
end
end
end
# map.with_options :controller => 'something' do |something|
# something.resources :blah
# end
def process_with_options exp
@with_options = exp[1][3][-1]
@nested = Sexp.new(:lvar, exp[2][1])
self.current_controller = check_for_controller_name exp[1][3]
#process block
process exp[3]
@with_options = nil
@nested = nil
end
# map.namespace :something do |something|
# something.resources :blah
# end
def process_namespace exp
call = exp[1]
formal_args = exp[2]
block = exp[3]
@prefix << camelize(call[3][1][1])
@nested = Sexp.new(:lvar, formal_args[1])
process block
@prefix.pop
end
# map.something_abnormal '/blah', :controller => 'something', :action => 'wohoo'
def process_named_route exp
process_connect exp
end
#Process collection option
# :collection => { :some_action => :http_actions }
def process_collection exp
return unless exp.node_type == :hash
routes = @tracker.routes[@current_controller]
hash_iterate(exp) do |action, type|
routes << action[1]
end
end
private
#Checks an argument list for a hash that has a key :controller.
#If it does, returns the value.
#
#Otherwise, returns nil.
def check_for_controller_name args
args.each do |a|
if hash? a
hash_iterate(a) do |k, v|
if k[1] == :controller
return v[1]
end
end
end
end
nil
end
end
#This is for a really specific case where a hash is used as arguments
#to one of the map methods.
class RouteAliasProcessor < AliasProcessor
#This replaces
# { :some => :hash }.keys
#with
# [:some]
def process_call exp
process_default exp
if hash? exp[1] and exp[2] == :keys
keys = get_keys exp[1]
exp.clear
keys.each_with_index do |e,i|
exp[i] = e
end
end
exp
end
#Returns an array Sexp containing the keys from the hash
def get_keys hash
keys = Sexp.new(:array)
hash_iterate(hash) do |key, value|
keys << key
end
keys
end
end
#Processes the Sexp from routes.rb. Stores results in tracker.routes.
#
#Note that it is only interested in determining what methods on which
#controllers are used as routes, not the generated URLs for routes.
class RoutesProcessor < BaseProcessor
include RouteHelper
attr_reader :map, :nested, :current_controller
def initialize tracker
super
@map = Sexp.new(:lvar, :map)
@nested = nil #used for identifying nested targets
@prefix = [] #Controller name prefix (a module name, usually)
@current_controller = nil
@with_options = nil #For use inside map.with_options
end
def process_routes exp
process exp.dup
end
def process_call exp
case exp[2]
when :resources
process_resources exp
when :resource
process_resource exp
when :root
process_root exp
when :member
process_default exp
when :get, :put, :post, :delete
process_verb exp
when :match
process_match exp
else
exp
end
end
def process_iter exp
case exp[1][2]
when :namespace
process_namespace exp
when :resource
process_resource_block exp
when :resources
process_resources_block exp
when :scope
process_scope_block exp
else
super
end
end
def process_namespace exp
name = exp[1][3][1][1]
block = exp[3]
@prefix << camelize(name)
process block
@prefix.pop
exp
end
def process_root exp
args = exp[3][1..-1]
hash_iterate args[0] do |k, v|
if symbol? k and k[1] == :to
controller, action = extract_action v[1]
self.current_controller = controller
@tracker.routes[@current_controller] << action.to_sym
break
end
end
exp
end
def process_match exp
args = exp[3][1..-1]
hash_iterate args[0] do |k, v|
if string? k and string? v
controller, action = extract_action v[1]
self.current_controller = controller
@tracker.routes[@current_controller] << action.to_sym if action
elsif symbol? k and k[1] == :action
@tracker.routes[@current_controller] << v[1].to_sym
end
end
exp
end
def process_verb exp
args = exp[3][1..-1]
if symbol? args[0]
@tracker.routes[@current_controller] << args[0][1]
elsif string? args[0]
route = args[0][1].split "/"
if route.length != 2
@tracker.routes[@current_controller] << route[0].to_sym
else
self.current_controller = route[0]
@tracker.routes[@current_controller] << route[1].to_sym
@current_controller = nil
end
else hash? args[0]
hash_iterate args[0] do |k, v|
if string? v
controller, action = extract_action v[1]
self.current_controller = controller
@tracker.routes[@current_controller] << action.to_sym
end
end
end
exp
end
def process_resources exp
if exp[3] and exp[3][2] and exp[3][2][0] == :hash
#handle hash
elsif exp[3][1..-1].all? { |s| symbol? s }
exp[3][1..-1].each do |s|
self.current_controller = s[1]
add_resources_routes
end
end
exp
end
def process_resource exp
exp[3][1..-1].each do |s|
self.current_controller = s[1]
add_resource_routes
end
exp
end
def process_resources_block exp
process_resources exp[1]
process exp[3]
end
def process_resource_block exp
process_resource exp[1]
process exp[3]
end
def process_scope_block exp
#How to deal with options?
process exp[3]
end
def extract_action str
str.split "#"
end
end
module RouteHelper
#Manage Controller prefixes
#@prefix is an Array, but this method returns a string
#suitable for prefixing onto a controller name.
def prefix
if @prefix.length > 0
@prefix.join("::") << "::"
else
''
end
end
#Sets the controller name to a proper class name.
#For example
# self.current_controller = :session
# @controller == :SessionController #true
#
#Also prepends the prefix if there is one set.
def current_controller= name
@current_controller = (prefix + camelize(name) + "Controller").to_sym
@tracker.routes[@current_controller] ||= Set.new
end
#Add default routes
def add_resources_routes
@tracker.routes[@current_controller].merge [:index, :new, :create, :show, :edit, :update, :destroy]
end
#Add default routes minus :index
def add_resource_routes
@tracker.routes[@current_controller].merge [:new, :create, :show, :edit, :update, :destroy]
end
end
require 'processors/base_processor'
require 'processors/alias_processor'
require 'processors/lib/route_helper'
require 'util'
require 'set'
#Processes the Sexp from routes.rb. Stores results in tracker.routes.
#
#Note that it is only interested in determining what methods on which
#controllers are used as routes, not the generated URLs for routes.
class RoutesProcessor < BaseProcessor
attr_reader :map, :nested, :current_controller
def initialize tracker
super
@map = Sexp.new(:lvar, :map)
@nested = nil #used for identifying nested targets
@prefix = [] #Controller name prefix (a module name, usually)
@current_controller = nil
@with_options = nil #For use inside map.with_options
end
#Call this with parsed route file information.
#
#This method first calls RouteAliasProcessor#process_safely on the +exp+,
#so it does not modify the +exp+.
def process_routes exp
process RouteAliasProcessor.new.process_safely(exp)
end
#Looking for mapping of routes
def process_call exp
target = exp[1]
if target == map or target == nested
process_map exp
else
process_default exp
end
exp
end
#Process a map.something call
#based on the method used
def process_map exp
args = exp[3][1..-1]
case exp[2]
when :resource
process_resource args
when :resources
process_resources args
when :connect, :root
process_connect args
else
process_named_route args
end
exp
end
#Look for map calls that take a block.
#Otherwise, just do the default processing.
def process_iter exp
if exp[1][1] == map or exp[1][1] == nested
method = exp[1][2]
case method
when :namespace
process_namespace exp
when :resources, :resource
process_resources exp[1][3][1..-1]
process_default exp[3]
when :with_options
process_with_options exp
end
exp
else
super
end
end
#Process
# map.resources :x, :controller => :y, :member => ...
#etc.
def process_resources exp
controller = check_for_controller_name exp
if controller
self.current_controller = controller
process_resource_options exp[-1]
else
exp.each do |argument|
if sexp? argument and argument.node_type == :lit
self.current_controller = exp[0][1]
add_resources_routes
process_resource_options exp[-1]
end
end
end
end
#Add default routes
def add_resources_routes
@tracker.routes[@current_controller].merge [:index, :new, :create, :show, :edit, :update, :destroy]
end
#Process all the options that might be in the hash passed to
#map.resource, et al.
def process_resource_options exp
if exp.nil? and @with_options
exp = @with_options
elsif @with_options
exp = exp.concat @with_options[1..-1]
end
return unless exp.node_type == :hash
hash_iterate(exp) do |option, value|
case option[1]
when :controller, :requirements, :singular, :path_prefix, :as,
:path_names, :shallow, :name_prefix
#should be able to skip
when :collection, :member, :new
process_collection value
when :has_one
save_controller = current_controller
process_resource value[1..-1]
self.current_controller = save_controller
when :has_many
save_controller = current_controller
process_resources value[1..-1]
self.current_controller = save_controller
when :only
process_option_only value
when :except
process_option_except value
else
raise "Unhandled resource option: #{option}"
end
end
end
#Process route option :only => ...
def process_option_only exp
routes = @tracker.routes[@current_controller]
[:index, :new, :create, :show, :edit, :update, :destroy].each do |r|
routes.delete r
end
if exp.node_type == :array
exp[1..-1].each do |e|
routes << e[1]
end
end
end
#Process route option :except => ...
def process_option_except exp
return unless exp.node_type == :array
routes = @tracker.routes[@current_controller]
exp[1..-1].each do |e|
routes.delete e[1]
end
end
# map.resource :x, ..
def process_resource exp
controller = check_for_controller_name exp
if controller
self.current_controller = controller
process_resource_options exp[-1]
else
exp.each do |argument|
if argument.node_type == :lit
self.current_controller = pluralize(exp[0][1].to_s)
add_resource_routes
process_resource_options exp[-1]
end
end
end
end
#Add default routes minus :index
def add_resource_routes
@tracker.routes[@current_controller].merge [:new, :create, :show, :edit, :update, :destroy]
end
#Process
# map.connect '/something', :controller => 'blah', :action => 'whatever'
def process_connect exp
controller = check_for_controller_name exp
self.current_controller = controller if controller
#Check for default route
if string? exp[0]
if exp[0][1] == ":controller/:action/:id"
@tracker.routes[:allow_all_actions] = exp[0]
elsif exp[0][1].include? ":action"
@tracker.routes[@current_controller] = :allow_all_actions
return
end
end
#This -seems- redundant, but people might connect actions
#to a controller which already allows them all
return if @tracker.routes[@current_controller] == :allow_all_actions
exp[-1].each_with_index do |e,i|
if symbol? e and e[1] == :action
@tracker.routes[@current_controller] << exp[-1][i + 1][1].to_sym
return
end
end
end
# map.with_options :controller => 'something' do |something|
# something.resources :blah
# end
def process_with_options exp
@with_options = exp[1][3][-1]
@nested = Sexp.new(:lvar, exp[2][1])
self.current_controller = check_for_controller_name exp[1][3]
#process block
process exp[3]
@with_options = nil
@nested = nil
end
# map.namespace :something do |something|
# something.resources :blah
# end
def process_namespace exp
call = exp[1]
formal_args = exp[2]
block = exp[3]
@prefix << camelize(call[3][1][1])
@nested = Sexp.new(:lvar, formal_args[1])
process block
@prefix.pop
end
# map.something_abnormal '/blah', :controller => 'something', :action => 'wohoo'
def process_named_route exp
process_connect exp
end
#Process collection option
# :collection => { :some_action => :http_actions }
def process_collection exp
return unless exp.node_type == :hash
routes = @tracker.routes[@current_controller]
hash_iterate(exp) do |action, type|
routes << action[1]
end
end
#Manage Controller prefixes
#@prefix is an Array, but this method returns a string
#suitable for prefixing onto a controller name.
def prefix
if @prefix.length > 0
@prefix.join("::") << "::"
else
''
end
end
#Sets the controller name to a proper class name.
#For example
# self.current_controller = :session
# @controller == :SessionController #true
#
#Also prepends the prefix if there is one set.
def current_controller= name
@current_controller = (prefix + camelize(name) + "Controller").to_sym
@tracker.routes[@current_controller] ||= Set.new
end
private
#Checks an argument list for a hash that has a key :controller.
#If it does, returns the value.
#
#Otherwise, returns nil.
def check_for_controller_name args
args.each do |a|
if hash? a
hash_iterate(a) do |k, v|
if k[1] == :controller
return v[1]
end
end
end
end
nil
end
end
#This is for a really specific case where a hash is used as arguments
#to one of the map methods.
class RouteAliasProcessor < AliasProcessor
#This replaces
# { :some => :hash }.keys
#with
# [:some]
def process_call exp
process_default exp
if hash? exp[1] and exp[2] == :keys
keys = get_keys exp[1]
exp.clear
keys.each_with_index do |e,i|
exp[i] = e
end
end
exp
end
#Returns an array Sexp containing the keys from the hash
def get_keys hash
keys = Sexp.new(:array)
hash_iterate(hash) do |key, value|
keys << key
end
keys
end
if OPTIONS[:rails3]
require 'processors/lib/rails3_route_processor'
else
require 'processors/lib/rails2_route_processor'
end
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册