module Crystal class Router inject( :config => :config, :workspace => :workspace ) attr_reader :routes, :format_processor def initialize class_variable, routes = [[:default_router, DefaultRouter.new]], format_processor = DefaultFormatProcessor.new @class_variable, @format_processor = class_variable, format_processor @routes = Dictionary.new routes.each do |k, v| k.must_be.present v.must_be.present v.must.respond_to(:decode) v.must.respond_to(:encode) @routes[k] = v end end def url_for *args if args.first.is_a?(Class) args.size.must_be.in 2..3 klass, method, options = args url_for_class(klass, method, (options || {})) else first = args.first.to_s if first !~ /^\// args.size.must_be.in 1..2 method, options = args url_for_class(nil, method, (options || {})) else args.size.must_be.in 1..2 path, options = args url_for_path(path, (options || {})) end end end def decode path, params path.first.must == '/' params = params.to_openobject path, format = format_processor.remove_format path if format_processor result = nil routes.each do |name, route| result = route.decode path, params break if result end raise "No route for '#{workspace.path}' request!" unless result klass, method, params = result method ||= config.default_method params.must_be.defined raise "Invalid route! No method '#{method}' for #{klass}!" unless klass.instance_methods.include? method raise "Invalid route! You try to call protected method '#{method}' for '#{klass}'!" unless klass.public_instance_methods.include? method return klass, method, params end def encode klass, method, params = {} klass.must_be.defined method, params = method.to_s, params.to_openobject format = params.delete :format raise "Invalid route! No method '#{method}' for #{klass}!" unless klass.instance_methods.include? method raise "Invalid route! You try to call protected method '#{method}' for '#{klass}'!" unless klass.public_instance_methods.include? method result = nil routes.each do |name, route| result = route.encode klass, method, params break if result end raise "No route for '#{klass}.#{method}'!" unless result path, params = result path.must_be.defined params.must_be.defined path = format_processor.add_format path, format if format_processor and format return path, params end def persist_params &block before = Thread.current[:persist_params] begin Thread.current[:persist_params] = true block.call ensure Thread.current[:persist_params] = before end end def dont_persist_params &block before = Thread.current[:persist_params] begin Thread.current[:persist_params] = false block.call ensure Thread.current[:persist_params] = before end end def persist_params? !!Thread.current[:persist_params] end def global_persistent_params; @global_persistent_params ||= [] end protected def current_class; workspace[@class_variable] end def url_for_class klass, method, params klass ||= current_class params = params.to_openobject # special params format = params.format path, params = encode klass, method.to_s, params url = url_for_path path, params url.marks.format = format.to_s if format url end def url_for_path path, params params = params.to_openobject # special params url = if params.include? :url_root (params.delete(:url_root) || "") + path else config.url_root! + path end host, port = params.delete(:host), params.delete(:port) format = params.delete(:format) raise ":no_prefix is deprecated!" if params.no_prefix? # TODO3 now it uses :url_root instead, remove this after updating spaces # inject persistent params inject_persistent_params! params, params.delete(:persist) # json params = {:json => params.to_json}.to_openobject if params.delete :as_json #and !params.empty? # format if format params.format = format url.marks.format = format.to_s end # Delete 'nil' parameters to_delete = [] params.each{|k, v| to_delete << k if v.nil?} to_delete.each{|k| params.delete k} # build url delimiter = path.include?('?') ? '&' : '?' url << "#{delimiter}#{params.to_query}" unless params.empty? if host.blank? url else %{http://#{host}#{":#{port}" unless port.blank?}#{url}} end end DONOT_PERSIST = ['_', '_method'] def inject_persistent_params! params, persist # global persistent params unless cached_global_persistend_params = workspace.cached_global_persistend_params cached_global_persistend_params, workspace_params = {}, workspace.params global_persistent_params.collect!{|k| k.to_s} workspace_params.each do |k, v| k = k.to_s cached_global_persistend_params[k] = v if global_persistent_params.include?(k) end workspace.cached_global_persistend_params = cached_global_persistend_params end cached_global_persistend_params.each do |k, v| params[k] = v unless params.include? k end # underscored persistent params if persist_params? or persist unless cached_persistend_params = workspace.cached_persistend_params cached_persistend_params, workspace_params = {}, workspace.params workspace_params.each do |k, v| k = k.to_s cached_persistend_params[k] = v if (k =~ /^_/) and !DONOT_PERSIST.include?(k) end workspace.cached_persistend_params = cached_persistend_params end cached_persistend_params.each do |k, v| params[k] = v unless params.include? k end end end end end