Class: Compass::Core::CanIUse

Inherits:
Object
  • Object
show all
Includes:
Singleton
Defined in:
lib/compass/core/caniuse.rb

Constant Summary collapse

DATA_FILE_NAME =
File.join(Compass::Core.base_directory, "data", "caniuse.json")
DATA_FEATURE_FILES =
Dir.glob(File.join(Compass::Core.base_directory, "data", "caniuse_extras", "**", "*.json"))
PUBLIC_BROWSER_NAMES =

The browser names from caniuse are ugly.

Hash.new {|h, k| k}
CAN_I_USE_NAMES =
Hash.new {|h, k| k}
SPEC_VERSION_MATCHERS =
Hash.new do |h, k|
  h[k] = /##{k}\b/
end
CAPABILITY_MATCHERS =
{
  :full_support => lambda {|support, capability| !support ^ (capability =~ /\by\b/) },
  :partial_support => lambda {|support, capability| !support ^ (capability =~ /\ba\b/) },
  :prefixed => lambda {|support, capability| !support ^ (capability =~ /\bx\b/) },
  :spec_versions => lambda {|versions, capability| versions.any? {|v| capability =~ SPEC_VERSION_MATCHERS[v] } }
}
ALTERNATE_VERSIONS =

These are versions that users might reasonably type mapped to the caniuse version.

{
  "android" => {
    "4.2" => "4.2-4.3",
    "4.3" => "4.2-4.3"
  },
  "opera" => {
    "9.5" => "9.5-9.6",
    "9.6" => "9.5-9.6",
    "10.0" => "10.0-10.1",
    "10.1" => "10.0-10.1",
  },
  "opera-mobile" => {
    "14" => "0"
  }
}

Instance Method Summary collapse

Constructor Details

#initializeCanIUse

Returns a new instance of CanIUse.



9
10
11
12
13
14
15
16
17
18
19
# File 'lib/compass/core/caniuse.rb', line 9

def initialize
  @data = MultiJson.load(File.read(DATA_FILE_NAME))
  # support ad-hoc features
  DATA_FEATURE_FILES.each do |feature_file|
    feature_name = File.basename(feature_file, ".json")
    # if the feature doesn't exist in the master `caniuse.json`
    if @data["data"][feature_name].nil?
      @data["data"][feature_name] = MultiJson.load(File.read(feature_file))
    end
  end
end

Instance Method Details

#all_prefixes(browser) ⇒ Object

returns all possible prefixes a browser might use.



61
62
63
64
65
66
67
68
69
# File 'lib/compass/core/caniuse.rb', line 61

def all_prefixes(browser)
  assert_valid_browser browser
  data = browser_data(browser)
  prefixes = ["-#{data["prefix"]}"]
  if data["prefix_exceptions"]
    prefixes += data["prefix_exceptions"].values.uniq.map{|p| "-#{p}"}
  end
  prefixes
end

#assert_valid_browser(browser) ⇒ Object

efficiently checks if a browser is valid



291
292
293
294
295
296
# File 'lib/compass/core/caniuse.rb', line 291

def assert_valid_browser(browser)
  @known_browsers ||= Set.new(browsers)
  unless @known_browsers.include?(browser)
    raise ArgumentError, "#{browser} is not known browser."
  end
end

#assert_valid_capability(capability) ⇒ Object

efficiently checks if a capability is valid



299
300
301
302
303
304
305
# File 'lib/compass/core/caniuse.rb', line 299

def assert_valid_capability(capability)
  @known_capabilities ||= Set.new(capabilities)
  unless @known_capabilities.include?(capability)
    raise ArgumentError, "#{capability} is not known browser capability."
  end
  nil
end

#assert_valid_prefix(prefix) ⇒ Object

efficiently checks if a prefix is valid



283
284
285
286
287
288
# File 'lib/compass/core/caniuse.rb', line 283

def assert_valid_prefix(prefix)
  @known_prefixes ||= Set.new(prefixes(browsers))
  unless @known_prefixes.include?(prefix)
    raise ArgumentError, "#{prefix} is not known browser prefix."
  end
end

#assert_valid_version(browser, *versions) ⇒ Object



307
308
309
310
311
312
313
# File 'lib/compass/core/caniuse.rb', line 307

def assert_valid_version(browser, *versions)
  versions.each do |v|
    unless versions(browser).include?(v)
      raise ArgumentError, "#{v} is not known version for #{browser}."
    end
  end
end

#browser_data(browser) ⇒ Object

the metadata assocated with a given browser



278
279
280
# File 'lib/compass/core/caniuse.rb', line 278

