divining_rod 0.5.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+ gem "shoulda"
3
+ gem "rack"
4
+ gem "mocha"
@@ -0,0 +1,14 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ mocha (0.9.12)
5
+ rack (1.2.2)
6
+ shoulda (2.11.3)
7
+
8
+ PLATFORMS
9
+ ruby
10
+
11
+ DEPENDENCIES
12
+ mocha
13
+ rack
14
+ shoulda
data/LICENSE CHANGED
@@ -1,4 +1,5 @@
1
- Copyright (c) 2009 Mark Percival
1
+ Copyright 2009-2011 Mark Percival
2
+ Portions Copyright 2011 Twitter, Inc.
2
3
 
3
4
  Permission is hereby granted, free of charge, to any person obtaining
4
5
  a copy of this software and associated documentation files (the
data/Rakefile CHANGED
@@ -1,53 +1,11 @@
1
1
  require 'rubygems'
2
2
  require 'rake'
3
3
 
4
- begin
5
- require 'jeweler'
6
- Jeweler::Tasks.new do |gem|
7
- gem.name = "divining_rod"
8
- gem.summary = %Q{A mobile phone web request profiler}
9
- gem.description = %Q{A mobile phone web request profiler using definitions that look like rails routes}
10
- gem.email = "mark@mpercival.com"
11
- gem.homepage = "http://github.com/markpercival/divining_rod"
12
- gem.authors = ["Mark Percival"]
13
- gem.add_development_dependency "thoughtbot-shoulda", ">= 0"
14
- # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
15
- end
16
- Jeweler::GemcutterTasks.new
17
- rescue LoadError
18
- puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
19
- end
20
-
21
4
  require 'rake/testtask'
22
5
  Rake::TestTask.new(:test) do |test|
23
- test.libs << 'lib' << 'test'
6
+ test.libs << 'lib' << 'test' << '.'
24
7
  test.pattern = 'test/**/test_*.rb'
25
8
  test.verbose = true
26
9
  end
27
10
 
28
- begin
29
- require 'rcov/rcovtask'
30
- Rcov::RcovTask.new do |test|
31
- test.libs << 'test'
32
- test.pattern = 'test/**/test_*.rb'
33
- test.verbose = true
34
- end
35
- rescue LoadError
36
- task :rcov do
37
- abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
38
- end
39
- end
40
-
41
- task :test => :check_dependencies
42
-
43
11
  task :default => :test
44
-
45
- require 'rake/rdoctask'
46
- Rake::RDocTask.new do |rdoc|
47
- version = File.exist?('VERSION') ? File.read('VERSION') : ""
48
-
49
- rdoc.rdoc_dir = 'rdoc'
50
- rdoc.title = "divining_rod #{version}"
51
- rdoc.rdoc_files.include('README*')
52
- rdoc.rdoc_files.include('lib/**/*.rb')
53
- end
@@ -1,74 +1,21 @@
1
- # Generated by jeweler
2
- # DO NOT EDIT THIS FILE DIRECTLY
3
- # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
1
  # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "divining_rod/version"
5
4
 
6
5
  Gem::Specification.new do |s|
7
- s.name = %q{divining_rod}
8
- s.version = "0.5.0"
6
+ s.name = "divining_rod"
7
+ s.version = DiviningRod::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Mark Percival"]
10
+ s.email = ["mark@markpercival.us"]
11
+ s.homepage = "http://github.com/mdp/divining_rod"
12
+ s.summary = %q{An opinionated ruby encryption library}
13
+ s.description = %q{Supports OpenSSL compatible AES, HMAC, and RSA encryption}
9
14
 
10
- s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
- s.authors = ["Mark Percival"]
12
- s.date = %q{2010-08-30}
13
- s.description = %q{A mobile phone web request profiler using definitions that look like rails routes}
14
- s.email = %q{mark@mpercival.com}
15
- s.extra_rdoc_files = [
16
- "LICENSE",
17
- "README.markdown"
18
- ]
19
- s.files = [
20
- ".document",
21
- ".gitignore",
22
- "LICENSE",
23
- "README.markdown",
24
- "Rakefile",
25
- "VERSION",
26
- "divining_rod.gemspec",
27
- "example_config.rb",
28
- "lib/divining_rod.rb",
29
- "lib/divining_rod/definition.rb",
30
- "lib/divining_rod/mapper.rb",
31
- "lib/divining_rod/mappings.rb",
32
- "lib/divining_rod/matchers.rb",
33
- "lib/divining_rod/murge.rb",
34
- "lib/divining_rod/profile.rb",
35
- "lib/divining_rod/rack.rb",
36
- "lib/divining_rod/rack/divining_rack.rb",
37
- "lib/divining_rod/utilities.rb",
38
- "spec/definition_spec.rb",
39
- "spec/example_mapping_spec.rb",
40
- "spec/mapper_spec.rb",
41
- "spec/mapping_spec.rb",
42
- "spec/profile_spec.rb",
43
- "spec/rack_spec.rb",
44
- "spec/spec_helper.rb"
45
- ]
46
- s.homepage = %q{http://github.com/markpercival/divining_rod}
47
- s.rdoc_options = ["--charset=UTF-8"]
48
- s.require_paths = ["lib"]
49
- s.rubygems_version = %q{1.3.6}
50
- s.summary = %q{A mobile phone web request profiler}
51
- s.test_files = [
52
- "spec/definition_spec.rb",
53
- "spec/example_mapping_spec.rb",
54
- "spec/mapper_spec.rb",
55
- "spec/mapping_spec.rb",
56
- "spec/profile_spec.rb",
57
- "spec/rack_spec.rb",
58
- "spec/spec_helper.rb"
59
- ]
60
-
61
- if s.respond_to? :specification_version then
62
- current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
63
- s.specification_version = 3
15
+ s.rubyforge_project = "divining_rod"
64
16
 
65
- if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
66
- s.add_development_dependency(%q<thoughtbot-shoulda>, [">= 0"])
67
- else
68
- s.add_dependency(%q<thoughtbot-shoulda>, [">= 0"])
69
- end
70
- else
71
- s.add_dependency(%q<thoughtbot-shoulda>, [">= 0"])
72
- end
17
+ s.files = `git ls-files`.split("\n")
18
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
19
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20
+ s.require_paths = ["lib"]
73
21
  end
74
-
@@ -1,17 +1,17 @@
1
- DiviningRod::Mappings.define do |map|
1
+ DiviningRod::Mappings.define(:format => :html) do |map|
2
2
  # Android based phones
3
3
  map.ua /Android/, :format => :webkit, :name => 'Android', :tags => [:android, :youtube_capable, :google_gears]
4
-
4
+
5
5
  # Apple iPhone OS
6
6
  map.ua /Apple.*Mobile.*Safari/, :format => :webkit, :tags => [:apple, :iphone_os, :youtube_capable] do |iphone|
7
7
  iphone.ua /iPad/, :tags => :ipad, :name => 'iPad'
8
8
  iphone.ua /iPod/, :tags => :ipod, :name => 'iPod Touch'
9
9
  iphone.ua /iPhone/, :tags => [:iphone], :name => 'iPhone'
10
10
  end
11
-
11
+
12
12
  #Blackberry, needs more detail here
13
13
  map.ua /BlackBerry/, :tags => :blackberry, :name => 'BlackBerry'
14
-
14
+
15
15
  # The desktop browsers, we don't set a format on these to let it pass through
16
16
  map.with_options :tags => :desktop do |desktop|
17
17
  desktop.ua /Chrome/, :tags => :chrome, :name => 'Chrome'
@@ -22,10 +22,11 @@ DiviningRod::Mappings.define do |map|
22
22
  msie.ua /MSIE 6/, :version => 6
23
23
  msie.ua /MSIE 7/, :version => 7
24
24
  msie.ua /MSIE 8/, :version => 8
25
+ msie.ua /MSIE 10/, :version => 10, :tags => :html5
25
26
  end
26
27
  end
27
-
28
+
28
29
  # Enable this to forces a default format if unmatched
29
30
  # otherwise it will return the request.format
30
- # map.default :format => :html
31
- end
31
+ map.default :name => "Unknown"
32
+ end
@@ -2,8 +2,7 @@ module DiviningRod; end
2
2
 
3
3
  require 'divining_rod/profile'
4
4
  require 'divining_rod/utilities'
5
- require 'divining_rod/murge'
5
+ require 'divining_rod/mash'
6
6
  require 'divining_rod/definition'
7
7
  require 'divining_rod/mappings'
8
- require 'divining_rod/mapper'
9
8
  require 'divining_rod/matchers'
@@ -1,13 +1,11 @@
1
1
  module DiviningRod
2
2
  class Definition
3
- include Murge
4
3
 
5
4
  attr_accessor :prc, :group, :opts, :parent
6
- attr_writer :children
7
5
 
8
6
  def initialize(opts={}, &blk)
9
7
  @prc = blk
10
- @opts = opts
8
+ @opts = Mash.new(opts)
11
9
  end
12
10
 
13
11
  def evaluate(request)
@@ -26,28 +24,16 @@ module DiviningRod
26
24
  end
27
25
 
28
26
  def tags
29
- opts[:tags] || []
27
+ @tags ||= opts[:tags] || []
30
28
  end
31
-
32
- def opts
33
- if parent
34
- @murged_opts ||= murge(parent.opts, @opts)
35
- else
36
- @opts
37
- end
29
+
30
+ def children
31
+ @children ||= []
38
32
  end
39
-
33
+
40
34
  def format
41
35
  opts[:format]
42
36
  end
43
-
44
- def children
45
- @children ||= []
46
- @children.each do |child|
47
- child.parent ||= self
48
- end
49
- @children
50
- end
51
37
 
52
38
  end
53
39
  end
@@ -4,18 +4,67 @@ module DiviningRod
4
4
  class << self
5
5
 
6
6
  attr_accessor :root_definition
7
-
7
+
8
8
  def define(opts = {})
9
- @root_definition = Definition.new { true }
10
- yield Mapper.new(@root_definition, opts)
11
- @root_definition.freeze
9
+ @root_definition = Definition.new(opts) { true }
10
+ yield Mappings.new(@root_definition)
11
+ @root_definition
12
12
  end
13
-
13
+
14
14
  def evaluate(obj)
15
15
  @root_definition.evaluate(obj)
16
16
  end
17
-
17
+
18
+ end
19
+
20
+ attr_reader :default_opts, :parent
21
+
22
+ def initialize(parent, default_opts = {})
23
+ @parent = parent
24
+ @default_opts = Mash.new(default_opts)
25
+ end
26
+
27
+ def pattern(type, pattern, opts = {})
28
+ definition = Matchers.send(type.to_sym, pattern, merged_opts(opts))
29
+ append_to_parent(definition)
30
+ if block_given?
31
+ yield self.class.new(definition)
32
+ end
33
+ definition
34
+ end
35
+
36
+ def default(opts = {})
37
+ definition = Definition.new(merged_opts(opts)) { true }
38
+ append_to_parent(definition)
39
+ definition
40
+ end
41
+
42
+ def with_options(opts)
43
+ yield self.class.new(parent, opts)
44
+ end
45
+
46
+ def method_missing(meth, *args, &blk)
47
+ # Lets us use map.ua instead of map.pattern :ua
48
+ if Matchers.respond_to?(meth.to_sym)
49
+ self.pattern(meth, args[0], args[1,], &blk)
50
+ else
51
+ super
52
+ end
18
53
  end
19
54
 
55
+ private
56
+
57
+ def merged_opts(opts)
58
+ opts = parent.opts.merge(opts)
59
+ opts = default_opts.merge(opts)
60
+ opts
61
+ end
62
+
63
+ def append_to_parent(definition)
64
+ parent.children << definition
65
+ definition
66
+ end
67
+
68
+
20
69
  end
21
70
  end
@@ -0,0 +1,17 @@
1
+ module DiviningRod
2
+ class Mash < Hash
3
+
4
+ def initialize(hsh = {})
5
+ self.replace(hsh || {})
6
+ end
7
+
8
+ def merge(opts = {})
9
+ if self[:tags] || opts[:tags]
10
+ tags = Array(self[:tags]) | Array(opts[:tags])
11
+ opts[:tags] = tags
12
+ end
13
+ super(opts)
14
+ end
15
+
16
+ end
17
+ end
@@ -20,7 +20,7 @@ module DiviningRod
20
20
 
21
21
  def ua_prof(pattern, opts ={})
22
22
  Definition.new(opts) {|request|
23
- if pattern.match(request.env['X_WAP_PROFILE'])
23
+ if pattern.match(request.env['HTTP_X_WAP_PROFILE'])
24
24
  true
25
25
  end
26
26
  }
@@ -1,16 +1,17 @@
1
1
  module DiviningRod
2
2
  module Murge
3
-
3
+
4
4
  # This is tacky, but I'm preserving tags in the option hash
5
5
  # and only want to have to write this once
6
6
  #
7
7
  def murge(old_opts, new_opts)
8
8
  old_opts = old_opts || {}
9
+ new_opts = new_opts || {}
9
10
  tags = Array(old_opts[:tags]) | Array(new_opts[:tags])
10
11
  opts = old_opts.merge(new_opts)
11
12
  opts[:tags] = tags
12
13
  opts
13
14
  end
14
-
15
+
15
16
  end
16
- end
17
+ end
@@ -1,10 +1,10 @@
1
1
  module DiviningRod
2
- class Profile
2
+ module Profiler
3
3
 
4
4
  def initialize(request)
5
- @request = request.clone #Lets not mess with the real one
5
+ @request = request
6
6
  end
7
-
7
+
8
8
  def match
9
9
  @match ||= DiviningRod::Mappings.evaluate(@request)
10
10
  end
@@ -21,14 +21,18 @@ module DiviningRod
21
21
  match != DiviningRod::Mappings.root_definition
22
22
  end
23
23
 
24
+ def tagged?(tag)
25
+ if match
26
+ Array(match.tags).include?(tag.to_sym)
27
+ else
28
+ false
29
+ end
30
+ end
31
+
24
32
  def method_missing(meth)
25
33
  if meth.to_s.match(/(.+)\?$/)
26
34
  tag = $1
27
- if match
28
- match.tags.include?(tag.to_s) || match.tags.include?(tag.to_sym) || match.tags == tag
29
- else
30
- false
31
- end
35
+ tagged?(tag)
32
36
  elsif match.opts.include?(meth.to_sym)
33
37
  match.opts[meth]
34
38
  else
@@ -37,4 +41,9 @@ module DiviningRod
37
41
  end
38
42
 
39
43
  end
44
+
45
+ class Profile
46
+ include Profiler
47
+ end
40
48
  end
49
+
@@ -1,6 +1,6 @@
1
1
  module DiviningRod
2
2
  class Utilities
3
-
3
+
4
4
  def self.parse_subdomain(request)
5
5
  env = request.env
6
6
  if forwarded = env["HTTP_X_FORWARDED_HOST"]
@@ -14,6 +14,6 @@ module DiviningRod
14
14
  []
15
15
  end
16
16
  end
17
-
17
+
18
18
  end
19
- end
19
+ end
@@ -0,0 +1,3 @@
1
+ module DiviningRod
2
+ VERSION = "0.6.0"
3
+ end
@@ -0,0 +1,41 @@
1
+ require 'test_helper'
2
+
3
+ class DefinitionTest < Test::Unit::TestCase
4
+ context DiviningRod::Definition do
5
+
6
+ should "work with no children not match" do
7
+ definition = DiviningRod::Definition.new(:format => :iphone, :tags => [:overpriced, :easy_to_break]) { |request|
8
+ request == 'iphone'
9
+ }
10
+ assert_equal definition.evaluate('foo'), nil
11
+ end
12
+
13
+ should "work with no children and match" do
14
+ definition = DiviningRod::Definition.new(:format => :iphone, :tags => [:overpriced, :easy_to_break]) { |request|
15
+ request == 'iphone'
16
+ }
17
+ assert_equal definition, definition.evaluate('iphone')
18
+ end
19
+
20
+ context "with children" do
21
+
22
+ should "work with children that match" do
23
+ definition = DiviningRod::Definition.new(:format => :iphone, :tags => [:overpriced, :easy_to_break]) { |request|
24
+ request.match(/iphone/)
25
+ }
26
+ definition.children << DiviningRod::Definition.new(:format => :ipod, :tags => [:small, :lacks_a_camera]) { |request|
27
+ request.match(/ipod/)
28
+ }
29
+ definition.children << DiviningRod::Definition.new(:format => :ipad, :tags => [:big, :lacks_a_camera]) { |request|
30
+ request.match(/ipad/)
31
+ }
32
+ result = definition.evaluate('iphone ipad')
33
+ assert_equal :ipad, result.format
34
+ assert result.tags.include?(:big)
35
+ end
36
+
37
+
38
+ end
39
+
40
+ end
41
+ end
@@ -0,0 +1,71 @@
1
+ require 'test_helper'
2
+ require 'example_config'
3
+
4
+ def profile_ua(ua)
5
+ DiviningRod::Profile.new(request_mock(:ua => ua))
6
+ end
7
+
8
+
9
+ class ExampleConfigTest < Test::Unit::TestCase
10
+ context 'the example config' do
11
+
12
+ context "recognizing Apple devices" do
13
+
14
+ should "recognize an iPad" do
15
+ profile = profile_ua("Mozilla/5.0(iPad; U; CPU iPhone OS 3_2 like Mac OS X; en-us) AppleWebKit/531.21.10 (KHTML, like Gecko) Version/4.0.4 Mobile/7B314 Safari/531.21.10")
16
+ assert profile.ipad?
17
+ end
18
+
19
+ should "recognize an iPod touch 2.2.1" do
20
+ profile = profile_ua("Mozilla/5.0 (iPod; U; CPU iPhone OS 2_2_1 like Mac OS X; en-us) AppleWebKit/525.18.1 (KHTML, like Gecko) Version/3.1.1 Mobile/5H11 Safari/525.20")
21
+ assert profile.ipod?
22
+ end
23
+
24
+ should "recognize an iPod touch 3.1.2" do
25
+ profile = profile_ua("Mozilla/5.0 (iPod; U; CPU iPhone OS 3_1_2 like Mac OS X; en-us) AppleWebKit/528.18 (KHTML, like Gecko) Version/4.0 Mobile/7D11 Safari/528.16")
26
+ assert profile.ipod?
27
+ end
28
+
29
+ should "recognize an iPhone 3.1.2" do
30
+ profile = profile_ua("Mozilla/5.0 (iPhone; U; CPU iPhone OS 3_1_2 like Mac OS X; en-us) AppleWebKit/528.18 (KHTML, like Gecko) Version/4.0 Mobile/7D11 Safari/528.16")
31
+ assert profile.iphone?
32
+ end
33
+
34
+ should "recognize an iPhone 2.1.2" do
35
+ profile = profile_ua("Mozilla/5.0 (iPhone; U; CPU iPhone OS 2_2_1 like Mac OS X; en-us) AppleWebKit/525.18.1 (KHTML, like Gecko) Version/3.1.1 Mobile/5H11 Safari/525.20")
36
+ assert profile.iphone?
37
+ end
38
+
39
+ end
40
+
41
+ context "recognizing Android devices" do
42
+
43
+ should "recognize a basic android" do
44
+ profile = profile_ua("Mozilla/5.0 (Linux; U; Android 1.0; en-us; dream) AppleWebKit/525.10+ (KHTML, like Gecko) Version/3.0.4 Mobile Safari/523.12.2")
45
+ assert profile.android?
46
+ end
47
+
48
+ end
49
+
50
+ context "recognizing desktop browsers" do
51
+
52
+ should "recognize Internet Explorer 10" do
53
+ profile = profile_ua("Mozilla/4.0 (compatible; MSIE 10.0; Windows NT 7.0)")
54
+ assert profile.desktop?
55
+ assert_equal profile.version, 10
56
+ assert profile.html5?
57
+ end
58
+
59
+ end
60
+
61
+ context "with root definition options" do
62
+ should "carry through" do
63
+ profile = profile_ua("Unknown phone")
64
+ assert_equal "Unknown", profile.name
65
+ assert_equal :html, profile.format
66
+ end
67
+ end
68
+
69
+
70
+ end
71
+ end
@@ -1,12 +1,17 @@
1
1
  require 'rubygems'
2
- require 'spec'
3
2
  require 'rack'
4
- require File.expand_path('../../lib/divining_rod', __FILE__)
3
+ require 'test/unit'
4
+ require 'shoulda'
5
+ require 'mocha'
6
+ $:.unshift File.join(File.dirname(__FILE__), '..', 'lib')
7
+ require 'divining_rod'
8
+
9
+ include Mocha::API
5
10
 
6
11
  def request_mock(opts)
7
12
  opts = {
8
13
  :host => 'example.com'
9
14
  }.merge(opts)
10
15
  env = {'HTTP_USER_AGENT' => opts[:ua], 'SERVER_NAME' => opts[:host], 'X_WAP_PROFILE' => opts[:wap_profile]}
11
- mock('RailsRequest', :env => env, :format => opts[:format])
12
- end
16
+ stub(:env => env, :format => opts[:format])
17
+ end
@@ -0,0 +1,25 @@
1
+ require 'test_helper'
2
+
3
+ module DiviningRod
4
+ class MashTest < Test::Unit::TestCase
5
+ context 'Mash is like Hash' do
6
+ should "work that way" do
7
+ mash = Mash.new({:a_key => "and a value"})
8
+ and_mash = Mash.new({:my_key => "and my value"})
9
+ assert_equal ({:a_key => "and a value", :my_key => "and my value"}), mash.merge(and_mash)
10
+ end
11
+
12
+ should "also mash tags" do
13
+ mash = Mash.new({:tags => :foo})
14
+ and_mash = Mash.new({:tags => [:bar, :baz]})
15
+ assert_equal mash.merge(and_mash), {:tags => [:foo, :bar, :baz]}
16
+ end
17
+
18
+ should "accept nil happily" do
19
+ mash = Mash.new(nil)
20
+ assert_equal({}, mash)
21
+ end
22
+ end
23
+ end
24
+ end
25
+
metadata CHANGED
@@ -1,12 +1,8 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: divining_rod
3
3
  version: !ruby/object:Gem::Version
4
- prerelease: false
5
- segments:
6
- - 0
7
- - 5
8
- - 0
9
- version: 0.5.0
4
+ prerelease:
5
+ version: 0.6.0
10
6
  platform: ruby
11
7
  authors:
12
8
  - Mark Percival
@@ -14,91 +10,71 @@ autorequire:
14
10
  bindir: bin
15
11
  cert_chain: []
16
12
 
17
- date: 2010-08-30 00:00:00 -07:00
13
+ date: 2011-04-25 00:00:00 +00:00
18
14
  default_executable:
19
- dependencies:
20
- - !ruby/object:Gem::Dependency
21
- name: thoughtbot-shoulda
22
- prerelease: false
23
- requirement: &id001 !ruby/object:Gem::Requirement
24
- requirements:
25
- - - ">="
26
- - !ruby/object:Gem::Version
27
- segments:
28
- - 0
29
- version: "0"
30
- type: :development
31
- version_requirements: *id001
32
- description: A mobile phone web request profiler using definitions that look like rails routes
33
- email: mark@mpercival.com
15
+ dependencies: []
16
+
17
+ description: Supports OpenSSL compatible AES, HMAC, and RSA encryption
18
+ email:
19
+ - mark@markpercival.us
34
20
  executables: []
35
21
 
36
22
  extensions: []
37
23
 
38
- extra_rdoc_files:
39
- - LICENSE
40
- - README.markdown
24
+ extra_rdoc_files: []
25
+
41
26
  files:
42
27
  - .document
43
28
  - .gitignore
29
+ - Gemfile
30
+ - Gemfile.lock
44
31
  - LICENSE
45
32
  - README.markdown
46
33
  - Rakefile
47
- - VERSION
48
34
  - divining_rod.gemspec
49
35
  - example_config.rb
50
36
  - lib/divining_rod.rb
51
37
  - lib/divining_rod/definition.rb
52
- - lib/divining_rod/mapper.rb
53
38
  - lib/divining_rod/mappings.rb
39
+ - lib/divining_rod/mash.rb
54
40
  - lib/divining_rod/matchers.rb
55
41
  - lib/divining_rod/murge.rb
56
42
  - lib/divining_rod/profile.rb
57
43
  - lib/divining_rod/rack.rb
58
44
  - lib/divining_rod/rack/divining_rack.rb
59
45
  - lib/divining_rod/utilities.rb
60
- - spec/definition_spec.rb
61
- - spec/example_mapping_spec.rb
62
- - spec/mapper_spec.rb
63
- - spec/mapping_spec.rb
64
- - spec/profile_spec.rb
65
- - spec/rack_spec.rb
66
- - spec/spec_helper.rb
46
+ - lib/divining_rod/version.rb
47
+ - test/test_definition.rb
48
+ - test/test_example_config.rb
49
+ - test/test_helper.rb
50
+ - test/test_mash.rb
67
51
  has_rdoc: true
68
- homepage: http://github.com/markpercival/divining_rod
52
+ homepage: http://github.com/mdp/divining_rod
69
53
  licenses: []
70
54
 
71
55
  post_install_message:
72
- rdoc_options:
73
- - --charset=UTF-8
56
+ rdoc_options: []
57
+
74
58
  require_paths:
75
59
  - lib
76
60
  required_ruby_version: !ruby/object:Gem::Requirement
61
+ none: false
77
62
  requirements:
78
63
  - - ">="
79
64
  - !ruby/object:Gem::Version
80
- segments:
81
- - 0
82
65
  version: "0"
83
66
  required_rubygems_version: !ruby/object:Gem::Requirement
67
+ none: false
84
68
  requirements:
85
69
  - - ">="
86
70
  - !ruby/object:Gem::Version
87
- segments:
88
- - 0
89
71
  version: "0"
90
72
  requirements: []
91
73
 
92
- rubyforge_project:
93
- rubygems_version: 1.3.6
74
+ rubyforge_project: divining_rod
75
+ rubygems_version: 1.6.2
94
76
  signing_key:
95
77
  specification_version: 3
96
- summary: A mobile phone web request profiler
97
- test_files:
98
- - spec/definition_spec.rb
99
- - spec/example_mapping_spec.rb
100
- - spec/mapper_spec.rb
101
- - spec/mapping_spec.rb
102
- - spec/profile_spec.rb
103
- - spec/rack_spec.rb
104
- - spec/spec_helper.rb
78
+ summary: An opinionated ruby encryption library
79
+ test_files: []
80
+
data/VERSION DELETED
@@ -1 +0,0 @@
1
- 0.5.0
@@ -1,55 +0,0 @@
1
- module DiviningRod
2
- class Mapper
3
- include Murge
4
-
5
- def initialize(parent, default_opts = {})
6
- @parent = parent
7
- @default_opts = default_opts
8
- end
9
-
10
- def pattern(type, pattern, opts = {})
11
- opts = murge(default_options, opts)
12
- definition = Matchers.send(type.to_sym, pattern, opts)
13
- append_to_parent(definition)
14
- if block_given?
15
- yield self.class.new(definition)
16
- end
17
- definition
18
- end
19
-
20
- def with_options(opts)
21
- yield self.class.new(@parent, opts)
22
- end
23
-
24
- def default(opts = {})
25
- definition = Definition.new(opts) { true }
26
- append_to_parent(definition)
27
- definition
28
- end
29
-
30
- def method_missing(meth, *args, &blk)
31
- # Lets us use map.ua instead of map.pattern :ua
32
- if Matchers.respond_to?(meth.to_sym)
33
- self.pattern(meth, args[0], args[1,], &blk)
34
- else
35
- super
36
- end
37
- end
38
-
39
- private
40
-
41
- def default_options
42
- @default_opts || {}
43
- end
44
-
45
- def append_to_parent(definition)
46
- if @parent
47
- @parent.children << definition
48
- end
49
- definition
50
- end
51
-
52
- end
53
-
54
-
55
- end
@@ -1,40 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe DiviningRod::Definition do
4
-
5
- it "should work with no children not match" do
6
- definition = DiviningRod::Definition.new(:format => :iphone, :tags => [:overpriced, :easy_to_break]) { |request|
7
- request == 'iphone'
8
- }
9
- definition.evaluate('foo').should be_nil
10
- end
11
-
12
- it "should work with no children and match" do
13
- definition = DiviningRod::Definition.new(:format => :iphone, :tags => [:overpriced, :easy_to_break]) { |request|
14
- request == 'iphone'
15
- }
16
- definition.evaluate('iphone').should eql(definition)
17
- end
18
-
19
- describe "with children" do
20
-
21
- it "should work with children that match" do
22
- definition = DiviningRod::Definition.new(:format => :iphone, :tags => [:overpriced, :easy_to_break]) { |request|
23
- request.match(/iphone/)
24
- }
25
- definition.children << DiviningRod::Definition.new(:format => :ipod, :tags => [:big, :lacks_a_camera]) { |request|
26
- request.match(/ipod/)
27
- }
28
- definition.children << DiviningRod::Definition.new(:format => :ipad, :tags => [:big, :lacks_a_camera]) { |request|
29
- request.match(/ipad/)
30
- }
31
- result = definition.evaluate('iphone ipad')
32
- result.should_not eql(definition)
33
- result.format.should eql(:ipad)
34
- result.tags.should include(:overpriced)
35
- end
36
-
37
-
38
- end
39
-
40
- end
@@ -1,60 +0,0 @@
1
- require 'spec/spec_helper'
2
- require 'example_config'
3
-
4
- def profile_ua(ua)
5
- DiviningRod::Profile.new(request_mock(:ua => ua))
6
- end
7
-
8
-
9
- describe 'the example config' do
10
-
11
- describe "recognizing Apple devices" do
12
-
13
- it "should recognize an iPad" do
14
- profile = profile_ua("Mozilla/5.0(iPad; U; CPU iPhone OS 3_2 like Mac OS X; en-us) AppleWebKit/531.21.10 (KHTML, like Gecko) Version/4.0.4 Mobile/7B314 Safari/531.21.10")
15
- profile.ipad?.should be_true
16
- end
17
-
18
- it "should recognize an iPod touch 2.2.1" do
19
- profile = profile_ua("Mozilla/5.0 (iPod; U; CPU iPhone OS 2_2_1 like Mac OS X; en-us) AppleWebKit/525.18.1 (KHTML, like Gecko) Version/3.1.1 Mobile/5H11 Safari/525.20")
20
- profile.ipod?.should be_true
21
- end
22
-
23
- it "should recognize an iPod touch 3.1.2" do
24
- profile = profile_ua("Mozilla/5.0 (iPod; U; CPU iPhone OS 3_1_2 like Mac OS X; en-us) AppleWebKit/528.18 (KHTML, like Gecko) Version/4.0 Mobile/7D11 Safari/528.16")
25
- profile.ipod?.should be_true
26
- end
27
-
28
- it "should recognize an iPhone 3.1.2" do
29
- profile = profile_ua("Mozilla/5.0 (iPhone; U; CPU iPhone OS 3_1_2 like Mac OS X; en-us) AppleWebKit/528.18 (KHTML, like Gecko) Version/4.0 Mobile/7D11 Safari/528.16")
30
- profile.iphone?.should be_true
31
- end
32
-
33
- it "should recognize an iPhone 2.1.2" do
34
- profile = profile_ua("Mozilla/5.0 (iPhone; U; CPU iPhone OS 2_2_1 like Mac OS X; en-us) AppleWebKit/525.18.1 (KHTML, like Gecko) Version/3.1.1 Mobile/5H11 Safari/525.20")
35
- profile.iphone?.should be_true
36
- end
37
-
38
- end
39
-
40
- describe "recognizing Android devices" do
41
-
42
- it "should recognize a basic android" do
43
- profile = profile_ua("Mozilla/5.0 (Linux; U; Android 1.0; en-us; dream) AppleWebKit/525.10+ (KHTML, like Gecko) Version/3.0.4 Mobile Safari/523.12.2")
44
- profile.android?.should be_true
45
- end
46
-
47
- end
48
-
49
- describe "recognizing desktop browsers" do
50
-
51
- it "should recognize Internet Explorer 7" do
52
- profile = profile_ua("Mozilla/4.0 (compatible; MSIE 7.0b; Windows NT 6.0)")
53
- profile.desktop?.should be_true
54
- profile.version.should eql(7)
55
- end
56
-
57
- end
58
-
59
-
60
- end
@@ -1,21 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe DiviningRod::Mapper do
4
-
5
- before :each do
6
- @root_definition = DiviningRod::Definition.new { true }
7
- mapper = DiviningRod::Mapper.new(@root_definition, {:tags => [:fuck], :foo => true})
8
- mapper.ua /Safari/, :tags => [:baz] do |map|
9
- map.with_options :tags => :awsome do |awesome|
10
- awesome.ua /Apple/, :tags => [:foo]
11
- end
12
- end
13
- end
14
-
15
- it "should map a definition" do
16
- request = request_mock(:ua => 'Apple Mobile Safari', :format => :html)
17
- result = @root_definition.evaluate(request)
18
- result.tags.should include(:fuck)
19
- end
20
-
21
- end
@@ -1,78 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe DiviningRod::Mappings do
4
-
5
- before :each do
6
- DiviningRod::Mappings.define(:tags => :mobile) do |map|
7
- map.ua /Apple/, :format => :webkit, :tags => [:apple] do |apple|
8
-
9
- # iPod, iPhone, and iPad
10
- apple.with_options :tags => [:youtube, :geolocate, :iphone] do |advanced|
11
- advanced.ua /iPad/, :tags => :ipad do |ipad|
12
- ipad.ua /OS\s4/, :tags => :version4
13
- ipad.ua /OS\s3/, :tags => :version3 do |v3|
14
- v3.ua /Unicorns/, :tags => :omg_unicorns do |unicorns|
15
- unicorns.ua /eat kittens/, :tags => [:omg_they_eat_kittens]
16
- end
17
- end
18
- ipad.ua /OS\s2/, :tags => :version2
19
- end
20
- advanced.ua /iPod/, :tags => [:ipod, :ipod_touch]
21
- end
22
-
23
- # Also an apple but actualy a newton
24
- apple.ua /Newton/, :tags => [:apple] do |newton|
25
- newton.ua /OS 8/, :tags => :os8
26
- end
27
- end
28
- end
29
- end
30
-
31
- it "should match a top level user agent" do
32
- request = request_mock(:ua => 'Apple Mobile Safari', :format => :html)
33
- result = DiviningRod::Mappings.root_definition.evaluate(request)
34
- result.should_not be_nil
35
- result.tags.should include(:apple)
36
- result.tags.should_not include(:ipad)
37
- result.tags.should include(:mobile)
38
- end
39
-
40
- it "should match a child definition" do
41
- ipad_request = request_mock(:ua => 'Apple iPad', :format => :html)
42
- result = DiviningRod::Mappings.evaluate(ipad_request)
43
- result.tags.should include(:ipad)
44
- end
45
-
46
- it "should match a sub child definition" do
47
- ipad_request = request_mock(:ua => 'Apple iPad - now powered by Unicorns - OS 3.3', :format => :html)
48
- result = DiviningRod::Mappings.evaluate(ipad_request)
49
- result.tags.should include(:ipad)
50
- result.tags.should include(:omg_unicorns)
51
- result.tags.should include(:version3)
52
- end
53
-
54
- it "should match a really really deep child definition" do
55
- ipad_request = request_mock(:ua => 'Apple iPad - now powered by Unicorns who eat kittens - OS 3.3', :format => :html)
56
- result = DiviningRod::Mappings.evaluate(ipad_request)
57
- result.tags.should include(:ipad, :youtube)
58
- result.tags.should include(:omg_unicorns)
59
- result.tags.should include(:omg_they_eat_kittens)
60
- result.tags.should include(:version3)
61
- end
62
-
63
- it "should match a in order defined" do
64
- ipad_request = request_mock(:ua => 'Apple iPad - now powered by Unicorns who eat kittens - OS 2', :format => :html)
65
- result = DiviningRod::Mappings.evaluate(ipad_request)
66
- result.tags.should include(:ipad, :youtube)
67
- result.tags.should_not include(:omg_they_eat_kittens, :omg_unicorns)
68
- result.tags.should include(:version2)
69
- end
70
-
71
- it "should match a in order defined" do
72
- ipad_request = request_mock(:ua => 'Apple Newton - OS 8', :format => :html)
73
- result = DiviningRod::Mappings.evaluate(ipad_request)
74
- result.tags.should_not include(:omg_they_eat_kittens, :omg_unicorns)
75
- result.tags.should include(:os8)
76
- end
77
-
78
- end
@@ -1,118 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe DiviningRod do
4
-
5
- before :each do
6
- @request = request_mock(:ua => 'My iPhone which is actually an iPad')
7
-
8
- DiviningRod::Mappings.define do |map|
9
- map.ua /iPhone/, :format => :webkit, :tags => [:iphone, :youtube, :geolocate] do |iphone|
10
- iphone.ua /iPad/, :tags => [:ipad]
11
- end
12
- end
13
- end
14
-
15
- it "should recognize an iPhone" do
16
- profile = DiviningRod::Profile.new(@request)
17
- profile.format.should eql(:webkit)
18
- end
19
-
20
- it "should know if it belongs to a category tag" do
21
- profile = DiviningRod::Profile.new(@request)
22
- profile.ipad?.should be_true
23
- end
24
-
25
- it "should know if it does not belongs to a category" do
26
- profile = DiviningRod::Profile.new(@request)
27
- profile.wap?.should be_false
28
- end
29
-
30
- describe "without a default route" do
31
-
32
- before :each do
33
- @request = request_mock(:ua => 'My Foo Fone', :format => :html)
34
-
35
- DiviningRod::Mappings.define do |map|
36
- map.ua /iPhone/, :format => :webkit, :tags => [:iphone, :youtube, :geolocate]
37
- end
38
- end
39
-
40
- it "should use the default group for unknown phones" do
41
- profile = DiviningRod::Profile.new(@request)
42
- profile.wap?.should be_false
43
- profile.format.should eql(:html)
44
- end
45
-
46
- end
47
-
48
-
49
- describe "with a default route" do
50
-
51
- before :each do
52
- @request = request_mock(:ua => 'My Foo Fone')
53
-
54
- DiviningRod::Mappings.define do |map|
55
- map.ua /iPhone/, :format => :webkit, :tags => [:iphone, :youtube, :geolocate]
56
- map.default :format => :html
57
- end
58
- end
59
-
60
- it "should use the default group for unknown phones" do
61
- profile = DiviningRod::Profile.new(@request)
62
- profile.wap?.should be_false
63
- profile.format.should eql(:html)
64
- end
65
-
66
- end
67
-
68
- describe "without a default definition" do
69
-
70
- before :each do
71
- @request = request_mock(:ua => 'Foo Fone', :format => :html)
72
-
73
- DiviningRod::Mappings.define do |map|
74
- map.ua /iPhone/, :format => :webkit, :tags => [:iphone, :youtube, :geolocate]
75
- end
76
- end
77
-
78
- it "should not find a match" do
79
- DiviningRod::Profile.new(@request).recognized?.should be_false
80
- end
81
-
82
- end
83
-
84
- describe "matching a subdomain" do
85
-
86
- before :each do
87
- @request = request_mock(:ua => 'Foo Fone', :host => 'wap.example.com')
88
-
89
- DiviningRod::Mappings.define do |map|
90
- map.subdomain /wap/, :format => :wap, :tags => [:shitty]
91
- end
92
- end
93
-
94
- it "should not find a match" do
95
- DiviningRod::Profile.new(@request).recognized?.should be_true
96
- profile = DiviningRod::Profile.new(@request)
97
- profile.wap?
98
- end
99
-
100
- end
101
-
102
- describe "matching the weird requests(no user_agent passed)" do
103
-
104
- before :each do
105
- @request = request_mock(:ua => nil, :subdomains => [])
106
-
107
- DiviningRod::Mappings.define do |map|
108
- map.ua /iPhone/, :format => :wap, :tags => [:shitty]
109
- end
110
- end
111
-
112
- it "should not find a match" do
113
- DiviningRod::Profile.new(@request).recognized?.should be_false
114
- end
115
-
116
- end
117
-
118
- end
@@ -1,27 +0,0 @@
1
- require 'spec_helper'
2
- require "divining_rod/rack"
3
- require "rack/test"
4
-
5
- describe DiviningRod do
6
- include Rack::Test::Methods
7
-
8
- def app
9
- DiviningRod::Rack.new(app = lambda { |env| [200, {'Content-Type' => 'text/plain'}, ['foo']] })
10
- end
11
-
12
- before :each do
13
- DiviningRod::Mappings.define do |map|
14
- map.ua /iPhone/, :format => :webkit, :tags => [:iphone, :youtube, :geolocate] do |iphone|
15
- iphone.ua /iPad/, :tags => [:ipad]
16
- end
17
- end
18
- end
19
-
20
- it "should profile an incoming request" do
21
- header 'User-Agent', "iPhone Safari"
22
- get "/"
23
- last_request.env['divining_rod.profile'].should be_an_instance_of(DiviningRod::Profile)
24
- last_request.env['divining_rod.profile'].iphone?.should be_true
25
- end
26
-
27
- end