active_recall 2.1.0 → 2.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: 1145dd0124d31f1cd1d200170e3956b20818e73bd2d1fedb4ceacf4f82a8f4ba
4
- data.tar.gz: b4b8e2c1d1edd66cc0c756200b525aec806468c7f95626c257bd95f369d56037
3
+ metadata.gz: 63a48f6351a76013bbf2922ca75b507b6dccb1ab23867896217b53bc6a77e26f
4
+ data.tar.gz: 9d8e3189bb8f5b1f15b284168e5973e87070bf223c0427b5a574cacd38c8e4b0
5
5
  SHA512:
6
- metadata.gz: e94584e1041b5f695ab3ec0d24a87a7c3a4abf200fb11c7951917f98cbd84f9e24225b52a6c45d393fe31beb76e08a9ac683266cd191f7ec5cacd78644b8ed68
7
- data.tar.gz: 6979b3ac85b8cd12751566161bfa2d55ca5832fc82693a92469f3844ec481d86c4bda05fc709fcbb2ea8f3dbc84959a8d342a683ce6e936a05ab235c278a66df
6
+ metadata.gz: efe496383029f35391f1b54533f525451c5ee8a02e440316b990b812923b2061fc94eeff2ed1d691b17a1838b2d266b51ec9dde7d3305d511b484edb53b606b7
7
+ data.tar.gz: ff9f67e94b77d93175af60926ad889655862989c18a9b2fb07b8e75503526b8281f32cd3a982ab1d70933363454e40461a10dd2be72784bc5097243173ff5b1e
@@ -21,6 +21,8 @@ jobs:
21
21
  ruby:
22
22
  - 3.2
23
23
  - 3.3
24
+ - 3.4
25
+ - 4.0
24
26
  allow_failures:
25
27
  - false
26
28
  env:
@@ -35,7 +37,9 @@ jobs:
35
37
  uses: ruby/setup-ruby@v1
36
38
  with:
37
39
  ruby-version: ${{ matrix.ruby }}
38
- bundler-cache: true
40
+ bundler-cache: false
41
+ - name: Bundle Install
42
+ run: bundle install
39
43
  - name: Install Appraisal Dependencies
40
44
  run: bundle exec appraisal install
41
45
  - name: Test with Rails 7.0
