Skip to content

Commit

Permalink
Remove recursion from MatchFinder (#26)
Browse files Browse the repository at this point in the history
* Update benchmark for more consisten results

* Update crystal version to 0.34 in shard.yml

* Removed recursion from MatchFinder - 9.3

* Update crystal version to 0.35.0

* Use Time.measure in benchmark

* Update comment on MatchFinder#run

* Use loop instead of until in MatchFinder#run

* Remove unused code from benchmark
  • Loading branch information
Matthew McGarvey authored Jun 23, 2020
1 parent 8b66bbd commit 72362f1
Show file tree
Hide file tree
Showing 3 changed files with 32 additions and 28 deletions.
2 changes: 1 addition & 1 deletion shard.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ version: 0.2.2
authors:
- Paul Smith <[email protected]>

crystal: 0.27
crystal: 0.35

license: MIT
27 changes: 15 additions & 12 deletions src/benchmark.cr
Original file line number Diff line number Diff line change
Expand Up @@ -16,21 +16,24 @@ router.add("put", "/users/:id", :update)
router.add("get", "/users/:id/edit", :edit)
router.add("get", "/users/:id/new", :new)

time = Time.now

elapsed_times = [] of Time::Span
1000.times do
router.match!("post", "/users")
router.match!("get", "/users/1")
router.match!("delete", "/users/1")
router.match!("put", "/users/1")
router.match!("get", "/users/1/edit")
router.match!("get", "/users/1/new")
elapsed = Time.measure do
1000.times do
router.match!("post", "/users")
router.match!("get", "/users/1")
router.match!("delete", "/users/1")
router.match!("put", "/users/1")
router.match!("get", "/users/1/edit")
router.match!("get", "/users/1/new")
end
end
elapsed_times << elapsed
end

elapsed = Time.now - time
elapsed_text = elapsed_text(elapsed)

puts elapsed_text
sum = elapsed_times.sum
average = sum / elapsed_times.size
puts "Average time: " + elapsed_text(average)

private def elapsed_text(elapsed)
minutes = elapsed.total_minutes
Expand Down
31 changes: 16 additions & 15 deletions src/lucky_router/match_finder.cr
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
class LuckyRouter::MatchFinder(T)
private getter fragment, parts, params
private getter parts, params
private property fragment

@fragment : Fragment(T)
# The parts are the raw strings from each section of the path.
Expand All @@ -17,23 +18,23 @@ class LuckyRouter::MatchFinder(T)
def initialize(@fragment, @parts, @params = {} of String => String)
end

# This uses the magic/pain of recursion to continue matching fragments
# until there is a match in the final fragment, otherwise it returns `NoMatch`
# This looks for a matching fragment for the given parts
# and returns NoMatch if one is not found
def run : Match(T) | NoMatch
add_to_params if has_param?
loop do
match = matched_fragment
return NoMatch.new if match.nil?

if last_part? && has_match?
# If we're on the last part and have a match, return the payload and params :D
Match(T).new(matched_fragment.not_nil!.payload.not_nil!, params)
elsif static_fragment
# Always try to match a static part before a dynamic one
MatchFinder(T).new(static_fragment.not_nil!, next_parts, params).run
elsif dynamic_fragment
# If no static part matches, check if the part can by dynamic
MatchFinder(T).new(dynamic_fragment.not_nil!, next_parts, params).run
else
NoMatch.new
add_to_params if has_param?
if last_part? && has_match?
return Match(T).new(matched_fragment.not_nil!.payload.not_nil!, params)
end
self.fragment = match
parts.shift
break if parts.empty?
end

NoMatch.new
end

private def has_param?
Expand Down

0 comments on commit 72362f1

Please sign in to comment.