Module: PaprikaClient::Hashing
- Defined in:
- lib/paprika_client/hashing.rb
Overview
Reproduces the recipe hash that Paprika clients compute for change
detection during sync.
The value is SHA-256 of the recipe serialized the way Python's
json.dumps(obj, sort_keys=True) does it: keys sorted, ", " between
items, ": " between key and value, and non-ASCII escaped as \uXXXX.
The Paprika sync server stores whatever hash a client sends (it does not validate it against a secret algorithm), so any deterministic scheme works for round-tripping; matching the community reference implementation keeps us consistent with other tools.
Class Method Summary collapse
-
.digest(attributes) ⇒ Object
Compute the sync hash for a recipe attributes hash.
- .escape_string(str) ⇒ Object
-
.python_json_dumps(obj) ⇒ Object
Serialize a Ruby object the way Python's json.dumps(sort_keys=True) does.
Class Method Details
.digest(attributes) ⇒ Object
Compute the sync hash for a recipe attributes hash. The existing "hash" key (if any) is excluded from the computation.
22 23 24 25 |
# File 'lib/paprika_client/hashing.rb', line 22 def digest(attributes) without_hash = attributes.reject { |k, _| k.to_s == "hash" } Digest::SHA256.hexdigest(python_json_dumps(without_hash)) end |
.escape_string(str) ⇒ Object
61 62 63 64 65 |
# File 'lib/paprika_client/hashing.rb', line 61 def escape_string(str) str.gsub(/[\\"\x00-\x1f]|[^\x00-\x7f]/) do |ch| ESCAPES[ch] || ch.each_char.map { |c| format('\\u%04x', c.ord) }.join end end |
.python_json_dumps(obj) ⇒ Object
Serialize a Ruby object the way Python's json.dumps(sort_keys=True) does.
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
# File 'lib/paprika_client/hashing.rb', line 28 def python_json_dumps(obj) case obj when Hash pairs = obj.keys.sort_by(&:to_s).map do |key| "#{python_json_dumps(key.to_s)}: #{python_json_dumps(obj[key])}" end "{#{pairs.join(", ")}}" when Array "[#{obj.map { |v| python_json_dumps(v) }.join(", ")}]" when String %("#{escape_string(obj)}") when nil "null" when true "true" when false "false" else obj.to_s end end |