data/.tool-versions CHANGED
@@ -1 +1 @@
1
- ruby 3.3.0
1
+ ruby 3.4.8
data/CLAUDE.md ADDED
@@ -0,0 +1,194 @@
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
+
5
+ ## Project Overview
6
+
7
+ ActiveRecall is a Ruby gem that implements a spaced-repetition system (SRS) for ActiveRecord models. It allows treating arbitrary ActiveRecord models as flashcards with configurable scheduling algorithms. The gem is backwards-compatible with the okubo gem and requires Rails 6+ and Ruby 3+.
8
+
9
+ ## Common Commands
10
+
11
+ ### Testing
12
+ ```bash
13
+ # Run all tests (PREFERRED - handles appraisal automatically)
14
+ bin/spec
15
+
16
+ # Run tests against a specific Rails version
17
+ bin/spec rails-7-0
18
+ bin/spec rails-7-1
19
+ bin/spec rails-8-0
20
+
21
+ # Note: Direct bundle exec commands may encounter sqlite3 version conflicts
22
+ # The bin/spec wrapper handles this by using the appropriate appraisal gemfiles
23
+ ```
24
+
25
+ ### Linting
26
+ ```bash
27
+ # Run StandardRB linter
28
+ bin/lint
29
+ # or
30
+ bundle exec standardrb
31
+
32
+ # Auto-fix issues
33
+ bundle exec standardrb --fix
34
+ ```
35
+
36
+ ### Development Setup
37
+ ```bash
38
+ # Install dependencies and set up database
39
+ bin/setup
40
+
41
+ # Launch interactive console
42
+ bin/console
43
+ ```
44
+
45
+ ### Testing Against Multiple Rails Versions
46
+ ```bash
47
+ # Generate gemfiles for different Rails versions (run after Gemfile changes)
48
+ bundle exec appraisal install
49
+
50
+ # Run tests against specific Rails version manually
51
+ bundle exec appraisal rails-7-0 rake spec
52
+ bundle exec appraisal rails-7-1 rake spec
53
+ bundle exec appraisal rails-8-0 rake spec
54
+
55
+ # Note: bin/spec runs tests against all Rails versions automatically
56
+ # This is defined in the Appraisals file and creates separate gemfiles in gemfiles/
57
+ ```
58
+
59
+ ### Gem Management
60
+ ```bash
61
+ # Build the gem
62
+ bundle exec rake build
63
+
64
+ # Install gem locally
65
+ bundle exec rake install
66
+
67
+ # Release new version (requires updating lib/active_recall/version.rb first)
68
+ bundle exec rake release
69
+ ```
70
+
71
+ ## Architecture
72
+
73
+ ### Core Components
74
+
75
+ 1. **ActiveRecall::Base** (lib/active_recall/base.rb)
76
+ - Provides `has_deck` class method that makes any ActiveRecord model a deck owner
77
+ - Mixes in DeckMethods and ItemMethods when invoked
78
+ - Sets up polymorphic relationship between users and their decks
79
+
80
+ 2. **ActiveRecall::Deck** (lib/active_recall/models/deck.rb)
81
+ - Polymorphic model representing a collection of items to review
82
+ - Belongs to a user (polymorphic), has many items
83
+ - Key methods: `review`, `next`, `untested`, `failed`, `known`, `expired`, `box(n)`
84
+ - Handles database adapter differences (MySQL vs others) for random ordering
85
+
86
+ 3. **ActiveRecall::Item** (lib/active_recall/models/item.rb)
87
+ - Represents individual flashcard items in a deck
88
+ - Polymorphic association to source objects (the actual flashcard content)
89
+ - Methods: `score!(grade)`, `right!`, `wrong!`
90
+ - Delegates to configured algorithm for calculating next review dates
91
+
92
+ 4. **Algorithms** (lib/active_recall/algorithms/)
93
+ - Each algorithm class implements: `type`, `required_attributes`, `right`, `wrong`, and optionally `score`
94
+ - Algorithm types: `:binary` (right/wrong only) or `:gradable` (accepts scores)
95
+ - Binary algorithms: LeitnerSystem (default), SoftLeitnerSystem, FibonacciSequence
96
+ - Gradable algorithm: SM2
97
+ - All algorithms are stateless; they accept current state and return new state as a hash
98
+
99
+ 5. **Configuration** (lib/active_recall/configuration.rb)
100
+ - Global configuration via `ActiveRecall.configure`
101
+ - Primary setting: `algorithm_class` (defaults to LeitnerSystem)
102
+ - Configuration should be set in Rails initializers
103
+
104
+ ### Data Flow
105
+
106
+ 1. A model calls `has_deck :items_name` to enable SRS functionality
107
+ 2. This creates a Deck record (polymorphic, user_id/user_type)
108
+ 3. Items are added to deck with `<<` operator, creating Item records
109
+ 4. Item records store: box number, review dates, times_right/times_wrong, easiness_factor (for SM2)
110
+ 5. User calls `right_answer_for!(item)` or `wrong_answer_for!(item)` (or `score!(grade, item)` for gradable algorithms)
111
+ 6. This delegates to Item's `right!`/`wrong!`/`score!` which calls the configured algorithm
112
+ 7. Algorithm returns hash of updated attributes (box, next_review, etc.)
113
+ 8. Deck provides query methods that filter items by state and return source objects
114
+
115
+ ### Key Design Patterns
116
+
117
+ - **Polymorphic associations**: Decks belong to any "user" model, Items reference any "source" model
118
+ - **Strategy pattern**: Algorithms are swappable via configuration
119
+ - **Delegation**: Deck methods delegate to Item scopes, which use the configured algorithm
120
+ - **Stateless algorithms**: All algorithm classes are stateless with class methods only (except FibonacciSequence which caches Fibonacci numbers in instances)
121
+
122
+ ## Database Schema
123
+
124
+ The gem creates two tables via generators:
125
+ - `active_recall_decks`: id, user_id (polymorphic), user_type, created_at, updated_at
126
+ - `active_recall_items`: id, deck_id, source_id (polymorphic), source_type, box, times_right, times_wrong, last_reviewed, next_review, easiness_factor, created_at, updated_at
127
+
128
+ ## Migration from Okubo
129
+
130
+ The gem includes a migration template (`migrate_okubo_to_active_recall.rb`) that renames tables from okubo_* to active_recall_*. Use `rails generate active_recall --migrate_data true` to generate this migration.
131
+
132
+ ## Code Style
133
+
134
+ - Uses StandardRB for linting (config in standard.yml)
135
+ - All files have `# frozen_string_literal: true` pragma
136
+ - Ruby 3.2+ syntax required
137
+
138
+ ## Testing Conventions
139
+
140
+ ### Test File Structure
141
+ - Spec files mirror lib/ directory structure (e.g., `lib/active_recall/foo.rb` → `spec/active_recall/foo_spec.rb`)
142
+ - All spec files start with `# frozen_string_literal: true` and `require "spec_helper"`
143
+ - Use `describe ClassName` for top-level blocks
144
+ - Use `context` blocks to organize different scenarios
145
+ - Use `subject { described_class.new }` for the class under test
146
+
147
+ ### Common Patterns
148
+ ```ruby
149
+ # Basic structure
150
+ describe ActiveRecall::SomeClass do
151
+ subject { described_class.new }
152
+
153
+ describe "#method_name" do
154
+ it "describes expected behavior" do
155
+ expect(subject.method_name).to eq(expected_value)
156
+ end
157
+ end
158
+
159
+ context "when specific condition" do
160
+ it "behaves differently" do
161
+ # test implementation
162
+ end
163
+ end
164
+ end
165
+ ```
166
+
167
+ ### Shared Examples
168
+ - Algorithm specs use shared examples for common behavior (see spec/active_recall/algorithms/algorithm_spec.rb)
169
+ - Binary algorithms share: `it_behaves_like "binary spaced repetition algorithms"`
170
+ - This ensures consistent testing across algorithm implementations
171
+
172
+ ### Test Data Setup
173
+ - Use `let` blocks for test data that needs to be created
174
+ - Use `before` blocks for setup that must run before each test
175
+ - The spec_helper.rb defines Word and User models for testing deck functionality
176
+
177
+ ### Running Specific Tests
178
+ While `bin/spec` is preferred for full test runs, you can run individual specs during development:
179
+ ```bash
180
+ # During active development, you may need to specify the appraisal
181
+ bundle exec appraisal rails-8-0 rspec spec/active_recall/configuration_spec.rb
182
+
183
+ # Or run all specs via bin/spec which handles all Rails versions
184
+ bin/spec
185
+ ```
186
+
187
+ ### Test Infrastructure Notes
188
+ - Tests use an in-memory SQLite database (`:memory:`) configured in spec/spec_helper.rb
189
+ - Different Rails versions require different sqlite3 gem versions:
190
+ - Rails 7.0/7.1 use sqlite3 ~> 1.4
191
+ - Rails 8.0 uses sqlite3 >= 2.1
192
+ - The appraisal system manages these version conflicts via separate gemfiles
193
+ - Always use `bin/spec` to avoid sqlite3 version conflict errors
194
+ - Direct `bundle exec rspec` commands will fail with "can't activate sqlite3" errors
data/Gemfile CHANGED
@@ -4,5 +4,18 @@ source "https://rubygems.org"
4
4
 