def browser_data(browser)
  @data["agents"][CAN_I_USE_NAMES[browser]]
end

#browser_ranges(capability, prefix = nil, include_unprefixed_versions = true) ⇒ Object



80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/compass/core/caniuse.rb', line 80

def browser_ranges(capability, prefix = nil, include_unprefixed_versions = true)
  assert_valid_capability capability
  browsers = prefix.nil? ? browsers() : browsers_with_prefix(prefix)
  browsers.inject({}) do |m, browser|
    browser_versions = versions(browser)
    min_version = find_first_prefixed_version(browser, browser_versions, capability, prefix)
    if min_version
      max_version = if include_unprefixed_versions
                      browser_versions.last
                    else
                      find_first_prefixed_version(browser, browser_versions.reverse, capability, prefix)
                    end
      m.update(browser => [min_version, max_version])
    end
    m
  end
end

#browser_support(browser, version, capability) ⇒ Object



272
273
274
275
# File 'lib/compass/core/caniuse.rb', line 272

def browser_support(browser, version, capability)
  version = caniuse_version(browser, version)
  capability_data(capability)["stats"][CAN_I_USE_NAMES[browser]][version]
end

#browsersObject

Returns all the known browsers according to caniuse



42
43
44
# File 'lib/compass/core/caniuse.rb', line 42

def browsers
  @browsers ||= @data["agents"].keys.map{|b| PUBLIC_BROWSER_NAMES[b] }.sort
end

#browsers_with_prefix(prefix) ⇒ Object

returns the list of browsers that use the given prefix



137
138
139
140
141
# File 'lib/compass/core/caniuse.rb', line 137

def browsers_with_prefix(prefix)
  assert_valid_prefix prefix
  prefix = "-" + prefix unless prefix.start_with?("-")
  browsers.select {|b| all_prefixes(b).include?(prefix) }
end

#caniuse_version(browser, version) ⇒ Object

returns a valid version given the version provided by the user This is used to maintain API compatibility when caniuse removes a version from their data (which seems to be replaced with a semantic equivalent).



258
259
260
261
# File 'lib/compass/core/caniuse.rb', line 258

def caniuse_version(browser, version)
  return unless version
  ALTERNATE_VERSIONS[browser] && ALTERNATE_VERSIONS[browser][version] || version
end

#capabilitiesObject

The list of capabilities tracked by caniuse.



243
244
245
246
247
248
# File 'lib/compass/core/caniuse.rb', line 243

def capabilities
  @capabilities ||= @data["data"].keys.select do |cap|
    cats = @data["data"][cap]["categories"]
    cats.any?{|cat| cat =~ /CSS/ }
  end.sort
end

#capability_data(capability) ⇒ Object

the browser data assocated with a given capability



268
269
270
# File 'lib/compass/core/caniuse.rb', line 268

def capability_data(capability)
  @data["data"][capability]
end

#capability_matches(support, capability_options_list) ⇒ Object

Return whether the capability matcher the options specified. For each capability option in the options the capability will need to match it.



156
157
158
159
160
# File 'lib/compass/core/caniuse.rb', line 156

def capability_matches(support, capability_options_list)
  capability_options_list.any? do |capability_options|
    capability_options.all? {|c, v| CAPABILITY_MATCHERS[c].call(v, support)}
  end
end

#find_first_prefixed_version(browser, versions, capability, prefix) ⇒ Object



98
99
100
101
102
103
104
105
106
107
108
# File 'lib/compass/core/caniuse.rb', line 98

def find_first_prefixed_version(browser, versions, capability, prefix)
  versions.find do |version|
    support = browser_support(browser, version, capability)
    if prefix.nil?
      support !~ /\b(n|p)\b/ && support !~ /\bx\b/
    else
      actual_prefix = prefix(browser, version)
      support !~ /\b(n|p)\b/ && support =~ /\bx\b/ && prefix == actual_prefix
    end
  end
end

#inspectObject



263
264
265
# File 'lib/compass/core/caniuse.rb', line 263

def inspect
  "#{self.class.name}(#{browsers.join(", ")})"
end

#next_version(browser, version) ⇒ Object



180
181
182
183
184
185
# File 'lib/compass/core/caniuse.rb', line 180

def next_version(browser, version)
  version = caniuse_version(browser, version)
  versions = versions(browser)
  index = versions.index(version)
  index < versions.length - 1 ? versions[index + 1] : nil
end

#omitted_usage(browser, min_supported_version) ⇒ Object #omitted_usage(browser, min_unsupported_version, max_unsupported_version) ⇒ Object

