active_recall 1.1.0 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6f26bb57ebefa4026475f96a4e9df6051990de46bb131ff38f6a8cb5536d30b1
4
- data.tar.gz: 439d4544a42ebfd704686ea81bcc7f32242ef3e4828772fed2cc77843d014d8c
3
+ metadata.gz: 4569fb2091dedb5e74e5b52969bccda5a63dafcce224d794d2c9f1685a943a87
4
+ data.tar.gz: 3efab95a63e1c777e1b4e20c436752490b18f12394a8e1e01380e2ee04fadefd
5
5
  SHA512:
6
- metadata.gz: 6eade4d6980383d43077857517aaec84b287953567860b7c3f9c5b0ba414f3ed9d1ac43c2e4747147f2b7689edcbf8daa7e5c6829b8b1ed90f56b74bfa560987
7
- data.tar.gz: 6d77b4755a8eef49319d17b3f2d0e285dca85ad53c51553f9783761b7c8ec0cd1cde86ec69170d0efecefb8a39f893c04a35ba81eab42cb72542689ca9f82578
6
+ metadata.gz: bff87655e7cd1968ae406995a500651500cd5997205adb026da4f3fe454cdbbdf2f0954cf8259cbe02c0c21045dcc8aa479cc2b2a7ab51f41a971669cbc6bcdd
7
+ data.tar.gz: 7e04ae7f856dfc2b1f3e86d560329a443726c4d163f5b0f80518385fc847fa3f593baea926632bc9ba5d951c890a23375861a0bafb877374202bdb83a944804e
@@ -0,0 +1,46 @@
1
+ name: Tests
2
+
3
+ on:
4
+ pull_request:
5
+ branches:
6
+ - master
7
+ push:
8
+ branches:
9
+ - master
10
+
11
+ jobs:
12
+ unit_tests:
13
+ name: Unit Tests
14
+ if: "contains(github.event.commits[0].message, '[ci skip]') == false"
15
+ strategy:
16
+ fail-fast: false
17
+ matrix:
18
+ os:
19
+ - macos
20
+ - ubuntu
21
+ ruby:
22
+ - 2.5
23
+ - 2.6
24
+ - 2.7
25
+ - 3.0
26
+ allow_failures:
27
+ - false
28
+ include:
29
+ - os: ubuntu
30
+ ruby: ruby-head
31
+ allow_failures: true
32
+ env:
33
+ BUNDLE_GEMFILE: "${{ matrix.gemfile }}"
34
+ ALLOW_FAILURES: "${{ matrix.allow_failures }}"
35
+ runs-on: ${{ matrix.os }}-latest
36
+ continue-on-error: ${{ endsWith(matrix.ruby, 'head') || matrix.ruby == 'debug' }}
37
+ steps:
38
+ - name: Checkout
39
+ uses: actions/checkout@v2
40
+ - name: Setup Ruby
41
+ uses: ruby/setup-ruby@v1
42
+ with:
43
+ ruby-version: ${{ matrix.ruby }}
44
+ bundler-cache: true
45
+ - name: Test
46
+ run: bundle exec rake spec || $ALLOW_FAILURES
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- 2.5.3
1
+ 3.0.0
data/Gemfile CHANGED
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  source 'https://rubygems.org'
4
- ruby '2.5.3'
5
4
 
6
5
  git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
7
6
 
data/Gemfile.lock CHANGED
@@ -1,30 +1,30 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- active_recall (1.1.0)
5
- activerecord (>= 5.2.3)
6
- activesupport (>= 5.2.3)
4
+ active_recall (1.4.0)
5
+ activerecord (>= 5.2.3, <= 6.1)
6
+ activesupport (>= 5.2.3, <= 6.1)
7
7
 
8
8
  GEM
9
9
  remote: https://rubygems.org/
10
10
  specs:
