diff options
author | Andrew White <andyw@pixeltrix.co.uk> | 2012-12-19 20:54:47 +0000 |
---|---|---|
committer | Andrew White <andyw@pixeltrix.co.uk> | 2012-12-19 22:13:08 +0000 |
commit | 56fee39c392788314c44a575b3fd66e16a50c8b5 (patch) | |
tree | e12603fff0d1e7c69d021f822b4077a74b91ddc4 /actionpack/lib/action_dispatch/journey/path | |
parent | b225693a0d86f2e33c66049a69e5148e5c93b7cd (diff) | |
download | rails-56fee39c392788314c44a575b3fd66e16a50c8b5.tar.gz rails-56fee39c392788314c44a575b3fd66e16a50c8b5.tar.bz2 rails-56fee39c392788314c44a575b3fd66e16a50c8b5.zip |
Integrate Journey into Action Dispatch
Move the Journey code underneath the ActionDispatch namespace so
that we don't pollute the global namespace with names that may
be used for models.
Fixes rails/journey#49.
Diffstat (limited to 'actionpack/lib/action_dispatch/journey/path')
-rw-r--r-- | actionpack/lib/action_dispatch/journey/path/pattern.rb | 195 |
1 files changed, 195 insertions, 0 deletions
diff --git a/actionpack/lib/action_dispatch/journey/path/pattern.rb b/actionpack/lib/action_dispatch/journey/path/pattern.rb new file mode 100644 index 0000000000..e14168aeb2 --- /dev/null +++ b/actionpack/lib/action_dispatch/journey/path/pattern.rb @@ -0,0 +1,195 @@ +module ActionDispatch + module Journey + module Path + class Pattern + attr_reader :spec, :requirements, :anchored + + def initialize strexp + parser = Journey::Parser.new + + @anchored = true + + case strexp + when String + @spec = parser.parse strexp + @requirements = {} + @separators = "/.?" + when Router::Strexp + @spec = parser.parse strexp.path + @requirements = strexp.requirements + @separators = strexp.separators.join + @anchored = strexp.anchor + else + raise "wtf bro: #{strexp}" + end + + @names = nil + @optional_names = nil + @required_names = nil + @re = nil + @offsets = nil + end + + def ast + @spec.grep(Nodes::Symbol).each do |node| + re = @requirements[node.to_sym] + node.regexp = re if re + end + + @spec.grep(Nodes::Star).each do |node| + node = node.left + node.regexp = @requirements[node.to_sym] || /(.+)/ + end + + @spec + end + + def names + @names ||= spec.grep(Nodes::Symbol).map { |n| n.name } + end + + def required_names + @required_names ||= names - optional_names + end + + def optional_names + @optional_names ||= spec.grep(Nodes::Group).map { |group| + group.grep(Nodes::Symbol) + }.flatten.map { |n| n.name }.uniq + end + + class RegexpOffsets < Journey::Visitors::Visitor # :nodoc: + attr_reader :offsets + + def initialize matchers + @matchers = matchers + @capture_count = [0] + end + + def visit node + super + @capture_count + end + + def visit_SYMBOL node + node = node.to_sym + + if @matchers.key? node + re = /#{@matchers[node]}|/ + @capture_count.push((re.match('').length - 1) + (@capture_count.last || 0)) + else + @capture_count << (@capture_count.last || 0) + end + end + end + + class AnchoredRegexp < Journey::Visitors::Visitor # :nodoc: + def initialize separator, matchers + @separator = separator + @matchers = matchers + @separator_re = "([^#{separator}]+)" + super() + end + + def accept node + %r{\A#{visit node}\Z} + end + + def visit_CAT node + [visit(node.left), visit(node.right)].join + end + + def visit_SYMBOL node + node = node.to_sym + + return @separator_re unless @matchers.key? node + + re = @matchers[node] + "(#{re})" + end + + def visit_GROUP node + "(?:#{visit node.left})?" + end + + def visit_LITERAL node + Regexp.escape node.left + end + alias :visit_DOT :visit_LITERAL + + def visit_SLASH node + node.left + end + + def visit_STAR node + re = @matchers[node.left.to_sym] || '.+' + "(#{re})" + end + end + + class UnanchoredRegexp < AnchoredRegexp # :nodoc: + def accept node + %r{\A#{visit node}} + end + end + + class MatchData + attr_reader :names + + def initialize names, offsets, match + @names = names + @offsets = offsets + @match = match + end + + def captures + (length - 1).times.map { |i| self[i + 1] } + end + + def [] x + idx = @offsets[x - 1] + x + @match[idx] + end + + def length + @offsets.length + end + + def post_match + @match.post_match + end + + def to_s + @match.to_s + end + end + + def match other + return unless match = to_regexp.match(other) + MatchData.new names, offsets, match + end + alias :=~ :match + + def source + to_regexp.source + end + + def to_regexp + @re ||= regexp_visitor.new(@separators, @requirements).accept spec + end + + private + def regexp_visitor + @anchored ? AnchoredRegexp : UnanchoredRegexp + end + + def offsets + return @offsets if @offsets + + viz = RegexpOffsets.new @requirements + @offsets = viz.accept spec + end + end + end + end +end |