boldsign 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f0a4d128418d45a484979b044ce83a9e868ef1a142145962ec0580b0f34f4e20
4
- data.tar.gz: 4ee136cc02d3020b4beb1e70d614929de07f0f64e7965d8e0742a3e74cb0b893
3
+ metadata.gz: 9fc2965681164f6e721c5d7a18b9370d1ddfd7570916cde257078b29d3be34d5
4
+ data.tar.gz: 9210a75137622829403d8b6ae47dfa1f51e3f71462313b12ef076e6b240391a9
5
5
  SHA512:
6
- metadata.gz: 486cb527ac17cef2ddcf9505a5a2bfe59b3736ee75d9e9b8778344c16a8b6c0e5795d17f5fa7d739fb87f6dc70e1e353fea13929e4303ab8abf1c08adf66e792
7
- data.tar.gz: 79edff344004bd59f868a44b6812c1c16bfa922823d0a16eaf28be54f970714b75c16d19be411e6ea175d37b4de8e7ab7bcc30b75da592ade64c49a7afbd9a12
6
+ metadata.gz: 00ab26917b2ad3ab3af835ec58842b1fa75ba19d61254447466a5eee7b5db388f9c5800ec7807806fd737526772516f43277520ba1005d18539a8e54356f0ee8
7
+ data.tar.gz: '038338c9aca83500340f85e0d6d2e6247466701739ff3606ae8bf59905f930541514e9e3130ac5296b9ee658085993c601126d84ee5e6527451b4ba1d75df1ab'
data/CHANGELOG.md CHANGED
@@ -7,6 +7,29 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.3.0] — 2026-05-22
11
+
12
+ ### Changed
13
+ - Request bodies (JSON and multipart) now have their Hash keys recursively
14
+ converted to PascalCase before being sent to BoldSign. Callers can pass
15
+ idiomatic snake_case (or camelCase) Ruby symbols/strings and the gem will
16
+ emit the casing the API expects. Already-PascalCase keys pass through
17
+ unchanged. Non-Hash/Array values (including IO objects and
18
+ `Faraday::Multipart::FilePart` instances) are not touched.
19
+
20
+ ### Added
21
+ - `Boldsign::CaseConvert` module with `pascalize` / `pascalize_key` helpers
22
+ for callers that need to PascalCase keys outside of the HTTP path.
23
+
24
+ ## [0.2.0] — 2026-05-22
25
+
26
+ ### Added
27
+ - `Boldsign::Resources::Document#send_document` now supports multipart file
28
+ uploads via a `files:` keyword. Pass an array of `{io:, filename:,
29
+ content_type:}` hashes (or `Faraday::Multipart::FilePart` instances) to
30
+ send PDFs (or other supported file types) for signature. When `files:` is
31
+ omitted the request is sent as JSON, preserving the prior behavior.
32
+
10
33
  ## [0.1.0] — 2026-05-22
11
34
 
12
35
  ### Added
@@ -0,0 +1,43 @@
1
+ module Boldsign
2
+ # Converts arbitrary Ruby hash keys (snake_case or camelCase, Symbol or
3
+ # String) to PascalCase string keys recursively, matching the casing the
4
+ # BoldSign REST API expects in request bodies. Non-Hash and non-Array values
5
+ # — including binary IO objects, FilePart instances, scalars, dates, etc. —
6
+ # pass through untouched.
7
+ module CaseConvert
8
+ module_function
9
+
10
+ # Recursively PascalCase every Hash key reachable from `obj`.
11
+ # @param obj [Object]
12
+ # @return [Object] new object with the same shape and PascalCase keys
13
+ def pascalize(obj)
14
+ case obj
15
+ when Hash
16
+ obj.each_with_object({}) { |(k, v), acc| acc[pascalize_key(k)] = pascalize(v) }
17
+ when Array
18
+ obj.map { |v| pascalize(v) }
19
+ else
20
+ obj
21
+ end
22
+ end
23
+
24
+ # @param key [String, Symbol]
25
+ # @return [String] PascalCase string
26
+ def pascalize_key(key)
27
+ str = key.to_s
28
+ return str if str.empty?
29
+
30
+ if str.include?("_")
31
+ str.split("_").map { |part| capitalize_word(part) }.join
32
+ else
33
+ capitalize_word(str)
34
+ end
35
+ end
36
+
37
+ def capitalize_word(str)
38
+ return str if str.empty?
39
+
40
+ str[0].upcase + str[1..]
41
+ end
42
+ end
43
+ end
@@ -125,7 +125,7 @@ module Boldsign
125
125
  req.body = body
126
126
  else
127
127
  req.headers["Content-Type"] = "application/json"
128
- req.body = body.is_a?(String) ? body : JSON.generate(body)
128
+ req.body = body.is_a?(String) ? body : JSON.generate(CaseConvert.pascalize(body))
129
129
  end
130
130
  end
131
131
  end
@@ -4,11 +4,17 @@ module Boldsign
4
4
  # sending, listing, downloading, editing, reminding, authenticating, tagging,
5
5
  # and embedded signing flows.
6
6
  #
