crest 0.1.0 → 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 +4 -4
- data/CHANGELOG.md +21 -0
- data/LICENSE.txt +1 -1
- data/README.md +15 -22
- data/lib/crest/card.rb +3 -3
- data/lib/crest/photo.rb +7 -17
- data/lib/crest/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: dbe18f619bc95f0eb929c699184702b55f595cdd81f2287a8ef964e0b121b3dd
|
|
4
|
+
data.tar.gz: ee2f37cc8c25e057db03c6c3f7cb92d68164e278427cea28ac44dd733228ee8e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a2fc6523bd91f1d421ffe6806f9d4a30ae4bcd280b75e76aa35719db24bb9f7d51c054b99e227816815363b8969721bddda64619b96406a9e5bf877d7a43d453
|
|
7
|
+
data.tar.gz: '06169a97517d48d394d24b6b578e1424bddb2c52e3c6b11cef6c81bb46a2dff9e1e22d6fdabfc970f2bb40ad6cd82fc255b1e5b5b1d49f7a1d9d762173e39c42'
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,27 @@ 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
|
+
|
|
26
|
+
## 0.1.1 - 2026-09-11
|
|
27
|
+
|
|
28
|
+
* [Fix] Name HouseAccount, Inc. as the copyright holder in the license, which 0.1.0 shipped
|
|
29
|
+
naming the author instead
|
|
30
|
+
|
|
10
31
|
## 0.1.0 - 2026-09-11
|
|
11
32
|
|
|
12
33
|
* [Feature] Build a vCard 3.0 from a name, a number and whatever else is worth saving --
|
data/LICENSE.txt
CHANGED
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
|
|
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', '~>
|
|
17
|
+
gem 'crest', '~> 1.0'
|
|
18
18
|
```
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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 = '
|
|
39
|
+
config.email = 'support@houseaccount.com'
|
|
40
40
|
config.photo = Rails.root.join 'public/cube.png'
|
|
41
|
-
config.phone = -> {
|
|
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
|
|
56
|
-
told apart by the image's own first bytes rather than by
|
|
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
|
-
|
|
59
|
-
|
|
60
|
-
|
|
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: '+
|
|
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
|
-
#
|
|
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]
|
|
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.
|
|
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
|
-
#
|
|
10
|
-
|
|
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,
|
|
23
|
-
def
|
|
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