Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor the cookie module #95

Merged
merged 2 commits into from
Dec 22, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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