Permalink
Browse files

reorg and rework of matchers. Now we've extracted the creation of mat…

…chers to another module for more segmentation and easier creation of customer matchers
  • Loading branch information...
1 parent 12fddf0 commit 2056e3155465a464f4f5d3d6db642c8b1036bf57 @mdp committed Feb 21, 2010
View
@@ -10,11 +10,13 @@ A tool to help format your sites mobile pages.
_initializers/divining\_rod.rb_
- DiviningRod::Matchers.define do |map|
- # map.ua /user_agent_regex/, :format => :blackberry, :tags => [:your_tag]
+ DiviningRod::Mapping.define do |map|
+ # map.ua /user_agent_regex/, :format => :wml, :tags => [:your_tag]
map.ua /Apple.*Mobile.*Safari/, :format => :webkit, :tags => [:apple, :youtube_capable] do |iphone|
iphone.ua /iPhone/, :tags => :iphone
- iphone.ua /iPad/, :tags => :ipad
+ iphone.ua /iPad/, :tags => :ipad do |ipad|
+ ipad.ua /Unicorns/, :tags => [:omg_unicorns, :magic], :format => :happiness
+ end
iphone.ua /iPod/, :tags => :ipod
map.ua /Android/, :format => :webkit, :tags => [:android, :youtube_capable, :google_gears]
map.subdomain /wap/, :format => :wap, :tags => [:crappy_old_phone]
@@ -56,10 +58,12 @@ _app/views/mobile/show.webkit.html_
## Note on the development
-This is still very much in beta, but we are using it in production. As such we plan
-to do our best to keep the API the same.
+Tags always merge, while all other hash keys get overridden. Tags also will always allow you to call them as
+booleans. Ex @profile.iphone?
-The user agent definitions will be updated here later this week.
+If the :format key isn't available we default to request.format.
+
+Any of keys can be used and queried arbitrarily.
## Todo
View
@@ -2,4 +2,5 @@ module DiviningRod; end
require 'divining_rod/profile'
require 'divining_rod/definition'
-require 'divining_rod/definitions'
+require 'divining_rod/mapping'
+require 'divining_rod/matchers'
@@ -16,9 +16,7 @@ def evaluate(request)
result = self
unless self.children.empty?
self.children.each do |child|
- if child.evaluate(request)
- child_result = child
- end
+ child_result = child.evaluate(request)
break if child_result
end
end
@@ -1,11 +1,11 @@
module DiviningRod
- class Definitions
+ class Mapping
# Matchers create and keep track of the definitions.
class << self
attr_accessor :definitions
-
+
def define
yield(self)
end
@@ -17,34 +17,26 @@ def evaluate(request)
end
match
end
-
- def ua(pattern, opts={}, &blk)
- if @parent #merge the settings if they have a parent definition
- tags = Array(opts[:tags]) | @parent.tags
- opts = @parent.opts.merge(opts)
- opts[:tags] = tags # tags are merged, not overridden
- end
- definition = Definition.new(opts) { |request|
- if pattern.match(request.user_agent)
- true
- end
- }
+
+ # The only really complicated part of this whole endeavor
+ # Matchers defines a definition method which is passed a regex pattern
+ # and options, and returns the defintion object.
+ # In this method we wrap the process to merge parent params and
+ # add the defintion as a child of another defintion.
+ #
+ # Ex. map.pattern :ua, /iPhone/, :tags => [:apple, :webkit], :format => :webkit
+ def pattern(type, pattern, opts = {})
+ opts = self.merge_parent(opts)
+ definition = Matchers.send(type.to_sym, pattern, opts)
add_definition(definition, @parent)
+ # And here's the recursive to let up define children of a definition
if block_given?
@parent = definition
yield(self)
end
@parent = nil #reset the scope
end
- def subdomain(pattern, opts={})
- add_definition Definition.new(opts) { |request|
- if pattern.match(request.subdomains[0])
- true
- end
- }
- end
-
def default(opts = {})
add_definition Definition.new(opts) { |request| true }
end
@@ -61,6 +53,26 @@ def add_definition(definition, parent = nil)
def clear_definitions
@definitions = []
end
+
+ def method_missing(meth, *args, &blk)
+ # Lets us use map.ua instead of map.pattern :ua
+ if Matchers.respond_to?(meth.to_sym)
+ self.pattern(meth, args[0], args[1,], &blk)
+ else
+ super
+ end
+ end
+
+ protected
+
+ def merge_parent(opts)
+ if @parent #merge the settings if they have a parent definition
+ tags = Array(opts[:tags]) | @parent.tags
+ opts = @parent.opts.merge(opts)
+ opts[:tags] = tags # tags are merged, not overridden
+ end
+ opts
+ end
end
@@ -0,0 +1,23 @@
+module DiviningRod
+ class Matchers
+ class << self
+
+ def ua(pattern, opts = {})
+ Definition.new(opts) { |request|
+ if pattern.match(request.user_agent)
+ true
+ end
+ }
+ end
+
+ def subdomain(pattern, opts={})
+ Definition.new(opts) { |request|
+ if pattern.match(request.subdomains[0])
+ true
+ end
+ }
+ end
+
+ end
+ end
+end
@@ -5,7 +5,7 @@ class Profile
def initialize(request)
@request = request.clone #Lets not mess with the real one
- @match = DiviningRod::Definitions.evaluate(request)
+ @match = DiviningRod::Mapping.evaluate(request)
end
def format
@@ -34,9 +34,5 @@ def method_missing(meth)
end
end
- def definitions
- DiviningRod::Matchers.definitions || []
- end
-
end
end
View
@@ -4,8 +4,8 @@
before :each do
@request = mock("rails_request", :user_agent => 'My iPhone which is actually an iPad')
- DiviningRod::Definitions.clear_definitions
- DiviningRod::Definitions.define do |map|
+ DiviningRod::Mapping.clear_definitions
+ DiviningRod::Mapping.define do |map|
map.ua /iPhone/, :format => :webkit, :tags => [:iphone, :youtube, :geolocate] do |iphone|
iphone.ua /iPad/, :tags => [:ipad]
end
@@ -32,8 +32,8 @@
before :each do
@request = mock("rails_request", :user_agent => 'My Foo Fone', :format => :html)
- DiviningRod::Definitions.clear_definitions
- DiviningRod::Definitions.define do |map|
+ DiviningRod::Mapping.clear_definitions
+ DiviningRod::Mapping.define do |map|
map.ua /iPhone/, :format => :webkit, :tags => [:iphone, :youtube, :geolocate]
end
end
@@ -51,8 +51,8 @@
before :each do
@request = mock("rails_request", :user_agent => 'My Foo Fone')
- DiviningRod::Definitions.clear_definitions
- DiviningRod::Definitions.define do |map|
+ DiviningRod::Mapping.clear_definitions
+ DiviningRod::Mapping.define do |map|
map.ua /iPhone/, :format => :webkit, :tags => [:iphone, :youtube, :geolocate]
map.default :format => :html
end
@@ -70,8 +70,8 @@
before :each do
@request = mock("rails_request", :user_agent => 'Foo Fone', :format => :html)
- DiviningRod::Definitions.clear_definitions
- DiviningRod::Definitions.define do |map|
+ DiviningRod::Mapping.clear_definitions
+ DiviningRod::Mapping.define do |map|
map.ua /iPhone/, :format => :webkit, :tags => [:iphone, :youtube, :geolocate]
end
end
@@ -86,8 +86,8 @@
before :each do
@request = mock("rails_request", :user_agent => 'Foo Fone', :subdomains => ['wap'])
- DiviningRod::Definitions.clear_definitions
- DiviningRod::Definitions.define do |map|
+ DiviningRod::Mapping.clear_definitions
+ DiviningRod::Mapping.define do |map|
map.subdomain /wap/, :format => :wap, :tags => [:shitty]
end
end
@@ -104,8 +104,8 @@
before :each do
@request = mock("rails_request", :user_agent => nil, :subdomains => [])
- DiviningRod::Definitions.clear_definitions
- DiviningRod::Definitions.define do |map|
+ DiviningRod::Mapping.clear_definitions
+ DiviningRod::Mapping.define do |map|
map.ua /iPhone/, :format => :wap, :tags => [:shitty]
end
end
@@ -119,34 +119,34 @@
end
-describe DiviningRod::Definitions do
+describe DiviningRod::Mapping do
before :each do
@request = mock("rails_request", :user_agent => 'iPhone Foo')
- DiviningRod::Definitions.clear_definitions
- DiviningRod::Definitions.define do |map|
+ DiviningRod::Mapping.clear_definitions
+ DiviningRod::Mapping.define do |map|
map.ua /iPhone/, :format => :iphone, :tags => [:iphone, :youtube]
end
end
it "should recognize an iPhone" do
- DiviningRod::Definitions.definitions.first.evaluate(@request).should be_true
- DiviningRod::Definitions.definitions.first.format.should eql(:iphone)
+ DiviningRod::Mapping.definitions.first.evaluate(@request).should be_true
+ DiviningRod::Mapping.definitions.first.format.should eql(:iphone)
end
describe "defining a default definition" do
before :each do
@request = mock("rails_request", :user_agent => 'Foo Fone')
- DiviningRod::Definitions.clear_definitions
- DiviningRod::Definitions.define do |map|
+ DiviningRod::Mapping.clear_definitions
+ DiviningRod::Mapping.define do |map|
map.default :format => :unknown, :tags => [:html]
end
end
it "should use the default route if no other match is found" do
- DiviningRod::Definitions.definitions.first.evaluate(@request).should be_true
- DiviningRod::Definitions.definitions.first.format.should eql(:unknown)
+ DiviningRod::Mapping.definitions.first.evaluate(@request).should be_true
+ DiviningRod::Mapping.definitions.first.format.should eql(:unknown)
end
end
@@ -1,11 +1,11 @@
require 'spec_helper'
-describe DiviningRod::Definitions do
+describe DiviningRod::Mapping do
before :each do
@request = mock("rails_request", :user_agent => 'My iPhone', :format => :html)
- DiviningRod::Definitions.clear_definitions
- DiviningRod::Definitions.define do |map|
+ DiviningRod::Mapping.clear_definitions
+ DiviningRod::Mapping.define do |map|
map.ua /iPhone/, :format => :webkit, :tags => [:iphone, :youtube, :geolocate] do |iphone|
iphone.ua /iPad/, :tags => [:ipad] do |ipad|
ipad.ua /Unicorns/, :tags => [:omg_unicorns]
@@ -17,20 +17,20 @@
end
it "should match a top level user agent" do
- result = DiviningRod::Definitions.evaluate(@request)
+ result = DiviningRod::Mapping.evaluate(@request)
result.should_not be_nil
result.tags.should include(:iphone)
end
it "should match a child definition" do
ipad_request = mock("rails_request", :user_agent => 'My iPhone is really an iPad', :format => :html)
- result = DiviningRod::Definitions.evaluate(ipad_request)
+ result = DiviningRod::Mapping.evaluate(ipad_request)
result.tags.should include(:ipad)
end
it "should match a sub child definition" do
ipad_request = mock("rails_request", :user_agent => 'New iPad - now with Unicorns', :format => :html)
- result = DiviningRod::Definitions.evaluate(ipad_request)
+ result = DiviningRod::Mapping.evaluate(ipad_request)
result.tags.should include(:ipad)
result.tags.should include(:omg_unicorns)
end

0 comments on commit 2056e31

Please sign in to comment.