5
5
  git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
6
6
 
7
- # Specify your gem's dependencies in active_recall.gemspec
8
7
  gemspec
8
+
9
+ # TODO: Remove conditional dependency after dropping support for Ruby 3.2
10
+ begin
11
+ ruby_version = Gem::Version.new(RUBY_VERSION)
12
+ if ruby_version >= Gem::Version.new("3.4.0")
13
+ gem "nokogiri", ">= 1.16.2"
14
+ end
15
+ if ruby_version >= Gem::Version.new("4.0.0")
16
+ gem "benchmark"
17
+ gem "ostruct"
18
+ gem "logger"
19
+ end
20
+ rescue
21
+ end
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- active_recall (2.1.0)
4
+ active_recall (2.3.0)
5
5
  activerecord (>= 7.0, < 9.0)
6
6
  activesupport (>= 7.0, < 9.0)
7
7
 
@@ -119,7 +119,7 @@ GEM
119
119
  net-smtp
120
120
  marcel (1.0.2)
121
121
  mini_mime (1.1.5)
122
- mini_portile2 (2.8.5)
122
+ mini_portile2 (2.8.9)
123
123
  minitest (5.18.1)
124
124
  mutex_m (0.2.0)
125
125
  net-imap (0.4.9.1)
@@ -132,10 +132,14 @@ GEM
132
132
  net-smtp (0.4.0.1)
