eltiare (owner)

Revisions

gist: 20141 Download_button fork
public
Public Clone URL: git://gist.github.com/20141.git
Embed All Files: show embed
Text #
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
#I'm trying to make a dynamic router in Merb based on information in the
#database. Basically, I want the user to able to build their own URL
#"tree" with the home page being at the root. If what the user wants is
#a single page in a location, getting this information is easy: all one
#has to do is store the path in the database and retrieve it. However,
#if one wants to be adding different types of functionality (such as a
#blog) to a certain part of the tree, it gets a lot trickier because you
#have the base path of the blog and then all the other paths that make
#the blog function such as:
#
#Base path: /blog (stored in db)
#
#blog/posts blog/posts/new blog/posts/5 blog/posts/5/comments
#blog/posts/5/comments/new
#
#You can easily see how nasty it can get to have to define all these
#routes by hand for each module or slice. For a shopping cart, you could
#easily have tens to over 100 routes to define depending on how complex
#you want to make things. I'd prefer to use Merb's router instead of
#defining routes' regexp's individually as it is powerful and
#significantly easier to use. Here are the steps that I've come up with
#so far to help me reach this end:
#
#1. Create a database that will hold the path information for single
#pages and for the base of the modules (such as '/blog' from before).
#
#2. Populate database with paths such as '/' and '/blog' - with
#corresponding such as 'Routers::Blog' or 'Routers::PhotoGallary' in the
#class_name (see code link above).
#
#3. Check the database to see if the path exists for a _single_ page or
#the base of a module/slice. In this case, all the router has to worry
#about is returning the controller and action and other information
#associated with this page. This part is easy.
#
#4. If no single page is found, go through all the rows in the database,
#looking for a match of the beginning of the path. For instance, the
#request path of /blog/posts/3 would match the record in the database
#/blog: request.path.match(%r"^#{row.path}"
#
#5. From there, the the matched portion of the path would be removed from
#the path, and the remaining string passed to the appropriate model for
#further processing, so that it could figure out which controller it
#needs to send the information to as well as other informational
#processing tasks depending on the module.
#
#6: Routes are defined in each model that handles a different
#module/slice. This is why I want to have different routes that are
#separate from the main application's routes (ie, a subclassed route).
#The routes would be matched from the modified path from the previoius
#step.
 
Merb::Router.prepare do
  match(/\/.*/).defer_to { |request, params| Router::Base.get_hash(request, params) }
end
 
module Router
  class Base
    class FakeRequest
      def initialize(request); @request = request; end
      def path=(path); @path = path; end
      def path; @path || @request.path; end
      def method_missing(method, *args); @request.send(method, *args); end
    end
 
    include DataMapper::Resource
 
    property :id, Serial
    property :class_name, Discriminator, :index => true
    property :path, String, :unique_index => true, :length => 255
    property :subdomain, String, :index => true, :length => 150
    property :child_id, String, :length => 255
 
 
    class << self
      def default_storage_name; 'route'; end
      def default_hash; {:controller => 'pages', :action => 'show'}; end
      
      def router; @router ||= Class.new Eltiare::Router; end
 
      def get_hash(request, params)
        # First, check to see if there is something that matches the path exactly
        page = first(:path => request.path)
 
        if page && page.class_name == self.name
          params[:page] = request.path
          return params.merge(default_hash)
        elsif page
          params[:id] = page.id
          return params.merge(page.class.default_hash)
        else # Get the route of child object
          all(:class_name.not => self.name).each do |router|
            path_regexp = %r"^#{router.path}"
            if request.path.match(path_regexp) && request.subdomains.join('.') == router.subdomain.to_s
              req = FakeRequest.new(request)
              req.path = req.path.sub(path_regexp, '')
              params[:id] = router.child_id # This will get overwritten later if specified
              return router.get_hash(req, params)
            end
          end
        end
      end # get_hash
    end # class << self
 
    def get_hash(request, params)
      @route, @route_params = self.class.router.route_for(request)
      params.merge! @route_params if @route_params.is_a?(Hash)
      params
    end
 
  end # Base
end # Router
 
module Router
  class Blog < Base
    # if the blog module is installed at '/blog' and at '/eltiare/blog',
    # then the router below will match '/blog/things' _and_ '/eltiare/blog/things'
    router.prepare do |r|
      r.match('/things').to(:controller => 'things', :action => 'index')
    end
 
    class << self
      def default_hash; {:controller => 'stuff', :action => 'show'}; end
    end
  end
end # Router