Class: Routable::Router

Inherits:
Object
  • Object
show all
Defined in:
lib/routable/router.rb

Constant Summary collapse

TRANSITION_STYLES =
{
  :cover => UIModalTransitionStyleCoverVertical,
  :flip => UIModalTransitionStyleFlipHorizontal,
  :dissolve => UIModalTransitionStyleCrossDissolve,
  :curl => UIModalTransitionStylePartialCurl
}
PRESENTATION_STYLES =
{
  :full_screen => UIModalPresentationFullScreen,
  :page_sheet => UIModalPresentationPageSheet,
  :form_sheet => UIModalPresentationFormSheet,
  :current => UIModalPresentationCurrentContext
}

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Instance Attribute Details

The root UINavigationController we use to push/pop view controllers



26
27
28
# File 'lib/routable/router.rb', line 26

def navigation_controller
  @navigation_controller
end

Class Method Details

.routerObject



20
21
22
# File 'lib/routable/router.rb', line 20

def router
  @router ||= Router.new
end

Instance Method Details

#controller_for_url(url) ⇒ Object

Returns a UIViewController for the given url EX router.controller_for_url(“users/3”)

> #<UsersController @id=‘3’>



181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
# File 'lib/routable/router.rb', line 181

def controller_for_url(url)
  return shared_vc_cache[url] if shared_vc_cache[url]

  open_options = options_for_url(url)
  open_params = open_options[:open_params]
  open_klass = open_options[:klass]
  controller = open_klass.alloc
  if controller.respond_to? :initWithParams
    controller = controller.initWithParams(open_params)
  else
    controller = controller.init
  end

  if open_options[:shared]
    shared_vc_cache[url] = controller
    # when controller.viewDidUnload called, remove from cache.
    controller.class.class_eval do
      define_method(:new_dealloc) do
        shared_vc_cache.delete url
      end
    end
    controller.instance_eval do
      def viewDidUnload
        new_dealloc
        super
      end
    end
  end

  transition = open_options[:transition]
  if transition
    controller.modalTransitionStyle = TRANSITION_STYLES[transition]
  end

  presentation = open_options[:presentation]
  if presentation
    controller.modalPresentationStyle = PRESENTATION_STYLES[presentation] 
  end

  controller
end

#map(url, klass = nil, options = {}, &callback) ⇒ Object

Map a URL to a UIViewController EX router.map “/users/:id”, UsersController PARAMS

url => the URL to map
klass (optional) => the UIViewController class to open
options (optional) => hash of options
&callback => the block to be run when opening the URL

OPTIONS

:modal => true/false
  - We present the VC modally (router is not shared between the new nav VC)
:shared => true/false
  - If URL is called again, we pop to that VC if it's in memory.
:transition => [:cover, :flip, :dissolve, :curl]
  - A symbol to represented transition style used. Mapped to UIModalTransitionStyle.
:presentation => [:full_screen, :page_sheet, :form_sheet, :current]
  - A symbol to represented presentation style used. Mapped to UIModalPresentationStyle.


54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
# File 'lib/routable/router.rb', line 54

def map(url, klass = nil, options = {}, &callback)
  format = url

  if callback
    self.routes[format] = options.merge!(callback: callback)
  end

  if options[:transition] && !TRANSITION_STYLES.keys.include?(options[:transition])
    raise ArgumentError, ":transition must be one of #{TRANSITION_STYLES.keys}"
  end

  if options[:presentation] && !PRESENTATION_STYLES.keys.include?(options[:presentation])
    raise ArgumentError, ":presentation must be one of #{PRESENTATION_STYLES.keys}"
  end

  self.routes[format] = options.merge!(klass: klass)
end

#open(url, animated = true) ⇒ Object

Push the UIViewController for the given url EX router.open(“users/3”)

> router.navigation_controller pushes a UsersController



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
# File 'lib/routable/router.rb', line 76

def open(url, animated = true)
  controller_options = options_for_url(url)

  if controller_options[:callback]
    controller_options[:callback].call
    return
  end

  controller = controller_for_url(url)
  if self.navigation_controller.modalViewController
    self.navigation_controller.dismissModalViewControllerAnimated(animated)
  end
  if controller_options[:modal]
    if controller.class == UINavigationController
      self.navigation_controller.presentModalViewController(controller, animated: animated)
    else
      tempNavigationController = UINavigationController.alloc.init
      tempNavigationController.pushViewController(controller, animated: false)
      tempNavigationController.modalTransitionStyle = controller.modalTransitionStyle
      tempNavigationController.modalPresentationStyle = controller.modalPresentationStyle
      self.navigation_controller.presentModalViewController(tempNavigationController, animated: animated)
    end
  else
    if self.navigation_controller.viewControllers.member? controller
      self.navigation_controller.popToViewController(controller, animated:animated)
    else
      self.navigation_controller.pushViewController(controller, animated:animated)
    end
  end
end

#options_for_url(url) ⇒ Object



118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
# File 'lib/routable/router.rb', line 118

def options_for_url(url)
  # map of url => options

  @url_options_cache ||= {}
  if @url_options_cache[url]
    return @url_options_cache[url]
  end

  parts = url.split("/")

  open_options = nil
  open_params = {}

  self.routes.each { |format, options|

    # If the # of path components isn't the same, then
    # it for sure isn't a match.
    format_parts = format.split("/")
    if format_parts.count != parts.count
      next
    end

    matched = true
    format_params = {}
    # go through each of the path compoenents and
    # check if they match up (symbols aside)
    format_parts.each_with_index {|format_part, index|
      check_part = parts[index]

      # if we're looking at a symbol (ie :user_id),
      # then note it and move on.
      if format_part[0] == ":"
        format_params[format_part[1..-1].to_sym] = check_part
        next
      end

      # if we're looking at normal strings,
      # check equality.
      if format_part != check_part
        matched = false
        break
      end
    }

    if !matched
      next
    end

    open_options = options
    open_params = format_params
  }

  if open_options == nil
    raise "No route found for url #{url}"
  end

  @url_options_cache[url] = open_options.merge(open_params: open_params)
end

#pop(animated = true) ⇒ Object

Pop the top level UIViewController EX router.pop



110
111
112
113
114
115
116
# File 'lib/routable/router.rb', line 110

def pop(animated = true)
  if self.navigation_controller.modalViewController
    self.navigation_controller.dismissModalViewControllerAnimated(animated)
  else
    self.navigation_controller.popViewControllerAnimated(animated)
  end
end

#routesObject

Hash of URL => UIViewController classes EX {“users/:id” => UsersController,

"users/:id/posts" => PostsController,
"users/:user_id/posts/:id" => PostController }


33
34
35
# File 'lib/routable/router.rb', line 33

def routes
  @routes ||= {}
end

#shared_vc_cacheObject



223
224
225
# File 'lib/routable/router.rb', line 223

def shared_vc_cache
  @shared_vc_cache ||= {}
end