Skip to content

Commit

Permalink
Merge pull request #95 from opal/staging
Browse files Browse the repository at this point in the history
Refactor the cookie module
  • Loading branch information
hmdne authored Dec 22, 2021
2 parents 0bfb495 + bf5e9e6 commit c869443
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 33 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
## 0.3.2
* Cookie: refactor the module
* Note in documentation it's available as `$document.cookies` and it's the preferred way to access it
* Always encode a cookie with JSON, unless a new parameter `raw:` is provided

## 0.3.1
* Element#inner_dom: Reduce flickering - first build tree, then insert it
* NodeSet#to_a to be aliased to #to_ary
93 changes: 61 additions & 32 deletions opal/browser/cookies.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
require 'stringio'

module Browser

# Allows manipulation of browser cookies.
Expand All @@ -8,10 +6,19 @@ module Browser
#
# Usage:
#
# cookies = Browser::Cookies.new(`document`)
# cookies = $document.cookies
# cookies["my-cookie"] = "monster"
# cookies.delete("my-cookie")
#
# By default, cookies are stored JSON-encoded. You can supply a `raw:` option
# whenever you need to access/write the cookies in a raw way, eg.
#
# cookies["my-other-cookie", raw: true] = 123
#
# You can also set this option while referencing $document.cookies, eg.
#
# cookies = $document.cookies(raw: true)
# cookies["my-other-cookie"] = 123
class Cookies
# Default cookie options.
DEFAULT = {
Expand All @@ -26,24 +33,34 @@ class Cookies
# Create a new {Cookies} wrapper.
#
# @param document [native] the native document object
def initialize(document)
# @param options [Hash] the default cookie options
def initialize(document, options = {})
@document = document
@options = DEFAULT.dup
@options = DEFAULT.merge(options)
end

# Access the cookie with the given name.
#
# @param name [String] the name of the cookie
# @param options [Hash] the options for the cookie
#
# @option options [Boolean] :raw get a raw cookie value, don't encode it with JSON
#
# @return [Object]
def [](name)
def [](name, options = {})
options = @options.merge(options)

matches = `#@document.cookie`.scan(/#{Regexp.escape(FormData.encode(name))}=([^;]*)/)

return if matches.empty?

result = matches.flatten.map {|value|
JSON.parse(FormData.decode(value))
}
result = matches.flatten.map do |value|
if options[:raw]
FormData.decode(value)
else
JSON.parse(FormData.decode(value))
end
end

result.length == 1 ? result.first : result
end
Expand All @@ -54,70 +71,82 @@ def [](name)
# @param value [Object] the data to set
# @param options [Hash] the options for the cookie
#
# @option options [Boolean] :raw don't encode a value with JSON
# @option options [Integer] :max_age the max age of the cookie in seconds
# @option options [Time] :expires the expire date
# @option options [String] :path the path the cookie is valid on
# @option options [String] :domain the domain the cookie is valid on
# @option options [Boolean] :secure whether the cookie is secure or not
def []=(name, value, options = {})
string = value.is_a?(String) ? value : JSON.dump(value)
encoded_value = encode(name, string, @options.merge(options))
def []=(name, options = {}, value)
options = @options.merge(options)
if options[:raw]
string = value.to_s
else
string = JSON.dump(value)
end
encoded_value = encode(name, string, options)
`#@document.cookie = #{encoded_value}`
end

# Delete a cookie.
#
# @param name [String] the name of the cookie
def delete(name)
def delete(name, _options = {})
`#@document.cookie = #{encode name, '', expires: Time.now}`
end

# @!attribute [r] keys
# @return [Array<String>] all the cookie names
def keys
Array(`#@document.cookie.split(/; /)`).map {|cookie|
def keys(_options = {})
Array(`#@document.cookie.split(/; /)`).map do |cookie|
cookie.split(/\s*=\s*/).first
}
end
end

# @!attribute [r] values
# @return [Array] all the cookie values
def values
keys.map {|key|
self[key]
}
def values(options = {})
options = @options.merge(options)
keys.map do |key|
self[key, options]
end
end

# Enumerate the cookies.
#
# @yieldparam key [String] the name of the cookie
# @yieldparam value [String] the value of the cookie
#
# @param options [Hash] the options for the cookie
#
# @option options [Boolean] :raw don't encode a value with JSON
#
# @return [self]
def each(&block)
return enum_for :each unless block
def each(options = {}, &block)
return enum_for :each, options unless block
options = @options.merge(options)

keys.each {|key|
yield key, self[key]
}
keys.each do |key|
yield key, self[key, options]
end

self
end

# Delete all the cookies
#
# @return [self]
def clear
keys.each {|key|
def clear(_options = {})
keys.each do |key|
delete key
}
end

self
end

protected
def encode(key, value, options = {})
io = StringIO.new
io = []

io << FormData.encode(key) << ?= << FormData.encode(value) << '; '

Expand All @@ -127,15 +156,15 @@ def encode(key, value, options = {})
io << 'domain=' << options[:domain] << '; ' if options[:domain]
io << 'secure' if options[:secure]

io.string
io.join
end
end

class DOM::Document < DOM::Element
# @!attribute [r] cookies
# @return [Cookies] the cookies for the document
def cookies
Cookies.new(@native) if defined?(`#@native.cookie`)
def cookies(options = {})
Cookies.new(@native, options) if defined?(`#@native.cookie`)
end
end

Expand Down
2 changes: 1 addition & 1 deletion opal/browser/version.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
module Browser
VERSION = '0.3.1'
VERSION = '0.3.2'
end

0 comments on commit c869443

Please sign in to comment.