freshjots 0.2.1 → 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: 878a80b88154f8d8421f1e2aea48643ecdf00dda54ebdd2d3c758ae35d66a99f
4
- data.tar.gz: e4e4fe520d5007bc38d93fc29ed96ad1c36eadabfbe09aec36a91dda22cd14dc
3
+ metadata.gz: 7674ef20013fb9165dcc54d15c0f7b1b99c4ec1967a11e3646d5b72ed605ff3b
4
+ data.tar.gz: d4663a22426bfcf413a48d0d0e5b00c12d08d291ed388c9cbc5dcac33f4b98d1
5
5
  SHA512:
6
- metadata.gz: 4a34ff09969147fda0da083a3559ed4c4b4751f8f9a48ebd67c750f72079900dd1f0a17cf7b82236354c7b139209fb60fa8069fb7caddbf566b084b667ffe18a
7
- data.tar.gz: a2619e3b7a4d32837374585fb8a1265ab98f3e28482463bca33d5dce7075a3e491d29fcc1a328b6a1ac4e0f4bbc5744f4fca29588447fd51a80ad2903d434931
6
+ metadata.gz: bb91c405637119e815517dec72db163c65d3a3b58b317cd51fd156eb3fccf08a3967547a8b661fbb3c7c4477756844b4ad08d7e07463df4435ec21a8f0c297b4
7
+ data.tar.gz: 8f240df1b1b9bb8d69aa0d294e74efa2ae47bfcafa980488fd898bbb368e02016acf96cb97aec9d187848f0f4ec8a815682e73043dfa7448c52a066926f2643c
data/README.md CHANGED
@@ -34,16 +34,23 @@ client.append("cron-jobs-prod", "backup ok #{Time.now.utc.iso8601}")
34
34
  # Read a note's body.
35
35
  puts client.note("cron-jobs-prod")[:plain_body]
36
36
 
37
- # List your notes.
38
- client.notes.each { |note| puts "#{note[:filename]}\t#{note[:title]}" }
37
+ # List your notes (most recent activity first). Sort and filter with options:
38
+ client.notes(sort: "created", folder_id: 3, limit: 20).each do |note|
39
+ puts "#{note[:id]}\t#{note[:filename]}\t#{note[:title]}"
40
+ end
39
41
 
40
42
  # Create a note. The API derives the filename from the title — for a
41
43
  # note addressable by an exact filename, use append instead.
42
44
  created = client.create(title: "Research 2026 Q2", body: "Initial outline.")
43
45
  puts created[:filename] # server-derived stream name
46
+
47
+ # Organize: move into a folder (by id or name), delete (by id or filename), list folders.
48
+ client.move("cron-jobs-prod", folder: "Ops")
49
+ client.delete("old-note")
50
+ client.folders.each { |f| puts "#{f[:id]}\t#{f[:name]}" }
44
51
  ```
45
52
 
46
- The whole API is four methods: `notes`, `note(filename)`, `create(title:, body:)`, `append(filename, text)`. `note` and `create` return the note hash directly (no `{ note: … }` wrapper); `notes` returns the array.
53
+ Client methods: `notes(sort:, folder_id:, limit:, offset:)`, `note(filename)`, `note_by_id(id)`, `create(title:, body:)`, `append(filename, text)`, `delete(id_or_filename)`, `move(id_or_filename, folder:)`, and `folders`. `note`/`note_by_id`/`create` return the note hash directly (no `{ note: … }` wrapper); `notes` and `folders` return arrays. For `notes`, `sort` is `created|updated|appended` and `folder_id` may be a folder id or `"none"` (un-foldered only).
47
54
 
48
55
  ## Errors
49
56
 
@@ -67,8 +74,8 @@ Stable error codes: `unauthenticated`, `forbidden`, `not_found`,
67
74
 
68
75
  ## Auth
69
76
 
70
- Mint a token at <https://freshjots.com/settings/api_tokens> (Dev or
71
- Dev-pro tier required). Set it once:
77
+ Mint a token at <https://freshjots.com/settings/api_tokens> (Pro or
78
+ Team tier required). Set it once:
72
79
 
73
80
  ```sh
74
81
  export FRESHJOTS_TOKEN=<your-token>
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Freshjots
4
- VERSION = "0.2.1"
4
+ VERSION = "0.3.0"
5
5
  end
data/lib/freshjots.rb CHANGED
@@ -40,8 +40,18 @@ module Freshjots
40
40
  @token, @base_url = token, base_url
