platform_agent 1.0.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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 37665e6536aba2833b93dc0f111046ecd115a57c49e914301e2cf81921e7ae18
4
+ data.tar.gz: a3601b3632b89ea98e8b3e5a1f8ea10b325de7c566811aa4b93a6ae5e59dbf2f
5
+ SHA512:
6
+ metadata.gz: 62f9130310cc151af360e5720f53eb5319c156742e6b61c4ee1dc12c80e05c9e4e33c254cb07f48f72ffd69f8dbc763ee4798d98cb726f5c8b35c84300b4867d
7
+ data.tar.gz: 979a7288c0d77cdc4af9b121b32a188b3b0cb737bc498f6c4f20b1d5fb7fabfd137e34687a2077ed3d85318d0ee32ae0e26478b13d441b6279bd6eac9c145d03
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ gem 'rake'
6
+ gem 'byebug'
data/Gemfile.lock ADDED
@@ -0,0 +1,40 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ platform_agent (1.0.0)
5
+ activesupport (>= 5.2.0)
6
+ useragent (~> 0.16.3)
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ activemodel (5.2.1)
12
+ activesupport (= 5.2.1)
13
+ activesupport (5.2.1)
14
+ concurrent-ruby (~> 1.0, >= 1.0.2)
15
+ i18n (>= 0.7, < 2)
16
+ minitest (~> 5.1)
17
+ tzinfo (~> 1.1)
18
+ byebug (10.0.2)
19
+ concurrent-ruby (1.0.5)
20
+ i18n (1.1.0)
21
+ concurrent-ruby (~> 1.0)
22
+ minitest (5.11.3)
23
+ rake (12.3.1)
24
+ thread_safe (0.3.6)
25
+ tzinfo (1.2.5)
26
+ thread_safe (~> 0.1)
27
+ useragent (0.16.10)
28
+
29
+ PLATFORMS
30
+ ruby
31
+
32
+ DEPENDENCIES
33
+ activemodel (>= 5.2.0)
34
+ bundler (~> 1.15)
35
+ byebug
36
+ platform_agent!
37
+ rake
38
+
39
+ BUNDLED WITH
40
+ 1.17.1
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2019 Basecamp
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,54 @@
1
+ # Platform Agent
2
+
3
+ Parse user agent to discern the common platforms that UIs are tailored for. Expected that you'll inherit from this to specify native apps.
4
+
5
+ ## Examples
6
+
7
+ ```ruby
8
+ class ApplicationPlatform < PlatformAgent
9
+ def ios_app?
10
+ match? /BC3 iOS/
11
+ end
12
+
13
+ def android_app?
14
+ match? /BC3 Android/
15
+ end
16
+
17
+ def mac_app?
18
+ match?(/Electron/) && match?(/basecamp3/) && match?(/Macintosh/)
19
+ end
20
+
21
+ def windows_app?
22
+ match?(/Electron/) && match?(/basecamp3/) && match?(/Windows/)
23
+ end
24
+ end
25
+
26
+ module SetPlatform
27
+ extend ActiveSupport::Concern
28
+
29
+ included do
30
+ helper_method :platform
31
+ end
32
+
33
+ private
34
+ def platform
35
+ @platform ||= ApplicationPlatform.new(request.user_agent)
36
+ end
37
+ end
38
+
39
+ class ApplicationController < ActionController::Base
40
+ include SetPlatform
41
+ end
42
+
43
+ <% if platform.phone? %>
44
+ Do phone specific stuff!
45
+ <% end %>
46
+ ```
47
+
48
+ ## Maintenance Expectations
49
+
50
+ This library is an extraction from Basecamp that's been sufficient in almost unaltered form for years. While contributions are always welcome, do not expect a lot of feature evolution beyond the basics.
51
+
52
+ ## License
53
+
54
+ Platform Agent is released under the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,11 @@
1
+ require "bundler/setup"
2
+ require "bundler/gem_tasks"
3
+ require "rake/testtask"
4
+
5
+ Rake::TestTask.new do |test|
6
+ test.libs << "test"
7
+ test.test_files = FileList["test/*_test.rb"]
8
+ test.warning = false
9
+ end
10
+
11
+ task default: :test
@@ -0,0 +1,119 @@
1
+ require "user_agent"
2
+
3
+ class PlatformAgent
4
+ def initialize(user_agent_string)
5
+ self.user_agent_string = user_agent_string
6
+ end
7
+
8
+ delegate :browser, :version, :product, to: :user_agent
9
+
10
+ def desktop?
11
+ !mobile?
12
+ end
13
+
14
+ def mobile?
15
+ phone? || tablet? || mobile_app?
16
+ end
17
+
18
+ def phone?
19
+ iphone? || android? || other_phones?
20
+ end
21
+
22
+ def iphone?
23
+ match? /iPhone/
24
+ end
25
+
26
+ def android_phone?
27
+ !android_app? && android?
28
+ end
29
+
30
+ def other_phones?
31
+ match? /(iPod|Windows Phone|BlackBerry|BB10.*Mobile|Mobile.*Firefox)/
32
+ end
33
+
34
+ def tablet?
35
+ ipad? || match?(/(Kindle|Silk)/)
36
+ end
37
+
38
+ def ipad?
39
+ match? /iPad/
40
+ end
41
+
42
+ def android?
43
+ match? /Android/
44
+ end
45
+
46
+ def native_app?
47
+ mobile_app? || desktop_app?
48
+ end
49
+
50
+ def mobile_app?
51
+ ios_app? || android_app?
52
+ end
53
+
54
+ def iphone_app?
55
+ iphone? && ios_app?
56
+ end
57
+
58
+ def ipad_app?
59
+ ipad? && ios_app?
60
+ end
61
+
62
+ # Must overwrite with app-specific match
63
+ def ios_app?
64
+ false
65
+ end
66
+
67
+ # Must overwrite with app-specific match
68
+ def android_app?
69
+ false
70
+ end
71
+
72
+ def desktop_app?
73
+ mac_app? || windows_app?
74
+ end
75
+
76
+ # Must overwrite with app-specific match
77
+ def mac_app?
78
+ false
79
+ end
80
+
81
+ # Must overwrite with app-specific match
82
+ def windows_app?
83
+ false
84
+ end
85
+
86
+ def missing?
87
+ user_agent_string.nil?
88
+ end
89
+
90
+ def app_version
91
+ # App user-agent string is parsed into two separate UserAgent instances, it's the last one that contains the right version
92
+ user_agent.last.version if native?
93
+ end
94
+
95
+ def to_s
96
+ case
97
+ when ipad_app? then "iPad app"
98
+ when iphone_app? then "iPhone app"
99
+ when android_app? then "Android app"
100
+ when mac_app? then "Mac app"
101
+ when windows_app? then "Windows app"
102
+ when tablet? then "tablet"
103
+ when phone? then "phone"
104
+ when missing? then "missing"
105
+ else "web"
106
+ end
107
+ end
108
+
109
+ private
110
+ attr_accessor :user_agent_string
111
+
112
+ def match?(pattern)
113
+ true if user_agent_string.to_s.match(pattern)
114
+ end
115
+
116
+ def user_agent
117
+ @user_agent ||= UserAgent.parse(user_agent_string)
118
+ end
119
+ end
@@ -0,0 +1,20 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = 'platform_agent'
3
+ s.version = '1.0.0'
4
+ s.authors = 'David Heinemeier Hansson'
5
+ s.email = 'david@basecamp.com'
6
+ s.summary = 'Parse user agent to deduce the platform.'
7
+ s.homepage = 'https://github.com/basecamp/platform_agent'
8
+ s.license = 'MIT'
9
+
10
+ s.required_ruby_version = '>= 2.4.0'
11
+
12
+ s.add_dependency 'activesupport', '>= 5.2.0'
13
+ s.add_dependency 'useragent', '~> 0.16.3'
14
+
15
+ s.add_development_dependency 'activemodel', '>= 5.2.0'
16
+ s.add_development_dependency 'bundler', '~> 1.15'
17
+
18
+ s.files = `git ls-files`.split("\n")
19
+ s.test_files = `git ls-files -- test/*`.split("\n")
20
+ end
@@ -0,0 +1,91 @@
1
+ require 'active_support'
2
+ require 'active_support/testing/autorun'
3
+
4
+ require 'platform_agent'
5
+
6
+ class BasecampAgent < PlatformAgent
7
+ def ios_app?
8
+ match?(/BC3 iOS/)
9
+ end
10
+
11
+ def android_app?
12
+ match? /BC3 Android/
13
+ end
14
+
15
+ def mac_app?
16
+ match?(/Electron/) && match?(/basecamp3/) && match?(/Macintosh/)
17
+ end
18
+
19
+ def windows_app?
20
+ match?(/Electron/) && match?(/basecamp3/) && match?(/Windows/)
21
+ end
22
+ end
23
+
24
+ class PlatformAgentTest < ActiveSupport::TestCase
25
+ WINDOWS_PHONE = 'Mozilla/5.0 (Mobile; Windows Phone 8.1; Android 4.0; ARM; Trident/7.0; Touch; rv:11.0; IEMobile/11.0; NOKIA; Lumia 520) like iPhone OS 7_0_3 Mac OS X AppleWebKit/537 (KHTML, like Gecko) Mobile Safari/537'
26
+ CHROME_DESKTOP = 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36'
27
+ CHROME_ANDROID = 'Mozilla/5.0 (Linux; Android 4.4; Nexus 5 Build/_BuildID_) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/30.0.0.0 Mobile Safari/537.36'
28
+ SAFARI_IPHONE = 'Mozilla/5.0 (iPhone; CPU iPhone OS 8_1 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) Version/8.0 Mobile/12B410 Safari/600.1.4'
29
+ SAFARI_IPAD = 'Mozilla/5.0 (iPad; CPU iPhone OS 8_1 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) Version/8.0 Mobile/12B410 Safari/600.1.4'
30
+ BASECAMP_MAC = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_3) AppleWebKit/537.36 (KHTML, like Gecko) basecamp3/1.0.2 Chrome/47.0.2526.110 Electron/0.36.7 Safari/537.36'
31
+ BASECAMP_WINDOWS = 'Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 (KHTML, like Gecko) basecamp3/1.0.2 Chrome/49.0.2623.75 Electron/0.36.7 Safari/537.36'
32
+ BASECAMP_IPAD = 'BC3 iOS/3.0.1 (build 13; iPad Air 2); iOS 9.3'
33
+ BASECAMP_IPHONE = 'BC3 iOS/3.0.1 (build 13; iPhone 6S); iOS 9.3'
34
+ BASECAMP_ANDROID = 'BC3 Android/3.0.1 (build 13; Galaxy S3); Marshmallow'
35
+
36
+ test "chrome is via web" do
37
+ assert_equal "web", platform(CHROME_DESKTOP)
38
+ end
39
+
40
+ test "safari iOS is via phone" do
41
+ assert_equal "phone", platform(SAFARI_IPHONE)
42
+ end
43
+
44
+ test "safari iPad is via tablet" do
45
+ assert_equal "tablet", platform(SAFARI_IPAD)
46
+ end
47
+
48
+ test "basecamp iPhone is via iPhone app" do
49
+ assert_equal "iPhone app", platform(BASECAMP_IPHONE)
50
+ end
51
+
52
+ test "basecamp iPad is via iPad app" do
53
+ assert_equal "iPad app", platform(BASECAMP_IPAD)
54
+ end
55
+
56
+ test "basecamp Android is via Android app" do
57
+ assert_equal "Android app", platform(BASECAMP_ANDROID)
58
+ end
59
+
60
+ test "basecamp Mac is via Mac app" do
61
+ assert_equal "Mac app", platform(BASECAMP_MAC)
62
+ end
63
+
64
+ test "basecamp Windows is via Windows app" do
65
+ assert_equal "Windows app", platform(BASECAMP_WINDOWS)
66
+ end
67
+
68
+ test "blank user agent is via missing" do
69
+ assert_equal "missing", platform(nil)
70
+ end
71
+
72
+ test "other phones are via phone" do
73
+ assert_equal "phone", platform(WINDOWS_PHONE)
74
+ end
75
+
76
+ test "non-native app android detection" do
77
+ assert android_phone?(CHROME_ANDROID), "CHROME_ANDROID should be android phone"
78
+
79
+ assert_not android_phone?(SAFARI_IPHONE), "SAFARI_IPHONE should not be android phone"
80
+ assert_not android_phone?(BASECAMP_ANDROID), "BC3_ANDROID should not be android phone"
81
+ end
82
+
83
+ private
84
+ def platform(agent)
85
+ BasecampAgent.new(agent).to_s
86
+ end
87
+
88
+ def android_phone?(agent)
89
+ BasecampAgent.new(agent).android_phone?
90
+ end
91
+ end
metadata ADDED
@@ -0,0 +1,108 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: platform_agent
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - David Heinemeier Hansson
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2019-04-02 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activesupport
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 5.2.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 5.2.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: useragent
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 0.16.3
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 0.16.3
41
+ - !ruby/object:Gem::Dependency
42
+ name: activemodel
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: 5.2.0
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: 5.2.0
55
+ - !ruby/object:Gem::Dependency
56
+ name: bundler
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '1.15'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '1.15'
69
+ description:
70
+ email: david@basecamp.com
71
+ executables: []
72
+ extensions: []
73
+ extra_rdoc_files: []
74
+ files:
75
+ - Gemfile
76
+ - Gemfile.lock
77
+ - MIT-LICENSE
78
+ - README.md
79
+ - Rakefile
80
+ - lib/platform_agent.rb
81
+ - platform_agent.gemspec
82
+ - test/platform_agent_test.rb
83
+ homepage: https://github.com/basecamp/platform_agent
84
+ licenses:
85
+ - MIT
86
+ metadata: {}
87
+ post_install_message:
88
+ rdoc_options: []
89
+ require_paths:
90
+ - lib
91
+ required_ruby_version: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - ">="
94
+ - !ruby/object:Gem::Version
95
+ version: 2.4.0
96
+ required_rubygems_version: !ruby/object:Gem::Requirement
97
+ requirements:
98
+ - - ">="
99
+ - !ruby/object:Gem::Version
100
+ version: '0'
101
+ requirements: []
102
+ rubyforge_project:
103
+ rubygems_version: 2.7.3
104
+ signing_key:
105
+ specification_version: 4
106
+ summary: Parse user agent to deduce the platform.
107
+ test_files:
108
+ - test/platform_agent_test.rb