crest 0.1.1 → 1.0.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: 75faee574d6193d7dedbaf585f4260b503bfc0c773af9313669ba76bc4c2a7a9
4
- data.tar.gz: 04e8c9160ca452c308deed21f9a5aad900068a9547f7149eae597c3336084b4b
3
+ metadata.gz: dbe18f619bc95f0eb929c699184702b55f595cdd81f2287a8ef964e0b121b3dd
4
+ data.tar.gz: ee2f37cc8c25e057db03c6c3f7cb92d68164e278427cea28ac44dd733228ee8e
5
5
  SHA512:
6
- metadata.gz: 482847bd72627b7016f551f46a3e89eac352bee26b9e4586322d90276dc3d95f47d82c2380743fbe1394f8e606b58812a55d622056fec278205d5e432875a531
7
- data.tar.gz: 8ae58bf911147b1829be723453dfed043db427539e498d7c0d8a5a6f65ee0e319f4d5f217ae97b3f096159b323b125fd8cbebe00d5b0e5c29ca50bc829648fbe
6
+ metadata.gz: a2fc6523bd91f1d421ffe6806f9d4a30ae4bcd280b75e76aa35719db24bb9f7d51c054b99e227816815363b8969721bddda64619b96406a9e5bf877d7a43d453
7
+ data.tar.gz: '06169a97517d48d394d24b6b578e1424bddb2c52e3c6b11cef6c81bb46a2dff9e1e22d6fdabfc970f2bb40ad6cd82fc255b1e5b5b1d49f7a1d9d762173e39c42'
data/CHANGELOG.md CHANGED
@@ -7,6 +7,22 @@ For more information about changelogs, check [Keep a Changelog](http://keepachan
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## 1.0.0 - 2026-09-11
11
+
12
+ * [Breaking change] Answer `Crest::Photo#property` where `#to_s` used to answer, and leave a
13
+ picture that is neither a PNG nor a JPEG off the card -- warned about on stderr, which a host
14
+ already collects -- rather than raising. A card without a picture beats a 500 on a route
15
+ that is public by definition, and a `to_s` that can answer nil is a trap for anyone
16
+ interpolating one
17
+ * [Breaking change] Drop `Crest::Photo.of` and the process-wide hash of pictures behind it, so
18
+ `Crest::Card` builds a `Crest::Photo` and the gem holds no mutable global state at all. It saved
19
+ one file read and one base64 of a few kilobytes on a route hit occasionally, and charged for it
20
+ with a hash keyed on whatever was handed in -- the image bytes themselves, where a host passes
21
+ bytes -- never evicted, growing without bound for any host whose `photo` callable resolves to
22
+ more than one picture, and blind to a file that changed on disk
23
+ * [Feature] Say that the card is an organization's: `N:` carries the one name a business has,
24
+ which is what makes `name` a single setting rather than a compromise
25
+
10
26
  ## 0.1.1 - 2026-09-11
11
27
 
12
28
  * [Fix] Name HouseAccount, Inc. as the copyright holder in the license, which 0.1.0 shipped
data/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  A contact card your Rails app hands out, so a text from an unknown number arrives under a name.
4
4
 
5
5
  `GET /houseaccount.vcf` answers with a vCard. A phone offers to save it, and from then on your
6
- messages come from *HouseAccount* rather than from `+1 800 555 0100`.
6
+ messages come from *HouseAccount* rather than from `774-468-7322`.
7
7
 
8
8
  ## How to install
9
9
 
@@ -14,12 +14,12 @@ gem install crest
14
14
  Or, in your `Gemfile`:
15
15
 
16
16
  ```ruby
17
- gem 'crest', '~> 0.1.0'
17
+ gem 'crest', '~> 1.0'
18
18
  ```
19
19
 
20
- Crest is below `1.0`, so the pin stops short of `0.2.0` rather than admitting every `0.x`:
21
- `~> major.minor` only promises what it says once the major is real. From `1.0` on it will be
22
- `~> 1.0`, and `bundle update` will never cross a breaking change.
20
+ `~> major.minor` is what makes that safe: `bundle update` takes every fix and every feature
21
+ Crest ships, and never crosses a breaking change, because the major only moves when the API
22
+ does and from `1.0` on it moves only on purpose.
23
23
 
24
24
  ## Handing out a card
25
25
 
@@ -36,9 +36,9 @@ Crest.configure do |config|
36
36
  config.name = 'HouseAccount'
37
37
  config.org = 'HouseAccount'
38
38
  config.url = 'https://houseaccount.com/'
39
- config.email = 'hello@houseaccount.com'
39
+ config.email = 'support@houseaccount.com'
40
40
  config.photo = Rails.root.join 'public/cube.png'
41
- config.phone = -> { Delivery::Twilio.sender }
41
+ config.phone = -> { Twilio.sender }
42
42
  end
43
43
  ```
44
44
 
@@ -52,21 +52,14 @@ saves under a blank name. Everything else is left out of the card when you do no
52
52
  That is what makes a number read out of the environment safe: production and staging differ, and
53
53
  a value read once at boot would be wrong in one of them until a restart.
54
54
 
55
- The picture is bytes, or a path the gem reads. It is read from disk once, and PNG and JPEG are
56
- told apart by the image's own first bytes rather than by anything you have to declare.
55
+ The picture is bytes, or a path the gem reads, read afresh for each card, so replacing the file
56
+ replaces the picture. PNG and JPEG are told apart by the image's own first bytes rather than by
57
+ anything you have to declare, and anything else is warned about on stderr and left off the card
58
+ — a public route is the wrong place to discover that somebody swapped a file.
57
59
 
58
- ## Why a route and not an engine to mount
59
-
60
- You name the path, because the filename is your app's own name. And because it is a route rather
61
- than an engine, the line goes wherever you want it — inside a `scope`, behind a `constraints`,
62
- under a subdomain — which a mount point cannot do.
63
-
64
- ## Why the controller is not your ApplicationController
65
-
66
- `Crest::CardsController` descends from `ActionController::Base`, not from your
67
- `ApplicationController`. That is deliberate and it is the surprising half: the card is public by
68
- definition — a phone fetches it with no session — so an inherited `before_action :authenticate`
69
- would refuse the one request the gem exists to answer.
60
+ **The card is an organization's.** `N:` carries the one name a business has, which is why `name`
61
+ is a single setting. A person's card wants a given name and a family name in their own fields,
62
+ and Crest does not take them.
70
63
 
71
64
  ## Without Rails
72
65
 
@@ -75,7 +68,7 @@ The builder is plain Ruby and loads on its own, Rails or no Rails:
75
68
  ```ruby
76
69
  require 'crest/card'
77
70
 
78
- Crest::Card.new(name: 'HouseAccount', phone: '+18005550100',
71
+ Crest::Card.new(name: 'HouseAccount', phone: '+17744687322',
79
72
  url: 'https://houseaccount.com/', photo: 'cube.png').to_s
80
73
  ```
81
74
 
data/lib/crest/card.rb CHANGED
@@ -1,12 +1,12 @@
1
1
  require 'crest/photo'
2
2
 
3
3
  module Crest
4
- # A contact card a phone offers to save, written as the vCard 3.0 every phone reads.
4
+ # An organization's contact card, written as the vCard 3.0 a phone offers to save.
5
5
  class Card
6
6
  # What a line may hold before a vCard folds it, less the space that continues it.
7
7
  FOLD = 74
8
8
 
9
- # @param name [String] name the card is saved under.
9
+ # @param name [String] organization the card is saved under.
10
10
  # @param phone [String] number in the form a phone should dial it.
11
11
  # @param url [String] address of the site behind the name.
12
12
  # @param email [String] address somebody may write back to.
@@ -34,7 +34,7 @@ module Crest
34
34
  "TEL;TYPE=CELL:#{@phone}",
35
35
  ("URL:#{@url}" if @url),
36
36
  ("EMAIL;TYPE=INTERNET:#{@email}" if @email),
37
- (Photo.of(@photo).to_s if @photo),
37
+ (Photo.new(@photo).property if @photo),
38
38
  'END:VCARD',
39
39
  ].compact
40
40
  end
data/lib/crest/photo.rb CHANGED
@@ -1,32 +1,22 @@
1
- require 'crest/error'
2
-
3
1
  module Crest
4
2
  # The picture a card carries: bytes as they were handed over, or the file a path names.
5
3
  class Photo
6
4
  # The first bytes of each image format a phone will read a saved card's picture from.
7
5
  SIGNATURES = { 'PNG' => "\x89PNG".b, 'JPEG' => "\xFF\xD8\xFF".b }
8
6
 
9
- # The picture a path or a string of bytes stands for, read from disk only the first time,
10
- # so a card handed out a thousand times costs one read and one encoding.
11
- # @return [Crest::Photo] picture that file holds, or those bytes are.
12
- def self.of(photo)
13
- @read ||= {}
14
- @read[photo] ||= new photo
15
- end
7
+ # What a host hears about a picture no phone would draw.
8
+ UNREADABLE = 'crest: the photo is neither a PNG nor a JPEG, so the card goes out without it'
16
9
 
17
10
  # @param photo [String, Pathname] image bytes, or the file to read them from.
18
11
  def initialize(photo)
19
12
  @bytes = photo.respond_to?(:binread) ? photo.binread : photo
13
+ @type = SIGNATURES.find { |_, signature| @bytes.b.start_with? signature }&.first
14
+ warn UNREADABLE unless @type
20
15
  end
21
16
 
22
- # @return [String] PHOTO property, base64-encoded and typed by the image's own first bytes.
23
- def to_s = @property ||= "PHOTO;ENCODING=b;TYPE=#{type}:#{[@bytes].pack 'm0'}"
24
-
25
- private
26
-
27
- def type
28
- SIGNATURES.find { |_, signature| @bytes.b.start_with? signature }&.first ||
29
- raise(Error, 'a photo has to be a PNG or a JPEG')
17
+ # @return [String] PHOTO property, or nothing where the bytes are in no format a phone reads.
18
+ def property
19
+ @property ||= "PHOTO;ENCODING=b;TYPE=#{@type}:#{[@bytes].pack 'm0'}" if @type
30
20
  end
31
21
  end
32
22
  end
data/lib/crest/version.rb CHANGED
@@ -1,4 +1,4 @@
1
1
  module Crest
2
2
  # The version of this gem, as RubyGems knows it.
3
- VERSION = '0.1.1'
3
+ VERSION = '1.0.0'
4
4
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: crest
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Claudio Baccigalupo