41
41
  end
42
42
 
43
- def notes
44
- request(:get, "/notes")[:notes]
43
+ # List notes (summary projection). Keyword options mirror the API
44
+ # query params: sort (created|updated|appended, default updated),
45
+ # folder_id (a folder id, or "none" for un-foldered notes only), and
46
+ # limit/offset for pagination (the server caps a page at 200).
47
+ def notes(sort: nil, folder_id: nil, limit: nil, offset: nil)
48
+ query = {}
49
+ query[:sort] = sort if sort
50
+ query[:folder_id] = folder_id unless folder_id.nil? || folder_id.to_s.empty?
51
+ query[:limit] = limit unless limit.nil?
52
+ query[:offset] = offset unless offset.nil?
53
+ path = query.empty? ? "/notes" : "/notes?#{URI.encode_www_form(query)}"
54
+ request(:get, path)[:notes]
45
55
  end
46
56
 
47
57
  # show-by-filename renders the serializer at the top level (no
@@ -50,6 +60,11 @@ module Freshjots
50
60
  request(:get, "/notes/by-filename/#{escape(filename)}")
51
61
  end
52
62
 
63
+ # Full note by numeric id (GET /notes/:id) — top-level serializer.
64
+ def note_by_id(id)
65
+ request(:get, "/notes/#{escape(id)}")
66
+ end
67
+
53
68
  # Create a note. The API permits note[title, plain_body, format, ...]
54
69
  # — NOT filename: the server DERIVES the filename from the title. For
55
70
  # a note addressable by an exact, caller-chosen filename, use append
@@ -71,8 +86,50 @@ module Freshjots
71
86
  true
72
87
  end
73
88
 
89
+ # Delete a note. Accepts a numeric id or a filename (resolved to its
90
+ # id via the by-filename lookup). Locked (append-only) notes are
91
+ # refused by the API with note_locked. Returns true on success.
92
+ def delete(id_or_filename)
93
+ request(:delete, "/notes/#{resolve_note_id(id_or_filename)}")
94
+ true
95
+ end
96
+
97
+ # Move a note into a folder. `folder` may be a folder id, a folder
98
+ # name (resolved via /folders), or nil / "none" / "root" to send the
99
+ # note back to the root.
100
+ def move(id_or_filename, folder: nil)
101
+ id = resolve_note_id(id_or_filename)
102
+ request(:post, "/notes/#{id}/move", { folder_id: resolve_folder_id(folder) })
103
+ end
104
+
105
+ # List folders ({ folders: [...] } envelope).
106
+ def folders
107
+ request(:get, "/folders")[:folders]
108
+ end
109
+
74
110
  private
75
111
 
112
+ # A note reference is either a numeric id (used as-is) or a
113
+ # filename/title resolved to an id via the by-filename lookup.
114
+ def resolve_note_id(value)
115
+ return value if value.is_a?(Integer) || value.to_s.match?(/\A\d+\z/)
116
+
117
+ note(value.to_s)[:id]
118
+ end
119
+
120
+ # A folder reference resolves to an id (or nil for the root). Names go
121
+ # through /folders; an unknown or ambiguous name is an ArgumentError.
122
+ def resolve_folder_id(value)
123
+ return nil if value.nil? || %w[none root null].include?(value.to_s.downcase)
124
+ return value if value.is_a?(Integer) || value.to_s.match?(/\A\d+\z/)
125
+
126
+ matches = folders.select { |f| f[:name] == value.to_s }
127
+ raise ArgumentError, "no folder named '#{value}'" if matches.empty?
128
+ raise ArgumentError, "ambiguous folder name '#{value}' — use its id" if matches.size > 1
129
+
130
+ matches.first[:id]
131
+ end
132
+
76
133
  def request(method, path, body = nil)
77
134
  uri = URI("#{@base_url}#{path}")
78
135
  req = Net::HTTP.const_get(method.to_s.capitalize).new(uri)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: freshjots
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Goran Arsov
@@ -10,7 +10,7 @@ cert_chain: []
10
10
  date: 1980-01-02 00:00:00.000000000 Z
11
11
  dependencies: []
12
12
  description: Append-only notebooks for cron jobs, deploy scripts, and bots. Wraps
13
- the four-endpoint plain-text REST API at freshjots.com/api/v1.
13
+ the plain-text REST API at freshjots.com/api/v1.
14
14
  email:
15
15
  - arsphy@yahoo.com
16
16
  executables: []