11
- activemodel (6.0.0)
12
- activesupport (= 6.0.0)
13
- activerecord (6.0.0)
14
- activemodel (= 6.0.0)
15
- activesupport (= 6.0.0)
16
- activesupport (6.0.0)
11
+ activemodel (6.1.0)
12
+ activesupport (= 6.1.0)
13
+ activerecord (6.1.0)
14
+ activemodel (= 6.1.0)
15
+ activesupport (= 6.1.0)
16
+ activesupport (6.1.0)
17
17
  concurrent-ruby (~> 1.0, >= 1.0.2)
18
- i18n (>= 0.7, < 2)
19
- minitest (~> 5.1)
20
- tzinfo (~> 1.1)
21
- zeitwerk (~> 2.1, >= 2.1.8)
22
- concurrent-ruby (1.1.5)
18
+ i18n (>= 1.6, < 2)
19
+ minitest (>= 5.1)
20
+ tzinfo (~> 2.0)
21
+ zeitwerk (~> 2.3)
22
+ concurrent-ruby (1.1.8)
23
23
  diff-lcs (1.3)
24
- i18n (1.6.0)
24
+ i18n (1.8.9)
25
25
  concurrent-ruby (~> 1.0)
26
- minitest (5.11.3)
27
- rake (10.5.0)
26
+ minitest (5.14.3)
27
+ rake (12.3.3)
28
28
  rdoc (6.2.0)
29
29
  rspec (3.8.0)
30
30
  rspec-core (~> 3.8.0)
@@ -39,25 +39,20 @@ GEM
39
39
  diff-lcs (>= 1.2.0, < 2.0)
40
40
  rspec-support (~> 3.8.0)
41
41
  rspec-support (3.8.2)
42
- sqlite3 (1.4.1)
43
- thread_safe (0.3.6)
44
- tzinfo (1.2.5)
45
- thread_safe (~> 0.1)
46
- zeitwerk (2.1.10)
42
+ sqlite3 (1.4.2)
43
+ tzinfo (2.0.4)
44
+ concurrent-ruby (~> 1.0)
45
+ zeitwerk (2.4.2)
47
46
 
48
47
  PLATFORMS
49
48
  ruby
50
49
 
51
50
  DEPENDENCIES
52
51
  active_recall!
53
- bundler (>= 1.16)
54
- rake (~> 10.0)
52
+ rake (~> 12.0)
55
53
  rdoc
56
54
  rspec (~> 3.0)
57
55
  sqlite3
58
56
 
59
- RUBY VERSION
60
- ruby 2.5.3p105
61
-
62
57
  BUNDLED WITH
