Compare commits

...

11 Commits

Author SHA1 Message Date
Eric Budd c8aa13e13e Bump Iris version to 1.1.3 2024-05-04 12:07:53 -05:00
tjpcc 1515714986 use a hash set for unread topics
The "u" or "unread" command diffs two sets stored on disk: all topics, and unread messages.

A flat array was previously used for both, but this resulted in slow O(n^2) behavior.

This diff turns the unread messaegs into a hash set and uses that for the "contains" test in the loop over all topics.
2024-05-04 11:54:56 -05:00
Zachary Huang 8387df2070 prevent edit if no change made to message text 2024-05-04 11:48:49 -05:00
Zachary Crockett 467cf1d69e Fix handling of single-word hostname 2024-05-04 11:37:11 -05:00
Eric Budd ee21bffa50 Minor README/TODO tweaks 2024-05-04 11:29:30 -05:00
Eric B. Budd 03dfd561b4 Bump Iris version to 1.1.2 2022-12-26 00:05:44 -06:00
Eric B. Budd f046b1a436 Add section to docs on releases 2022-12-26 00:05:44 -06:00
Eric B. Budd 94ccc65647 Tweak and alphabetize command-line option completions and documentation 2022-12-26 00:05:44 -06:00
jlxip 4da59d6230 First attempt at bash and zsh completions 2022-12-26 00:05:44 -06:00
Eric B. Budd d744eb023d Fix bug where "hostname" portion of author is missing or garbled 2022-12-26 00:05:44 -06:00
Eric B. Budd 2361933f81 Edited the README for clarity. Thanks ~loghead for the suggestion! 2022-12-10 14:34:31 -06:00
7 changed files with 248 additions and 68 deletions

View File