7
- # @example Send a document
7
+ # @example Send a document (JSON body)
8
+ # client.documents.send_document(
9
+ # title: "NDA",
10
+ # signers: [{ name: "Jane", emailAddress: "jane@example.com", signerOrder: 1 }]
11
+ # )
12
+ #
13
+ # @example Send a document with file uploads (multipart)
8
14
  # client.documents.send_document(
9
15
  # title: "NDA",
10
16
  # signers: [{ name: "Jane", emailAddress: "jane@example.com", signerOrder: 1 }],
11
- # files: [...]
17
+ # files: [{ io: StringIO.new(pdf_bytes), filename: "nda.pdf", content_type: "application/pdf" }]
12
18
  # )
13
19
  #
14
20
  # @see https://developers.boldsign.com/documents
@@ -18,7 +24,25 @@ module Boldsign
18
24
  def behalf_list(**params); @client.get("/v1/document/behalfList", params); end
19
25
  def properties(document_id); @client.get("/v1/document/properties", documentId: document_id); end
20
26
 
21
- def send_document(body); @client.post("/v1/document/send", body: body); end
27
+ # Send a document for signature.
28
+ #
29
+ # Pass `files:` to upload one or more PDFs (or other supported file types)
30
+ # via multipart/form-data. Without `files:`, the request is sent as JSON
31
+ # and BoldSign must already know the file content (e.g. via Base64 in the
32
+ # body or by referencing an existing template).
33
+ #
34
+ # @param files [Array<Hash>, nil] When present, each hash must include
35
+ # `:io` and `:filename` and may include `:content_type`. The request
36
+ # switches to multipart/form-data and remaining kwargs are serialized
37
+ # to multipart string parts (non-scalar values become JSON strings).
38
+ # @param body [Hash] Top-level BoldSign send-document fields
39
+ # (`title:`, `signers:`, `disableEmails:`, `metadata:`, etc.).
40
+ def send_document(files: nil, **body)
41
+ return @client.post("/v1/document/send", body: body) if files.nil?
42
+
43
+ @client.post("/v1/document/send", body: multipart_send_body(body, files), multipart: true)
44
+ end
45
+
22
46
  def draft_send(body); @client.post("/v1/document/draftSend", body: body); end
23
47
  def edit(document_id, body); @client.put("/v1/document/edit", body: body, params: { documentId: document_id }); end
24
48
  def cancel_editing(document_id); @client.post("/v1/document/cancelEditing", params: { documentId: document_id }); end
@@ -46,6 +70,40 @@ module Boldsign
46
70
  def add_authentication(document_id, body); @client.patch("/v1/document/addAuthentication", body: body, params: { documentId: document_id }); end
47
71
 
48
72
  def prefill_fields(document_id, body); @client.patch("/v1/document/prefillFields", body: body, params: { documentId: document_id }); end
73
+
74
+ private
75
+
76
+ def multipart_send_body(body, files)
77
+ parts = body.each_with_object({}) do |(key, value), acc|
78
+ next if value.nil?
79
+
80
+ pascal_key = Boldsign::CaseConvert.pascalize_key(key)
81
+ acc[pascal_key] = if scalar?(value)
82
+ value.to_s
83
+ else
84
+ JSON.generate(Boldsign::CaseConvert.pascalize(value))
85
+ end
86
+ end
87
+ parts["Files"] = Array(files).map { |f| file_part(f) }
88
+ parts
89
+ end
90
+
91
+ def file_part(file)
92
+ return file if file.is_a?(Faraday::Multipart::FilePart)
93
+
94
+ unless file.is_a?(Hash)
95
+ raise ArgumentError, "files entries must be Hash with :io and :filename, or Faraday::Multipart::FilePart"
96
+ end
97
+
98
+ io = file[:io] || file["io"] or raise ArgumentError, "file part missing :io"
99
+ filename = file[:filename] || file["filename"] or raise ArgumentError, "file part missing :filename"
100
+ type = file[:content_type] || file["content_type"] || "application/octet-stream"
101
+ Faraday::Multipart::FilePart.new(io, type, filename)
102
+ end
103
+
104
+ def scalar?(value)
105
+ value.is_a?(String) || value.is_a?(Numeric) || value == true || value == false
106
+ end
49
107
  end
50
108
  end
51
109
  end
@@ -1,3 +1,3 @@
1
1
  module Boldsign
2
- VERSION = "0.1.0"
2
+ VERSION = "0.3.0"
3
3
  end
data/lib/boldsign.rb CHANGED
@@ -4,6 +4,7 @@ require "json"
4
4
 
5
5
  require_relative "boldsign/version"
6
6
  require_relative "boldsign/error"
7
+ require_relative "boldsign/case_convert"
7
8
  require_relative "boldsign/client"
8
9
  require_relative "boldsign/resource"
9
10
  require_relative "boldsign/resources/brand"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: boldsign
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - James Klein
@@ -92,6 +92,7 @@ files:
92
92
  - README.md
93
93
  - boldsign.gemspec
94
94
  - lib/boldsign.rb
95
+ - lib/boldsign/case_convert.rb
95
96
  - lib/boldsign/client.rb
96
97
  - lib/boldsign/error.rb
97
98
  - lib/boldsign/resource.rb