63
- 1.17.3
58
+ 2.2.3
data/README.md CHANGED
@@ -1,8 +1,8 @@
1
- # ActiveRecall [![Build Status](https://travis-ci.org/jaysonvirissimo/active_recall.svg?branch=master)](https://travis-ci.org/jaysonvirissimo/active_recall)
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
4
  It it based on, and is intended to be backwards compatible with, the [okubo](https://github.com/rgravina/okubo) gem.
5
- The primary differentiating features are that it lets the user specify the scheduling algorithm and is fully compatible with Rails 6.
5
+ The primary differentiating features are that it lets the user specify the scheduling algorithm and is fully compatible with Rails 6.0.
6
6
 
7
7
  ## Installation
8
8
 
@@ -29,9 +29,15 @@ Or install it yourself as:
29
29
  $ gem install active_recall
30
30
 
31
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
+ For Rails applications, try doing this from within an [initializer file](https://guides.rubyonrails.org/configuring.html#using-initializer-files).
32
39
 
33
- Assume you have an application allowing your users to study words in a foreign language. Using the <code>has_deck</code> method
34
- you can set up a deck of flashcards that the user will study:
40
+ 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:
35
41
 
36
42
  ```ruby
37
43
  class Word < ActiveRecord::Base
@@ -48,7 +54,7 @@ word = Word.create!(:kanji => "日本語", :kana => "にほんご", :translation
48
54
  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:
49
55
 
50
56
  ```ruby
51
- # Initally adding a word
57
+ # Initially adding a word
52
58
  user.words << word
53
59
  user.words.untested #=> [word]
54
60
 
@@ -64,7 +70,7 @@ user.words.failed #=> [word]
64
70
  user.words #=> [word]
65
71
  ```
66
72
 
67
- As time passes words need to be reviewed to keep them fresh in memory:
73
+ As time passes, words need to be reviewed to keep them fresh in memory:
68
74
 
69
75
  ```ruby
70
76
  # Three days later...
@@ -72,7 +78,7 @@ user.words.known #=> []
72
78
  user.words.expired #=> [word]
73
79
  ```
74
80
 
75
- Guessing a word correcly several times in a row results in the word taking longer to expire, and demonstrates mastery of that word.
81
+ Guessing a word correctly several times in a row results in the word taking longer to expire, and demonstrates mastery of that word.
76
82
 
77
83
  ```ruby
78
84
  user.right_answer_for!(word)
@@ -89,7 +95,7 @@ user.words.expired #=> [word]
89
95
  Reviewing
90
96
  ---------
91
97
 
92
- In addition to an <code>expired</code> method, ActiveRecall provides a suggested reviewing sequence for all unknown words in the deck.
98
+ In addition to an `expired` method, ActiveRecall provides a suggested reviewing sequence for all unknown words in the deck.
93
99
  Words are randomly chosen from all untested words, failed, and finally expired in order of precedence.
94
100
 
95
101
  ```ruby
@@ -32,11 +32,10 @@ Gem::Specification.new do |spec|
32
32
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
33
33
  spec.require_paths = ['lib']
34
34
 
35
- spec.add_development_dependency 'bundler', '>= 1.16'
36
- spec.add_development_dependency 'rake', '~> 10.0'
35
+ spec.add_development_dependency 'rake', '~> 12.0'
37
36
  spec.add_development_dependency 'rdoc'
38
37
  spec.add_development_dependency 'rspec', '~> 3.0'
39
38
  spec.add_development_dependency 'sqlite3'
40
- spec.add_runtime_dependency 'activerecord', '>= 5.2.3'
41
- spec.add_runtime_dependency 'activesupport', '>= 5.2.3'
39
+ spec.add_runtime_dependency 'activerecord', '>= 5.2.3', '<= 6.1'
40
+ spec.add_runtime_dependency 'activesupport', '>= 5.2.3', '<= 6.1'
42
41
  end
data/lib/active_recall.rb CHANGED
@@ -3,9 +3,29 @@
3
3
  require 'active_recall/base'
4
4
  require 'active_recall/deck_methods'
5
5
  require 'active_recall/item_methods'
6
+ require 'active_recall/algorithms/fibonacci_sequence'
6
7
  require 'active_recall/algorithms/leitner_system'
8
+ require 'active_recall/configuration'
7
9
  require 'active_recall/models/deck'
8
10
  require 'active_recall/models/item'
9
11
  require 'active_recall/version'
10
12
 
11
- ActiveRecord::Base.send(:include, ActiveRecall::Base)
13
+ ActiveRecord::Base.include ActiveRecall::Base
14
+
15
+ module ActiveRecall
16
+ class << self
17
+ attr_writer :configuration
18
+ end
19
+
20
+ def self.configure
21
+ yield(configuration)
22
+ end
23
+
24
+ def self.configuration
25
+ @configuration ||= Configuration.new
26
+ end
27
+
28
+ def self.reset
29
+ @configuration = Configuration.new
30
+ end
31
+ end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecall
4
+ class FibonacciSequence
5
+ def self.right(box:, times_right:, times_wrong:, current_time: Time.current)
6
+ new(
7
+ box: box,
8
+ current_time: current_time,
9
+ times_right: times_right,
10
+ times_wrong: times_wrong
11
+ ).right
12
+ end
13
+
14
+ def self.wrong(box:, times_right:, times_wrong:, current_time: Time.current)
15
+ new(
16
+ box: box,
17
+ current_time: current_time,
18
+ times_right: times_right,
19
+ times_wrong: times_wrong
20
+ ).wrong
21
+ end
22
+
23
+ def initialize(box:, times_right:, times_wrong:, current_time: Time.current)
24
+ @box = box
25
+ @current_time = current_time
26
+ @times_right = times_right
27
+ @times_wrong = times_wrong
28
+ end
29
+
30
+ def right
31
+ {
32
+ box: box + 1,
33
+ last_reviewed: current_time,
34
+ next_review: next_review,
35
+ times_right: times_right + 1,
36
+ times_wrong: times_wrong
37
+ }
38
+ end
39
+
40
+ def wrong
41
+ {
42
+ box: [0, box - 1].max,
43
+ last_reviewed: current_time,
44
+ next_review: nil,
45
+ times_right: times_right,
46
+ times_wrong: times_wrong + 1
47
+ }
48
+ end
49
+
50
+ private
51
+
52
+ attr_reader :box, :current_time, :times_right, :times_wrong
53
+
54
+ def fibonacci_number_at(index)
55
+ if (0..1).cover?(index)
56
+ index
57
+ else
58
+ fibonacci_number_at(index - 1) + fibonacci_number_at(index - 2)
59
+ end
60
+ end
61
+
62
+ def next_review
63
+ current_time + fibonacci_number_at(box + 1).days
64
+ end
65
+ end
66
+ end
@@ -4,7 +4,7 @@ module ActiveRecall
4
4
  class LeitnerSystem
5
5
  DELAYS = [3, 7, 14, 30, 60, 120, 240].freeze
6
6
 
7
- def self.right(box:, current_time: Time.current, times_right:, times_wrong:)
7
+ def self.right(box:, times_right:, times_wrong:, current_time: Time.current)
8
8
  new(
9
9
  box: box,
10
10
  current_time: current_time,
@@ -13,7 +13,7 @@ module ActiveRecall
13
13
  ).right
14
14
  end
15
15
 
16
- def self.wrong(box:, current_time: Time.current, times_right:, times_wrong:)
16
+ def self.wrong(box:, times_right:, times_wrong:, current_time: Time.current)
17
17
  new(
18
18
  box: box,
19
19
  current_time: current_time,
@@ -22,7 +22,7 @@ module ActiveRecall
22
22
  ).wrong
23
23
  end
24
24
 
25
- def initialize(box:, current_time: Time.current, times_right:, times_wrong:)
25
+ def initialize(box:, times_right:, times_wrong:, current_time: Time.current)
26
26
  @box = box
27
27
  @current_time = current_time
28
28
  @times_right = times_right
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecall
4
+ class Configuration
5
+ attr_accessor :algorithm_class
6
+
7
+ def initialize
8
+ @algorithm_class = LeitnerSystem
9
+ end
10
+ end
11
+ end
@@ -13,8 +13,9 @@ module ActiveRecall
13
13
  end
14
14
 
15
15
  def remove_deck
16
- deck = ActiveRecall::Deck.where(user_id: id, user_type: self.class.name).first
17
- deck.destroy
16
+ ActiveRecall::Deck
17
+ .where(user_id: id, user_type: self.class.name)
18
+ .destroy_all
18
19
  end
19
20
  end
20
21
  end
@@ -3,13 +3,13 @@
3
3
  module ActiveRecall
4
4
  module ItemMethods
5
5
  def right_answer_for!(item)
6
- i = deck.items.where(source_id: item.id).first
6
+ i = deck.items.find_by(source_id: item.id)
7
7
  i.right!
8
8
  i.save!
9
9
  end
10
10
 
11
11
  def wrong_answer_for!(item)
12
- i = deck.items.where(source_id: item.id).first
12
+ i = deck.items.find_by(source_id: item.id)
13
13
  i.wrong!
14
14
  i.save!
15
15
  end
@@ -3,10 +3,11 @@
3
3
  module ActiveRecall
4
4
  class Item < ActiveRecord::Base
5
5
  self.table_name = 'active_recall_items'
6
+
6
7
  belongs_to :deck
7
- belongs_to :source, polymorphic: true
8
- scope :untested, -> { where(['box = ? and last_reviewed is null', 0]) }
8
+
9
9
  scope :failed, -> { where(['box = ? and last_reviewed is not null', 0]) }
10
+ scope :untested, -> { where(['box = ? and last_reviewed is null', 0]) }
10
11
 
11
12
  def self.expired(current_time: Time.current)
12
13
  where(['box > ? and next_review <= ?', 0, current_time])
@@ -16,16 +17,24 @@ module ActiveRecall
16
17
  where(['box > ? and next_review > ?', 0, current_time])
17
18
  end
18
19
 
20
+ def source
21
+ source_type.constantize.find(source_id)
22
+ end
23
+
19
24
  def right!
20
- update!(LeitnerSystem.right(scoring_attributes))
25
+ update!(algorithm_class.right(**scoring_attributes))
21
26
  end
22
27
 
23
28
  def wrong!
24
- update!(LeitnerSystem.wrong(scoring_attributes))
29
+ update!(algorithm_class.wrong(**scoring_attributes))
25
30
  end
26
31
 
27
32
  private
28
33
 
34
+ def algorithm_class
35
+ ActiveRecall.configuration.algorithm_class
36
+ end
37
+
29
38
  def scoring_attributes
30
39
  attributes.symbolize_keys.slice(:box, :times_right, :times_wrong)
31
40
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActiveRecall
4
- VERSION = '1.1.0'
4
+ VERSION = '1.4.0'
5
5
  end
@@ -20,9 +20,7 @@ class ActiveRecallGenerator < Rails::Generators::Base
20
20
  def create_migration_files
21
21
  create_migration_file_if_not_exist 'create_active_recall_tables'
22
22
  create_migration_file_if_not_exist 'add_active_recall_item_answer_counts'
23
- if options['migrate_data']
24
- create_migration_file_if_not_exist 'migrate_okubo_to_active_recall'
25
- end
23
+ create_migration_file_if_not_exist 'migrate_okubo_to_active_recall' if options['migrate_data']
26
24
  end
27
25
 
28
26
  private
metadata CHANGED
@@ -1,44 +1,30 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: active_recall
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 1.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Robert Gravina
8
8
  - Jayson Virissimo
9
- autorequire:
9
+ autorequire:
10
10
  bindir: exe
11
11
  cert_chain: []
12
- date: 2019-09-17 00:00:00.000000000 Z
12
+ date: 2021-02-19 00:00:00.000000000 Z
13
13
  dependencies:
14
- - !ruby/object:Gem::Dependency
15
- name: bundler
16
- requirement: !ruby/object:Gem::Requirement
17
- requirements:
18
- - - ">="
19
- - !ruby/object:Gem::Version
20
- version: '1.16'
21
- type: :development
22
- prerelease: false
23
- version_requirements: !ruby/object:Gem::Requirement
24
- requirements:
25
- - - ">="
26
- - !ruby/object:Gem::Version
27
- version: '1.16'
28
14
  - !ruby/object:Gem::Dependency
29
15
  name: rake
30
16
  requirement: !ruby/object:Gem::Requirement
31
17
  requirements:
32
18
  - - "~>"
33
19
  - !ruby/object:Gem::Version
34
- version: '10.0'
20
+ version: '12.0'
35
21
  type: :development
36
22
  prerelease: false
37
23
  version_requirements: !ruby/object:Gem::Requirement
38
24
  requirements:
39
25
  - - "~>"
40
26
  - !ruby/object:Gem::Version
41
- version: '10.0'
27
+ version: '12.0'
42
28
  - !ruby/object:Gem::Dependency
43
29
  name: rdoc
44
30
  requirement: !ruby/object:Gem::Requirement
@@ -88,6 +74,9 @@ dependencies:
88
74
  - - ">="
89
75
  - !ruby/object:Gem::Version
90
76
  version: 5.2.3
77
+ - - "<="
78
+ - !ruby/object:Gem::Version
79
+ version: '6.1'
91
80
  type: :runtime
92
81
  prerelease: false
93
82
  version_requirements: !ruby/object:Gem::Requirement
@@ -95,6 +84,9 @@ dependencies:
95
84
  - - ">="
96
85
  - !ruby/object:Gem::Version
97
86
  version: 5.2.3
87
+ - - "<="
88
+ - !ruby/object:Gem::Version
89
+ version: '6.1'
98
90
  - !ruby/object:Gem::Dependency
99
91
  name: activesupport
100
92
  requirement: !ruby/object:Gem::Requirement
@@ -102,6 +94,9 @@ dependencies:
102
94
  - - ">="
103
95
  - !ruby/object:Gem::Version
104
96
  version: 5.2.3
97
+ - - "<="
98
+ - !ruby/object:Gem::Version
99
+ version: '6.1'
105
100
  type: :runtime
106
101
  prerelease: false
107
102
  version_requirements: !ruby/object:Gem::Requirement
@@ -109,6 +104,9 @@ dependencies:
109
104
  - - ">="
110
105
  - !ruby/object:Gem::Version
111
106
  version: 5.2.3
107
+ - - "<="
108
+ - !ruby/object:Gem::Version
109
+ version: '6.1'
112
110
  description: A spaced-repetition system to be used with ActiveRecord models
113
111
  email:
114
112
  - robert.gravina@gmail.com
@@ -117,10 +115,10 @@ executables: []
117
115
  extensions: []
118
116
  extra_rdoc_files: []
119
117
  files:
118
+ - ".github/workflows/tests.yml"
120
119
  - ".gitignore"
121
120
  - ".rspec"
122
121
  - ".ruby-version"
123
- - ".travis.yml"
124
122
  - Gemfile
125
123
  - Gemfile.lock
126
124
  - LICENSE
@@ -130,8 +128,10 @@ files:
130
128
  - bin/console
131
129
  - bin/setup
132
130
  - lib/active_recall.rb
131
+ - lib/active_recall/algorithms/fibonacci_sequence.rb
133
132
  - lib/active_recall/algorithms/leitner_system.rb
134
133
  - lib/active_recall/base.rb
134
+ - lib/active_recall/configuration.rb
135
135
  - lib/active_recall/deck_methods.rb
136
136
  - lib/active_recall/item_methods.rb
137
137
  - lib/active_recall/models/deck.rb
@@ -146,7 +146,7 @@ licenses:
146
146
  - MIT
147
147
  metadata:
148
148
  allowed_push_host: https://rubygems.org/
149
- post_install_message:
149
+ post_install_message:
150
150
  rdoc_options: []
151
151
  require_paths:
152
152
  - lib
@@ -161,9 +161,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
161
161
  - !ruby/object:Gem::Version
162
162
  version: '0'
163
163
  requirements: []
164
- rubyforge_project:
165
- rubygems_version: 2.7.6
166
- signing_key:
164
+ rubygems_version: 3.2.3
165
+ signing_key:
167
166
  specification_version: 4
168
167
  summary: A spaced-repetition system
169
168
  test_files: []
data/.travis.yml DELETED
@@ -1,5 +0,0 @@
1
- language: ruby
2
- cache: bundler
3
- rvm:
4
- - 2.5.3
5
- before_install: gem install bundler -v 1.17.3