133
133
  net-protocol
134
134
  nio4r (2.7.0)
135
- nokogiri (1.16.0)
135
+ nokogiri (1.18.7)
136
136
  mini_portile2 (~> 2.8.2)
137
137
  racc (~> 1.4)
138
- nokogiri (1.16.0-x86_64-linux)
138
+ nokogiri (1.18.7-arm64-darwin)
139
+ racc (~> 1.4)
140
+ nokogiri (1.18.7-x86_64-darwin)
141
+ racc (~> 1.4)
142
+ nokogiri (1.18.7-x86_64-linux-gnu)
139
143
  racc (~> 1.4)
140
144
  parallel (1.24.0)
141
145
  parser (3.3.1.0)
@@ -219,6 +223,8 @@ GEM
219
223
  ruby2_keywords (0.0.5)
220
224
  sqlite3 (2.5.0)
221
225
  mini_portile2 (~> 2.8.0)
226
+ sqlite3 (2.5.0-arm64-darwin)
227
+ sqlite3 (2.5.0-x86_64-darwin)
222
228
  sqlite3 (2.5.0-x86_64-linux-gnu)
223
229
  standard (1.35.1)
224
230
  language_server-protocol (~> 3.17.0.2)
@@ -244,12 +250,15 @@ GEM
244
250
  zeitwerk (2.6.12)
245
251
 
246
252
  PLATFORMS
253
+ arm64-darwin
247
254
  ruby
255
+ x86_64-darwin
248
256
  x86_64-linux
249
257
 
250
258
  DEPENDENCIES
251
259
  active_recall!
252
260
  appraisal
261
+ nokogiri (>= 1.16.2)
253
262
  rails (>= 7.0, < 9.0)
254
263
  rake (>= 12.0)
255
264
  rdoc
data/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # ActiveRecall
2
2
 
