Module: Sprockets::Utils

Extended by:
Utils
Included in:
Compressing, Configuration, Mime, Paths, Processing, Transformers, Utils
Defined in:
lib/sprockets/utils.rb,
lib/sprockets/utils/gzip.rb

Overview

Internal: Utils, we didn’t know where else to put it! Functions may eventually be shuffled into more specific drawers.

Defined Under Namespace

Classes: Gzip

Instance Method Summary collapse

Instance Method Details

#concat_javascript_sources(buf, source) ⇒ Object

Internal: Accumulate asset source to buffer and append a trailing semicolon if necessary.

buf - String buffer to append to source - String source to append

Returns buf String.



100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
# File 'lib/sprockets/utils.rb', line 100

def concat_javascript_sources(buf, source)
  return buf if source.bytesize <= 0

  buf << source
  # If the source contains non-ASCII characters, indexing on it becomes O(N).
  # This will lead to O(N^2) performance in string_end_with_semicolon?, so we should use 32 bit encoding to make sure indexing stays O(1)
  source = source.encode(Encoding::UTF_32LE) unless source.ascii_only?
  return buf if string_end_with_semicolon?(source)

  # If the last character in the string was whitespace,
  # such as a newline, then we want to put the semicolon
  # before the whitespace. Otherwise append a semicolon.
  if whitespace = WHITESPACE_ORDINALS[source[-1].ord]
    buf[-1] = ";#{whitespace}"
  else
    buf << ";"
  end

  buf
end

#dfs(initial) ⇒ Object

Internal: Post-order Depth-First search algorithm.

Used for resolving asset dependencies.

initial - Initial Array of nodes to traverse. block -

node  - Current node to get children of

Returns a Set of nodes.



165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
# File 'lib/sprockets/utils.rb', line 165

def dfs(initial)
  nodes, seen = Set.new, Set.new
  stack = Array(initial).reverse

  while node = stack.pop
    if seen.include?(node)
      nodes.add(node)
    else
      seen.add(node)
      stack.push(node)
      stack.concat(Array(yield node).reverse)
    end
  end

  nodes
end

#dfs_paths(path) ⇒ Object

Internal: Post-order Depth-First search algorithm that gathers all paths along the way.

TODO: Rename function.

path - Initial Array node path block -

node - Current node to get children of

Returns an Array of node Arrays.



192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
# File 'lib/sprockets/utils.rb', line 192

def dfs_paths(path)
  paths = []
  stack = [path]
  seen  = Set.new

  while path = stack.pop
    seen.add(path.last)
    paths << path

    children = yield path.last
    children.reverse_each do |node|
      stack.push(path + [node]) unless seen.include?(node)
    end
  end

  paths
end

#duplicable?(obj) ⇒ Boolean

Internal: Check if object can safely be .dup’d.

Similar to ActiveSupport #duplicable? check.

obj - Any Object

Returns false if .dup would raise a TypeError, otherwise true.

Returns:

  • (Boolean)


17
18
19
20
21
22
23
24
# File 'lib/sprockets/utils.rb', line 17

def duplicable?(obj)
  case obj
  when NilClass, FalseClass, TrueClass, Symbol, Numeric
    false
  else
    true
  end
end

#hash_reassoc(hash, key_a, key_b = nil, &block) ⇒ Object

Internal: Duplicate and store key/value on new frozen hash.

Similar to Hash#store for nested frozen hashes.

hash - Hash key_a - Object key. Use multiple keys for nested hashes. key_b - Object key. Use multiple keys for nested hashes. block - Receives current value at key.

Examples

config = {paths: ["/bin", "/sbin"]}.freeze
new_config = hash_reassoc(config, :paths) do |paths|
  paths << "/usr/local/bin"
end

Returns duplicated frozen Hash.



61
62
63
64
65
66
67
68
69
# File 'lib/sprockets/utils.rb', line 61

def hash_reassoc(hash, key_a, key_b = nil, &block)
  if key_b
    hash_reassoc1(hash, key_a) do |value|
      hash_reassoc(value, key_b, &block)
    end
  else
    hash_reassoc1(hash, key_a, &block)
  end
end

#hash_reassoc1(hash, key) ⇒ Object

Internal: Duplicate and store key/value on new frozen hash.

Separated for recursive calls, always use hash_reassoc(hash, *keys).

hash - Hash key - Object key

Returns Hash.



34
35
36
37
38
39
40
41
42
# File 'lib/sprockets/utils.rb', line 34

def hash_reassoc1(hash, key)
  hash = hash.dup if hash.frozen?
  old_value = hash[key]
  old_value = old_value.dup if duplicable?(old_value)
  new_value = yield old_value
  new_value.freeze if duplicable?(new_value)
  hash.store(key, new_value)
  hash.freeze
end

#module_include(base, mod) ⇒ Object

Internal: Inject into target module for the duration of the block.

mod - Module

Returns result of block.



129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
# File 'lib/sprockets/utils.rb', line 129

def module_include(base, mod)
  MODULE_INCLUDE_MUTEX.synchronize do
    old_methods = {}

    mod.instance_methods.each do |sym|
      old_methods[sym] = base.instance_method(sym) if base.method_defined?(sym)
    end

    mod.instance_methods.each do |sym|
      method = mod.instance_method(sym)
      if base.method_defined?(sym)
        base.send(:alias_method, sym, sym)
      end
      base.send(:define_method, sym, method)
    end

    yield
  ensure
    mod.instance_methods.each do |sym|
      base.send(:undef_method, sym) if base.method_defined?(sym)
    end
    old_methods.each do |sym, method|
      base.send(:define_method, sym, method)
    end
  end
end

#string_end_with_semicolon?(str) ⇒ Boolean

Internal: Check if string has a trailing semicolon.

str - String

Returns true or false.

Returns:

  • (Boolean)


79
80
81
82
83
84
85
86
87
88
89
90
91
# File 'lib/sprockets/utils.rb', line 79

def string_end_with_semicolon?(str)
  i = str.size - 1
  while i >= 0
    c = str[i].ord
    i -= 1

    next if WHITESPACE_ORDINALS[c]

    return c === 0x3B
  end

  true
end