Overloads:

  • #omitted_usage(browser, min_supported_version) ⇒ Object

    How many users would be omitted if support for the given browser starts with the given version.

  • #omitted_usage(browser, min_unsupported_version, max_unsupported_version) ⇒ Object

    How many users would be omitted if the browsers with version



116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
# File 'lib/compass/core/caniuse.rb', line 116

def omitted_usage(browser, min_version, max_version = nil)
  versions = versions(browser)
  min_version = caniuse_version(browser, min_version)
  max_version = caniuse_version(browser, max_version)
  if max_version.nil?
    assert_valid_version browser, min_version
  else
    assert_valid_version browser, min_version, max_version
  end
  usage = 0
  in_range = max_version.nil?
  versions.each do |version|
    break if max_version.nil? && version == min_version
    in_range = true if (!max_version.nil? && version == min_version)
    usage += usage(browser, version) if in_range
    break if !max_version.nil? && version == max_version
  end
  return usage
end

#prefix(browser, version = nil) ⇒ Object

Returns the prefix corresponding to a particular browser



47
48
49
50
51
52
53
54
55
56
57
58
# File 'lib/compass/core/caniuse.rb', line 47

def prefix(browser, version = nil)
  version = caniuse_version(browser, version)
  assert_valid_browser browser
  assert_valid_version browser, version if version
  data = browser_data(browser)
  p = if data["prefix_exceptions"] && data["prefix_exceptions"][version]
        data["prefix_exceptions"][version]
      else
        data["prefix"]
      end
  "-#{p}"
end

#prefixed_usage(prefix, capability, capability_options_list) ⇒ Object

returns the percentage of users (0-100) that would be affected if the prefix was not used with the given capability.



164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
# File 'lib/compass/core/caniuse.rb', line 164

def prefixed_usage(prefix, capability, capability_options_list)
  assert_valid_prefix prefix
  assert_valid_capability capability
  usage = 0
  browsers_with_prefix(prefix).each do |browser|
    versions(browser).each do |version|
      next unless prefix == prefix(browser, version)
      support = browser_support(browser, version, capability)
      if capability_matches(support, capability_options_list) and support =~ /\bx\b/
        usage += usage(browser, version)
      end
    end
  end
  usage
end

#prefixes(browsers = browsers()) ⇒ Object

returns the prefixes needed by the list of browsers given



72
73
74
75
76
77
78
# File 'lib/compass/core/caniuse.rb', line 72

def prefixes(browsers = browsers())
  result = browsers.map{|b| all_prefixes(b) }
  result.flatten!
  result.uniq!
  result.sort!
  result
end

#previous_version(browser, version) ⇒ Object



187
188
189
190
191
192
# File 'lib/compass/core/caniuse.rb', line 187

def previous_version(browser, version)
  version = caniuse_version(browser, version)
  versions = versions(browser)
  index = versions.index(version)
  index > 0 ? versions[index - 1] : nil
end

#requires_prefix(browser, min_version, capability, capability_options_list) ⇒ Object

Returns whether the given minimum version of a browser requires the use of a prefix for the stated capability.

Raises:

  • (ArgumentError)


196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
# File 'lib/compass/core/caniuse.rb', line 196

def requires_prefix(browser, min_version, capability, capability_options_list)
  min_version = caniuse_version(browser, min_version)
  assert_valid_browser browser
  assert_valid_capability capability
  found_version = false
  versions(browser).each do |version|
    found_version ||= version == min_version
    next unless found_version
    support = browser_support(browser, version, capability)
    if capability_matches(support, capability_options_list) and support =~ /\bx\b/
      return prefix(browser, version)
    end
  end
  raise ArgumentError, "#{min_version} is not a version for #{browser}" unless found_version
  nil
end

#usage(browser, version) ⇒ Object

the usage % for a given browser version.



251
252
253
# File 'lib/compass/core/caniuse.rb', line 251

def usage(browser, version)
  browser_data(browser)["usage_global"][version]
end

#versions(browser, min_usage = 0) ⇒ Object

Returns the versions of a browser. If the min_usage parameter is provided, only those versions having met the threshold of user percentage.

Parameters:

  • min_usage (defaults to: 0)

    a decimal number betwee 0 and 100



235
236
237
238
239
240
# File 'lib/compass/core/caniuse.rb', line 235

def versions(browser, min_usage = 0)
  assert_valid_browser browser
  versions = browser_data(browser)["versions"].compact
  return versions if min_usage == 0
  versions.select {|v| browser_data(browser)["usage_global"][v] > min_usage }
end