@ -1,5 +1,15 @@
# Changelog
## 1.1.3
* Fixed bug (https://github.com/Calamitous/iris/issues/62) causing errors when running Iris for the first time. [Thank you, lee2sman!](https://github.com/lee2sman)
* Fixed bug causing message to be deleted when exiting a message edit without making changes. [Thank you, zack466!](https://github.com/zack466)
* Greatly improved the speed of reloading unread messages. [Thank you, teepark!](https://github.com/teepark)
## 1.1.2
* Fixed bug where "hostname" portion of author is missing or garbled.
* Added CLI completion scripts for bash and zsh. [Thanks, jlxip!](https://github.com/jlxip)
* Minor tweaks and additions to the documentation.
## 1.1.1
* Make discarded message response more prominent
* Try nano if /usr/bin/vim is not set

147
README.md
View File

@ -5,7 +5,7 @@ Iris is a tiny bit of shared message and file convention that pretends to be for
It is a fully usable message system, designed for use between different users on a single server.
Iris is strictly text-based, requiring no GUI or web servers.
Iris is strictly text-based, requiring no GUI, database, or web servers.
* [Installation](#installation)
* [Usage](#usage)
@ -14,6 +14,7 @@ Iris is strictly text-based, requiring no GUI or web servers.
* [Text Features/Markup](#text-featuresmarkup)
* [Philosophy](#philosophy)
* [Tests](#tests)
* [Cutting A Release](#cutting-a-release)
* [Technical Bits](#technical-bits)
* [License](#license)
@ -36,7 +37,7 @@ Iris has a readline interface that can be used to navigate the message corpus.
```bash
%> iris
Welcome to Iris v. 1.1.1. Type "help" for a list of commands.; Ctrl-D or 'quit' to leave.
Welcome to Iris v. 1.1.3. Type "help" for a list of commands.; Ctrl-D or 'quit' to leave.
| ID | U | TIMESTAMP | AUTHOR | TITLE
| 1 | | 2018-01-24T05:49:53Z | jimmy_foo@ctrl-c.club | Welcome!
@ -104,7 +105,7 @@ The format of the unread topics list is identical to the format of the [topics](
---
### Display topic
`# (topic id) - Read specified topic`
`(topic id #) - Read specified topic`
Type in the index of the topic you wish to read. This will display the topic and all its replies.
```
@ -368,7 +369,7 @@ This outputs the current version of Iris, along with messsage, topic, and author
```bash
jennie_minnie@ctrl-c.club~> info
Iris 1.1.1
Iris 1.1.3
22 topics, 0 unread.
50 messages, 0 unread.
10 authors.
@ -385,25 +386,50 @@ This displays helpful reminders of the commands that Iris supports.
There are a few options you can pass in from the command-line:
* [--version, -v](#--version-v)
* [--stats, -s](#--stats-s)
* [--interactive, -i](#--interactive-i)
* [--mark-all-read](#--mark-all-read)
* [--debug](#--debug)
* [--dump, -d](#--dump-d)
* [--help, -h](#--help-h)
* [--debug](#--debug)
* [--interactive, -i](#--interactive-i)
* [--mark-all-read](#--mark-all-read)
* [--stats, -s](#--stats-s)
* [--test-file, -s](#--test-file)
* [--version, -v](#--version-v)
### --version/-v
### --debug
This displays the current version of Iris and exits.
This option turns on debug mode. Warnings and errors will be output as the program is used.
```bash
iris --version
```
Having these messages constantly appear can be distracting or annoying during regular Iris usage, but are useful when tracking down issues.
```bash
Iris 1.1.1
```
This option works in both interactive and non-interactive mode.
---
### --dump/-d
This reads the entire message corpus and outputs it as a stream of JSON data, suitable for piping into a backup file, `jq` parser, or similar.
This command does not enter Iris' interactive mode.
---
### --help/-h
This command displays a complete list of options that Iris recognizes.
---
### --interactive/-i
This command enters Iris' interactive mode, the default mode with which users can compose and read topics and replies.
This is the mode that Iris enters if no options are passed on the command-line.
---
### --mark-all-read
This command simply marks every message as read in Iris. It's a quick way to get to "Irisbox Zero".
---
@ -418,7 +444,7 @@ iris --stats
```
```bash
Iris 1.1.1
Iris 1.1.3
22 topics, 0 unread.
50 messages, 0 unread.
10 authors.
@ -426,39 +452,6 @@ Iris 1.1.1
---
### --interactive/-i
This command enters Iris' interactive mode, the default mode with which users can compose and read topics and replies.
This is the mode that Iris enters if no options are passed on the command-line.
---
### --mark-all-read
This command simply marks every message as read in Iris. It's a quick way to get to "Irisbox Zero".
---
### --dump/-d
This reads the entire message corpus and outputs it as a stream of JSON data, suitable for piping into a backup file, `jq` parser, or similar.
This command does not enter Iris' interactive mode.
---
### --debug
This option turns on debug mode. Warnings and errors will be output as the program is used.
Having these messages constantly appear can be distracting or annoying during regular Iris usage, but are useful when tracking down issues.
This option works in both interactive and non-interactive mode.
---
### --test-file/-f
```bash
@ -471,9 +464,17 @@ This option works in both interactive and non-interactive mode.
---
### --help/-h
### --version/-v
This command displays a complete list of options that Iris recognizes.
This displays the current version of Iris and exits.
```bash
iris --version
```
```bash
Iris 1.1.3
```
## Text Features/Markup
@ -596,7 +597,7 @@ Iris must:
* Deleted or edited messages should leave flags or placeholders for other users to know that other content was there before.
* The Iris client should expect that any message file could be missing, altered, or corrupted, and should handle those cases gracefully.
* Be portable
* All Iris files should be human-readable (and -editable, in a pinch)
* All Iris data files should be human-readable (and -editable, in a pinch)
* The use of the official Iris client should be optional for a user to manage his or her messages. A text editor should suffice.
* Other clients which follow the Iris file format should work seamlessly with the official Iris client.
* Be secure
@ -605,7 +606,7 @@ Iris must:
* Be a teacher
* Code should be clean, well-organized, and readable.
* Be limited in scope
* The source code should not exceed 1,000 LOC
* The source code should not exceed 1,000 SLOC
## Tests
@ -622,6 +623,42 @@ To run the tests:
ruby tests/iris_test.rb
```
```bash
Run options: --seed 11507
# Running:
.........................SSS.......SS.......S.....SSSSSSS....SSSSS
Finished in 0.107785s, 612.3294 runs/s, 677.2734 assertions/s.
66 runs, 73 assertions, 0 failures, 0 errors, 18 skips
You have skipped tests. Run with --verbose for details.
```
## Cutting A Release
### Prep
* Make all updates in an appropriately named branch. (ie. `1.1.3`)
* Make sure all commits are clean.
* Make sure tests pass.
### Updates
* Change version number in `iris.rb`
* Change version numbers in documentation `README.md`
* Add new version and details to `CHANGELOG`
* Create new commit for release (Named "Bump Iris version to 1.1.3" or similar)
### Make the Sausage
* Push the branch
* `git push origin`
* Merge the branch (Fast-forward only, for a linear commit history)
* Tag the release
* `git tag 1.1.3`
* Push the tags
* `git push origin --tags`
## Technical Bits
* [Dependencies](#dependencies)

View File

@ -10,12 +10,15 @@
* Flesh out technical sections
### Bugs
* Terrible slowdown when refreshing topics
* Performance is fine when no new topics show up
* Is `Time.now.utc.iso8601` working as expected?
* Fix bug when people are posting from different time zones
* Fix message ordering when editing/deleting multiple messages
* Gracefully handle attempt to "r 1 message"
### Features
* Add permalinks/indexes
* Add pagination/less for long message lists
* https://github.com/Calamitous/iris/issues/1
* Add local timezone rendering

41
completions/bash Executable file
View File

@ -0,0 +1,41 @@
#!/bin/bash -eu
# This is a bash completion script for iris
# It should be copied to /usr/share/bash-completion/completions/iris
_iris_module() {
local cur prev OPTS
COMPREPLY=()
cur="${COMP_WORDS[COMP_CWORD]}"
prev="${COMP_WORDS[COMP_CWORD-1]}"
case $prev in
-f | --test-file)
return 0 # File
;;
esac
case $cur in
--*)
OPTS="--debug
--dump
--help
--interactive
--stats
--test-file
--version"
;;
*)
OPTS="-d
-f
-h
-i
-s
-v"
;;
esac
COMPREPLY=( $(compgen -W "${OPTS[*]}" -- $cur) )
}
complete -F _iris_module -o bashdefault -o default iris

29
completions/zsh Executable file
View File

@ -0,0 +1,29 @@
#compdef _iris iris
# This is a zsh completion script for iris
# It should be copied to /usr/share/zsh/functions/Completion/Unix/_iris
function _iris {
local context state state_descr line
typeset -A opt_args
_arguments -C \
"--debug[Print warnings and debug informtation during use]" \
"-d[Dump entire message corpus out]" \
"--dump[Dump entire message corpus out]" \
"-h[Show help information]" \
"--help[Show help information]" \
"-i[Enter interactive mode (default)]" \
"--interactive[Enter interactive mode (default)]" \
"--mark-all-read[Mark every message in Iris as \"read\".]"
"-s[Display Iris version and message stats]" \
"--stats[Display Iris version and message stats]" \
"-f[Use the specified test file for messages]:f:->f" \
"--test-file[Use the specified test file for messages]:f:->f" \
"-v[Display the current version of Iris]" \
"--version[Display the current version of Iris]" \
if [ "$state" = "f" ]; then
_files
fi
}

36
iris.rb
View File

@ -4,6 +4,7 @@ require 'digest'
require 'etc'
require 'json'
require 'readline'
require 'set'
require 'tempfile'
require 'time'
# require 'pry' # Only needed for debugging
@ -22,18 +23,32 @@ class String
end
class Config
VERSION = '1.1.1'
VERSION = '1.1.3'
MESSAGE_FILE = "#{ENV['HOME']}/.iris.messages"
HISTORY_FILE = "#{ENV['HOME']}/.iris.history"
IRIS_SCRIPT = __FILE__
USER = ENV['USER'] || ENV['LOGNAME'] || ENV['USERNAME']
HOSTNAME = `hostname -d`.chomp
AUTHOR = "#{USER}@#{HOSTNAME}"
ENV_EDITOR = ENV['EDITOR'].presence || `which nano`.chomp.presence
@@debug_mode = false
def self.hostname
return @hostname if @hostname
hostname = `hostname`.chomp
hostname = 'localhost' if hostname.empty?
components = hostname.split('.')
return @hostname = hostname if components.length == 1
@hostname = components[-2..-1].compact.join('.')
end
def self.author
user = ENV['USER'] || ENV['LOGNAME'] || ENV['USERNAME']
@author ||= "#{user}@#{self.hostname}"
end
def self.find_files
(`ls /home/**/.iris.messages`).split("\n")
end
@ -221,6 +236,10 @@ class Corpus
end
end
def self.unread_topics_set
unread_topics.map(&:hash).to_set
end
def self.size
@@corpus.size
end
@ -343,7 +362,7 @@ end
class Message
attr_reader :timestamp, :edit_hash, :author, :parent, :message, :errors, :is_deleted
def initialize(message, parent = nil, author = Config::AUTHOR, edit_hash = nil, timestamp = Time.now.utc.iso8601, is_deleted = nil)
def initialize(message, parent = nil, author = Config.author, edit_hash = nil, timestamp = Time.now.utc.iso8601, is_deleted = nil)
@message = message
@parent = parent
@author = author
@ -775,6 +794,8 @@ class Interface
if message_text.length <= 1
Display.say 'Empty message, not updating...'
elsif message_text == message.message
Display.say 'No change made, not updating...'
else
Message.edit(message_text, message)
Display.say 'Message edited!'
@ -890,7 +911,7 @@ class Interface
end
def prompt
"#{Config::AUTHOR}~> "
"#{Config.author}~> "
end
def initialize(args)
@ -914,8 +935,9 @@ class Interface
Display.say Display.topic_header
# TODO: Paginate here
unread_hashes = Corpus.unread_topics_set
Corpus.topics.each_with_index do |topic, index|
if Corpus.unread_topics.include?(topic)
if unread_hashes.include?(topic.hash)
Display.say topic.to_topic_line(index + 1)
end
end

View File

@ -24,16 +24,54 @@ describe Config do
_(Config::HISTORY_FILE).must_match /\/\.iris\.history$/
end
it 'has the username' do
_(Config::USER).must_equal 'jerryberry'
end
describe '.hostname' do
before do
Config.instance_variable_set(:@hostname, nil)
end
it 'has a hostname' do
_(Config::HOSTNAME).wont_be_nil
it 'has a hostname' do
_(Config.hostname).wont_be_nil
end
it 'correctly interprets an empty string' do
Config.expects(:`).with('hostname').returns('')
_(Config.hostname).must_equal 'localhost'
end
it 'correctly interprets localhost' do
Config.instance_variable_set(:@hostname, nil)
Config.expects(:`).with('hostname').returns('localhost')
_(Config.hostname).must_equal 'localhost'
end
it 'correctly interprets a single word' do
Config.instance_variable_set(:@hostname, nil)
Config.expects(:`).with('hostname').returns('example')
_(Config.hostname).must_equal 'example'
end
it 'correctly interprets a subdomain' do
Config.instance_variable_set(:@hostname, nil)
Config.expects(:`).with('hostname').returns('example.com')
_(Config.hostname).must_equal 'example.com'
end
it 'correctly interprets a subsubdomain' do
Config.instance_variable_set(:@hostname, nil)
Config.expects(:`).with('hostname').returns('foo.example.com')
_(Config.hostname).must_equal 'example.com'
end
it 'correctly interprets an arbitrary number of subdomains' do
Config.instance_variable_set(:@hostname, nil)
Config.expects(:`).with('hostname').returns('foo.bar.baz.quux.example.com')
_(Config.hostname).must_equal 'example.com'
end
end
it 'has the author' do
_(Config::AUTHOR).must_equal "#{Config::USER}@#{Config::HOSTNAME}"
user = 'jerryberry'
_(Config.author).must_equal "#{user}@#{Config.hostname}"
end
it 'has the $EDITOR environment variable' do