3
3
  **ActiveRecall** is a spaced-repetition system that allows you to treat arbitrary [ActiveRecord](https://github.com/rails/rails/tree/master/activerecord) models as if they were flashcards to be learned and reviewed.
4
- It it based on, and is intended to be backwards compatible with, the [okubo](https://github.com/rgravina/okubo) gem.
4
+ It is based on, and is intended to be backwards compatible with, the [okubo](https://github.com/rgravina/okubo) gem.
5
5
  The primary differentiating features are that it lets the user specify the scheduling algorithm and is fully compatible with (and requires) Rails 6+ and Ruby 3+.
6
6
 
7
7
  ## Installation
@@ -28,17 +28,13 @@ Or install it yourself as:
28
28
 
29
29
  $ gem install active_recall
30
30
 
31
- ## Usage
32
- You can configure the desired SRS algorithm during runtime:
33
- ```ruby
34
- ActiveRecall.configure do |config|
35
- config.algorithm_class = ActiveRecall::FibonacciSequence
36
- end
37
- ```
38
- Algorithms include `FibonacciSequence`, `LeitnerSystem`, `SoftLeitnerSystem`, and `SM2` (see [here](https://en.wikipedia.org/wiki/SuperMemo#Description_of_SM-2_algorithm)).
39
- For Rails applications, try doing this from within an [initializer file](https://guides.rubyonrails.org/configuring.html#using-initializer-files).
31
+ The generator creates all the migrations any algorithm needs (including the `easiness_factor` column for SM2 and the FSRS-specific columns), so you don't have to revisit migrations when you switch algorithms later.
40
32
 
41
- Assume you have an application allowing your users to study words in a foreign language. Using the `has_deck` method you can set up a deck of flashcards that the user will study:
33
+ ## Quick Start
34
+
35
+ The fastest way to get going — no algorithm choice, no grade scale, just right/wrong feedback. This uses the default `LeitnerSystem`.
36
+
37
+ Suppose you have an application allowing your users to study words in a foreign language. Use `has_deck` to set up a deck of flashcards:
42
38
 
43
39
  ```ruby
44
40
  class Word < ActiveRecord::Base
@@ -48,18 +44,76 @@ class User < ActiveRecord::Base
48
44
  has_deck :words
49
45
  end
50
46
 
51
- user = User.create!(:name => "Robert")
52
- word = Word.create!(:kanji => "日本語", :kana => "にほんご", :translation => "Japanese language")
47
+ user = User.create!(name: "Robert")
48
+ word = Word.create!(kanji: "日本語", kana: "にほんご", translation: "Japanese language")
49
+
50
+ user.words << word
51
+ user.words.untested #=> [word]
52
+
53
+ user.right_answer_for!(word)
54
+ user.words.known #=> [word]
55
+
56
+ user.wrong_answer_for!(word)
57
+ user.words.failed #=> [word]
58
+ ```
59
+
60
+ That's it. Want graded feedback (Again/Hard/Good/Easy) or modern scheduling? See [Choosing an Algorithm](#choosing-an-algorithm) below.
61
+
62
+ ## Choosing an Algorithm
63
+
64
+ > **Not sure which to pick?** Stick with the default `LeitnerSystem` — it works out of the box and only needs right/wrong feedback. Reach for `FSRS` when you want modern, evidence-based scheduling and are willing to collect 1–4 ratings ("Again / Hard / Good / Easy") from users.
65
+
66
+ The full menu, in increasing order of sophistication:
67
+
68
+ | Algorithm | Type | How you grade | Reach for it when |
69
+ |---|---|---|---|
70
+ | **`LeitnerSystem`** *(default — start here)* | binary | `right_answer_for!` / `wrong_answer_for!` | You want the simplest thing that works |
71
+ | `SoftLeitnerSystem` | binary | `right_answer_for!` / `wrong_answer_for!` | Leitner is too punishing on occasional lapses |
72
+ | `FibonacciSequence` | binary | `right_answer_for!` / `wrong_answer_for!` | You want faster-growing intervals than Leitner |
73
+ | `SM2` | gradable | `score!(0..5, item)` | You want the classic SuperMemo behavior users know from Anki |
74
+ | **`FSRS`** *(modern recommendation)* | gradable | `score!(1..4, item)` | You're building something serious and want best-in-class retention |
75
+
76
+ **Binary** algorithms expect right-or-wrong feedback (`user.right_answer_for!(item)` / `user.wrong_answer_for!(item)`). **Gradable** algorithms expect a numeric grade per review (`user.score!(grade, item)`). Mixing them — e.g. calling `right_answer_for!` while configured to use SM2 — raises `ActiveRecall::IncompatibleAlgorithmError`.
77
+
78
+ ## Configuration
79
+
80
+ Skip this section if you're sticking with the default `LeitnerSystem` — there's nothing to configure.
81
+
82
+ To switch algorithms, set `algorithm_class` from a Rails [initializer file](https://guides.rubyonrails.org/configuring.html#using-initializer-files):
83
+
84
+ ```ruby
85
+ # config/initializers/active_recall.rb
86
+ ActiveRecall.configure do |config|
87
+ config.algorithm_class = ActiveRecall::FSRS # or SM2, SoftLeitnerSystem, FibonacciSequence
88
+ end
89
+ ```
90
+
91
+ ### FSRS-specific configuration
92
+
93
+ FSRS exposes three optional knobs. All have sensible defaults; tune only if you have a reason to:
94
+
95
+ - `fsrs_request_retention` — target retention probability (default `0.9`). Lower → longer intervals, more forgetting tolerated.
96
+ - `fsrs_maximum_interval` — caps the scheduled interval, in days.
97
+ - `fsrs_weights` — array of FSRS weights for advanced tuning.
98
+
99
+ ```ruby
100
+ ActiveRecall.configure do |config|
101
+ config.algorithm_class = ActiveRecall::FSRS
102
+ config.fsrs_request_retention = 0.85
103
+ config.fsrs_maximum_interval = 365
104
+ end
53
105
  ```
54
106
 
55
- You can add words and record attempts to guess the word as right or wrong. Various methods exist to allow you to access subsets of this collection:
107
+ ## Usage with binary algorithms
108
+
109
+ Applies to `LeitnerSystem`, `SoftLeitnerSystem`, and `FibonacciSequence`.
56
110
 
57
111
  ```ruby
58
112
  # Initially adding a word
59
113
  user.words << word
60
114
  user.words.untested #=> [word]
61
115
 
62
- # Guessing a word correctly (when using a binary algorithm)
116
+ # Guessing a word correctly
63
117
  user.right_answer_for!(word)
64
118
  user.words.known #=> [word]
65
119
 
@@ -75,11 +129,11 @@ As time passes, words need to be reviewed to keep them fresh in memory:
75
129
 
76
130
  ```ruby
77
131
  # Three days later...
78
- user.words.known #=> []
132
+ user.words.known #=> []
79
133
  user.words.expired #=> [word]
80
134
  ```
81
135
 
82
- Guessing a word correctly several times in a row results in the word taking longer to expire, and demonstrates mastery of that word.
136
+ Guessing a word correctly several times in a row makes the word take longer to expire, demonstrating mastery:
83
137
 
84
138
  ```ruby
85
139
  user.right_answer_for!(word)
@@ -93,21 +147,55 @@ user.right_answer_for!(word)
93
147
  user.words.expired #=> [word]
94
148
  ```
95
149
 
96
- When using a gradable algorithm (rather than binary) such as the SM2 algorithm, you will need to supply your own grade along with the item:
150
+ ## Usage with SM2
151
+
152
+ [SM2](https://en.wikipedia.org/wiki/SuperMemo#Description_of_SM-2_algorithm) uses a 0–5 grade scale:
153
+
154
+ | Grade | Meaning |
155
+ |---|---|
156
+ | `5` | Perfect response |
157
+ | `4` | Correct response after a hesitation |
158
+ | `3` | Correct response recalled with serious difficulty |
159
+ | `2` | Incorrect response, but close |
160
+ | `1` | Incorrect response with familiarity |
161
+ | `0` | Complete blackout |
162
+
163
+ Grades **≥ 3** count as a success: the box advances and `times_right` increments. Grades **< 3** reset the box to `0` and increment `times_wrong`. Each item's `easiness_factor` starts at `2.5` and is clamped to a minimum of `1.3`.
164
+
165
+ ```ruby
166
+ user.words << word
167
+
168
+ user.score!(5, word) # perfect recall — box advances, EF rises
169
+ user.score!(2, word) # incorrect — box resets to 0
170
+ ```
171
+
172
+ Calling `user.right_answer_for!(word)` while SM2 is configured raises `ActiveRecall::IncompatibleAlgorithmError` — use `score!` instead.
173
+
174
+ ## Usage with FSRS
175
+
176
+ [FSRS](https://github.com/open-spaced-repetition/fsrs4anki) uses a 1–4 grade scale matching the familiar Anki buttons:
177
+
178
+ | Grade | Meaning |
179
+ |---|---|
180
+ | `1` | Again (lapse) |
181
+ | `2` | Hard |
182
+ | `3` | Good |
183
+ | `4` | Easy |
184
+
185
+ FSRS tracks `stability`, `difficulty`, `state`, and `lapses` per item. Those columns are added automatically by `rails generate active_recall` — no extra setup needed.
186
+
97
187
  ```ruby
98
- grade = 3
99
- user.score!(grade, word)
188
+ user.words << word
100
189
 
101
- # Using the binary-only methods will raise an error
102
- user.right_answer_for!(word)
103
- => ActiveRecall::IncompatibleAlgorithmError
190
+ user.score!(3, word) # "Good" typical successful recall
191
+ user.score!(1, word) # "Again" — counts as a lapse
104
192
  ```
105
193
 
106
- Reviewing
107
- ---------
194
+ Calling `user.right_answer_for!(word)` while FSRS is configured raises `ActiveRecall::IncompatibleAlgorithmError` — use `score!` instead.
195
+
196
+ ## Reviewing
108
197
 
109
- In addition to an `expired` method, ActiveRecall provides a suggested reviewing sequence for all unknown words in the deck.
110
- Words are randomly chosen from all untested words, failed, and finally expired in order of precedence.
198
+ In addition to the `expired` scope, ActiveRecall provides a suggested reviewing sequence for all unknown words in the deck. Words are randomly chosen from `untested`, `failed`, and `expired` items, in that order of precedence. This works the same for every algorithm.
111
199
 
112
200
  ```ruby
113
201
  user.words.review #=> [word]
@@ -0,0 +1,54 @@
1
+ # Vendored Licenses
2
+
3
+ This file records third-party source code that has been vendored into this
4
+ repository, along with the upstream license terms. ActiveRecall's own license
5
+ is in [LICENSE](LICENSE).
6
+
7
+ ## rb-fsrs
8
+
9
+ - **Vendored at:** [`lib/active_recall/algorithms/fsrs/internal.rb`](lib/active_recall/algorithms/fsrs/internal.rb)
10
+ - **Upstream:** https://github.com/open-spaced-repetition/rb-fsrs
11
+ - **Version:** 0.9.0 (commit pulled from the published gem)
12
+ - **License:** MIT
13
+ - **Reason for vendoring:** The published `fsrs` 0.9.0 gem pins
14
+ `activesupport ~> 7.0`, which excludes Rails 8. ActiveRecall supports
15
+ Rails 8, so the code was vendored under `ActiveRecall::FSRS::Internal`.
16
+ The constraint has been widened on rb-fsrs `master`; revisit the
17
+ dependency-vs-vendoring decision once a release with the wider
18
+ constraint ships.
19
+
20
+ ### Local divergences from upstream
21
+
22
+ - `Scheduler#schedule_new_state` uses `1.minute` / `5.minutes` /
23
+ `10.minutes` in place of upstream's bare integer arithmetic
24
+ (`now + 60`, `now + (5 * 60)`, `now + (10 * 60)`). With `now` as a
25
+ `DateTime`, the upstream form adds days, not seconds, scheduling new
26
+ cards rated Again/Hard/Good 60 / 300 / 600 days out instead of
27
+ 1 / 5 / 10 minutes. Tracks upstream PR
28
+ https://github.com/open-spaced-repetition/rb-fsrs/pull/9.
29
+
30
+ ### Upstream license text
31
+
32
+ ```
33
+ The MIT License (MIT)
34
+
35
+ Copyright (c) 2024 clayton
36
+
37
+ Permission is hereby granted, free of charge, to any person obtaining a copy
38
+ of this software and associated documentation files (the "Software"), to deal
39
+ in the Software without restriction, including without limitation the rights
40
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
41
+ copies of the Software, and to permit persons to whom the Software is
42
+ furnished to do so, subject to the following conditions:
43
+
44
+ The above copyright notice and this permission notice shall be included in
45
+ all copies or substantial portions of the Software.
46
+
47
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
48
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
49
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
50
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
51
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
52
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
53
+ THE SOFTWARE.
54
+ ```
@@ -2,6 +2,7 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
+ gem "nokogiri", ">= 1.16.2"
5
6
  gem "rails", "~> 7.0"
6
7
  gem "sqlite3", "~> 1.4"
7
8
 
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: ..
3
3
  specs:
4
- active_recall (2.1.0)
4
+ active_recall (2.3.0)
5
5
  activerecord (>= 7.0, < 9.0)
6
6
  activesupport (>= 7.0, < 9.0)
7
7
 
@@ -119,6 +119,7 @@ GEM
119
119
  net-smtp
120
120
  marcel (1.0.2)
121
121
  mini_mime (1.1.5)
122
+ mini_portile2 (2.8.8)
122
123
  minitest (5.21.2)
123
124
  mutex_m (0.2.0)
124
125
  net-imap (0.4.9.1)
@@ -131,7 +132,14 @@ GEM
131
132
  net-smtp (0.4.0.1)
132
133
  net-protocol
133
134
  nio4r (2.7.0)
134
- nokogiri (1.16.0-arm64-darwin)
135
+ nokogiri (1.18.7)
136
+ mini_portile2 (~> 2.8.2)
137
+ racc (~> 1.4)
138
+ nokogiri (1.18.7-arm64-darwin)
139
+ racc (~> 1.4)
140
+ nokogiri (1.18.7-x86_64-darwin)
141
+ racc (~> 1.4)
142
+ nokogiri (1.18.7-x86_64-linux-gnu)
135
143
  racc (~> 1.4)
136
144
  parallel (1.24.0)
137
145
  parser (3.3.1.0)
@@ -216,7 +224,8 @@ GEM
216
224
  rubocop-ast (>= 1.30.0, < 2.0)
217
225
  ruby-progressbar (1.13.0)
218
226
  ruby2_keywords (0.0.5)
219
- sqlite3 (1.7.3-arm64-darwin)
227
+ sqlite3 (1.7.3)
228
+ mini_portile2 (~> 2.8.0)
220
229
  standard (1.35.1)
221
230
  language_server-protocol (~> 3.17.0.2)
222
231
  lint_roller (~> 1.0)
@@ -242,11 +251,15 @@ GEM
242
251
  zeitwerk (2.6.12)
243
252
 
244
253
  PLATFORMS
245
- arm64-darwin-23
254
+ arm64-darwin
255
+ ruby
256
+ x86_64-darwin
257
+ x86_64-linux
246
258
 
247
259
  DEPENDENCIES
248
260
  active_recall!
249
261
  appraisal
262
+ nokogiri (>= 1.16.2)
250
263
  rails (~> 7.0)
251
264
  rake (>= 12.0)
252
265
  rdoc
@@ -2,6 +2,7 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
+ gem "nokogiri", ">= 1.16.2"
5
6
  gem "rails", "~> 7.1"
6
7
  gem "sqlite3", "~> 1.4"
7
8