divining_rod 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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