acts_as_learnable 0.0.2
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 +7 -0
- data/.gitignore +15 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +71 -0
- data/Rakefile +8 -0
- data/acts_as_learnable.gemspec +28 -0
- data/lib/acts_as_learnable/base.rb +84 -0
- data/lib/acts_as_learnable/version.rb +3 -0
- data/lib/acts_as_learnable.rb +10 -0
- data/lib/generators/acts_as_learnable/USAGE +0 -0
- data/lib/generators/acts_as_learnable/templates/add_fields_migration.rb +0 -0
- data/lib/generators/acts_as_learnable/templates/create_table_migration.rb +0 -0
- data/test/test_base.rb +67 -0
- data/test/test_helper.rb +28 -0
- metadata +147 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 9092717ab1468252ea8a8cf91d265ec3e8027fbd
|
4
|
+
data.tar.gz: f079169cecead415c0f71e22f4b217bd76c592a9
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: a7da136b1d6a9289f4757ec0547adadbff6c4099b2d5182396d63e8a85cd07e8d354c233e3e82a47204cf86d96a013a04e2047111041558bd1714ec8865f2c62
|
7
|
+
data.tar.gz: 3ec4488330cda7f47dd0c46fe8fdfb29963180d4e5341d0bde9bdea58d05fb71ccfa1268aa51153a8784e29ee4901cdc255c4bf2af8cee0a2aaa5ffbd2ec688d
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2015 Danya Kim
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
# ActsAsLearnable
|
2
|
+
|
3
|
+
ActsAsLearnable is a Ruby gem for ActiveRecord models. It provides a simple way to create flashcards in your app. It automatically schedules flashcards for review depending on recall quality (1 to 5). You can easily create a Spaced Repetition System (SRS) using this gem.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
gem 'acts_as_learnable'
|
11
|
+
```
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle
|
16
|
+
|
17
|
+
Or install it yourself as:
|
18
|
+
|
19
|
+
$ gem install acts_as_learnable
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
|
23
|
+
Within your model call `acts_as_learnable` method:
|
24
|
+
|
25
|
+
```ruby
|
26
|
+
class Flashcard < ActiveRecord::Base
|
27
|
+
acts_as_learnable
|
28
|
+
end
|
29
|
+
```
|
30
|
+
|
31
|
+
It will automatically extend your class with all necessary methods.
|
32
|
+
|
33
|
+
Make sure you have the following fields on the model (generators will be added later):
|
34
|
+
|
35
|
+
```ruby
|
36
|
+
ActiveRecord::Schema.define(version: 1) do
|
37
|
+
create_table :flashcards do |t|
|
38
|
+
t.float :easiness_factor, default: 2.5
|
39
|
+
t.integer :repetitions, default: 0
|
40
|
+
t.integer :interval, default: 0
|
41
|
+
t.date :due, default: nil
|
42
|
+
t.date :studied_at, default: nil
|
43
|
+
end
|
44
|
+
end
|
45
|
+
```
|
46
|
+
|
47
|
+
Then you can review items:
|
48
|
+
|
49
|
+
```ruby
|
50
|
+
card = Flashcard.create
|
51
|
+
card.review(4) # => Recall quality from 1 (bad) to 5 (perfect)
|
52
|
+
card.due_today? # => false
|
53
|
+
card.due # => 2015-03-11
|
54
|
+
card.studied_at # => 2015-03-10
|
55
|
+
card.interval # => 1
|
56
|
+
card.repetitions # => 1
|
57
|
+
```
|
58
|
+
|
59
|
+
## Contributing
|
60
|
+
|
61
|
+
1. Fork it ( https://github.com/[my-github-username]/acts_as_learnable/fork )
|
62
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
63
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
64
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
65
|
+
5. Create a new Pull Request
|
66
|
+
|
67
|
+
## TODO
|
68
|
+
|
69
|
+
* Migration generators
|
70
|
+
* More detailed README
|
71
|
+
* Other repetition algorithms (Anki)
|
data/Rakefile
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
lib = File.expand_path('../lib', __FILE__)
|
2
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
|
+
|
4
|
+
require 'acts_as_learnable/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'acts_as_learnable'
|
8
|
+
spec.version = ActsAsLearnable::VERSION
|
9
|
+
spec.authors = ['Danya Kim']
|
10
|
+
spec.email = ["itsdanya@gmail.com"]
|
11
|
+
spec.summary = 'A simple way to create flashcards in your app.'
|
12
|
+
spec.description = 'ActsAsLearnable is a Ruby gem for ActiveRecord models. It provides a simple way to create flashcards in your app. It automatically schedules flashcards for review depending on recall quality (1 to 5). You can easily create a Spaced Repetition System (SRS) using this gem.'
|
13
|
+
spec.homepage = 'http://github.com/itsdan/acts_as_learnable'
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_dependency 'activerecord'
|
22
|
+
spec.add_dependency 'activesupport'
|
23
|
+
|
24
|
+
spec.add_development_dependency 'bundler', "~> 1.7"
|
25
|
+
spec.add_development_dependency 'rake', "~> 10.0"
|
26
|
+
spec.add_development_dependency 'sqlite3'
|
27
|
+
spec.add_development_dependency 'byebug'
|
28
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
module ActsAsLearnable
|
2
|
+
module Base
|
3
|
+
def acts_as_learnable
|
4
|
+
class_eval do
|
5
|
+
include ActsAsLearnable::InstanceMethods
|
6
|
+
end
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
module ClassMethods
|
11
|
+
def acts_as_learnable?
|
12
|
+
true
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
module InstanceMethods
|
17
|
+
def review(recall_quality)
|
18
|
+
fail unless (1..5).include?(recall_quality)
|
19
|
+
|
20
|
+
# If the quality of response was lower than 3, then start repetitions from the beginning
|
21
|
+
reset and return if recall_quality <= 2
|
22
|
+
|
23
|
+
# Calculate new easiness factor
|
24
|
+
update_easiness_factor(recall_quality)
|
25
|
+
|
26
|
+
# Repeat all items that scored below 4
|
27
|
+
if recall_quality == 3
|
28
|
+
self.interval = 0
|
29
|
+
else
|
30
|
+
# Otherwise, increment repetitions count and set new interval
|
31
|
+
self.repetitions += 1
|
32
|
+
update_interval
|
33
|
+
end
|
34
|
+
|
35
|
+
schedule_repetition
|
36
|
+
save
|
37
|
+
end
|
38
|
+
|
39
|
+
def due_today?
|
40
|
+
due.nil? || due <= Date.today
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
# Bad recall quality, start over
|
46
|
+
def reset
|
47
|
+
self.repetitions = 0
|
48
|
+
self.interval = 0
|
49
|
+
self.due = Date.today
|
50
|
+
self.studied_at = Date.today
|
51
|
+
save
|
52
|
+
end
|
53
|
+
|
54
|
+
# Calculates and updates easiness factor
|
55
|
+
def update_easiness_factor(quality)
|
56
|
+
new_easiness_factor = calculate_easiness_factor(quality)
|
57
|
+
# If EF is less than 1.3 then let EF be 1.3
|
58
|
+
self.easiness_factor = new_easiness_factor < 1.3 ? 1.3 : new_easiness_factor
|
59
|
+
end
|
60
|
+
|
61
|
+
# Calculates new easiness factor according to the formula
|
62
|
+
def calculate_easiness_factor(quality)
|
63
|
+
easiness_factor + (0.1 - (5 - quality) * (0.08 + (5 - quality) * 0.02))
|
64
|
+
end
|
65
|
+
|
66
|
+
# Update interval according to the formula
|
67
|
+
def update_interval
|
68
|
+
# TODO: Extract magic numbers
|
69
|
+
self.interval =
|
70
|
+
case repetitions
|
71
|
+
when 1 then 1
|
72
|
+
when 2 then 6
|
73
|
+
else
|
74
|
+
interval(repetitions - 1) * easiness_factor
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
# Update next repetition date, set studied_at to today
|
79
|
+
def schedule_repetition
|
80
|
+
self.due = Date.today + interval
|
81
|
+
self.studied_at = Date.today
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
File without changes
|
File without changes
|
File without changes
|
data/test/test_base.rb
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class BaseTest < MiniTest::Test
|
4
|
+
def setup
|
5
|
+
setup_db
|
6
|
+
@card = Flashcard.create
|
7
|
+
end
|
8
|
+
|
9
|
+
def test_valid
|
10
|
+
assert @card.valid?
|
11
|
+
assert @card.due_today?
|
12
|
+
assert_equal 2.5, @card.easiness_factor
|
13
|
+
assert_equal 0, @card.repetitions
|
14
|
+
assert_equal 0, @card.interval
|
15
|
+
assert_nil @card.due
|
16
|
+
assert_nil @card.studied_at
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_incorrect_response
|
20
|
+
@card.review(1)
|
21
|
+
assert @card.due_today?
|
22
|
+
assert_equal Date.today, @card.due
|
23
|
+
assert_equal 0, @card.repetitions
|
24
|
+
|
25
|
+
@card.review(2)
|
26
|
+
assert @card.due_today?
|
27
|
+
assert_equal Date.today, @card.due
|
28
|
+
assert_equal 0, @card.repetitions
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_correct_response
|
32
|
+
# Recalled with serious difficulty
|
33
|
+
@card.review(3)
|
34
|
+
assert @card.due_today?
|
35
|
+
assert_equal Date.today, @card.due
|
36
|
+
assert_equal 0, @card.repetitions
|
37
|
+
|
38
|
+
# Response after hesitation
|
39
|
+
@card.review(4)
|
40
|
+
refute @card.due_today?
|
41
|
+
assert_equal Date.today + 1, @card.due
|
42
|
+
assert_equal 1, @card.repetitions
|
43
|
+
|
44
|
+
# Perfect response
|
45
|
+
refute @card.due_today?
|
46
|
+
@card.review(5)
|
47
|
+
assert_equal Date.today + 6, @card.due
|
48
|
+
assert_equal 2, @card.repetitions
|
49
|
+
end
|
50
|
+
|
51
|
+
def test_flow
|
52
|
+
# Review two times
|
53
|
+
@card.review(4)
|
54
|
+
@card.review(4)
|
55
|
+
|
56
|
+
assert_equal 2, @card.repetitions
|
57
|
+
assert_equal 6, @card.interval
|
58
|
+
assert_equal Date.today, @card.studied_at
|
59
|
+
assert_equal Date.today + 6, @card.due
|
60
|
+
assert_in_delta @card.easiness_factor, 2.5, 0.01
|
61
|
+
refute @card.due_today?
|
62
|
+
end
|
63
|
+
|
64
|
+
def teardown
|
65
|
+
teardown_db
|
66
|
+
end
|
67
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require 'sqlite3'
|
3
|
+
require 'active_record'
|
4
|
+
require 'acts_as_learnable'
|
5
|
+
|
6
|
+
ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:')
|
7
|
+
|
8
|
+
def setup_db
|
9
|
+
ActiveRecord::Schema.define(version: 1) do
|
10
|
+
create_table :flashcards do |t|
|
11
|
+
t.float :easiness_factor, default: 2.5
|
12
|
+
t.integer :repetitions, default: 0
|
13
|
+
t.integer :interval, default: 0
|
14
|
+
t.date :due, default: nil
|
15
|
+
t.date :studied_at, default: nil
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def teardown_db
|
21
|
+
ActiveRecord::Base.connection.tables.each do |table|
|
22
|
+
ActiveRecord::Base.connection.drop_table(table)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
class Flashcard < ActiveRecord::Base
|
27
|
+
acts_as_learnable
|
28
|
+
end
|
metadata
ADDED
@@ -0,0 +1,147 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: acts_as_learnable
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Danya Kim
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-03-09 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: activerecord
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: activesupport
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: bundler
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1.7'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.7'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rake
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '10.0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '10.0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: sqlite3
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: byebug
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
description: ActsAsLearnable is a Ruby gem for ActiveRecord models. It provides a
|
98
|
+
simple way to create flashcards in your app. It automatically schedules flashcards
|
99
|
+
for review depending on recall quality (1 to 5). You can easily create a Spaced
|
100
|
+
Repetition System (SRS) using this gem.
|
101
|
+
email:
|
102
|
+
- itsdanya@gmail.com
|
103
|
+
executables: []
|
104
|
+
extensions: []
|
105
|
+
extra_rdoc_files: []
|
106
|
+
files:
|
107
|
+
- ".gitignore"
|
108
|
+
- Gemfile
|
109
|
+
- LICENSE.txt
|
110
|
+
- README.md
|
111
|
+
- Rakefile
|
112
|
+
- acts_as_learnable.gemspec
|
113
|
+
- lib/acts_as_learnable.rb
|
114
|
+
- lib/acts_as_learnable/base.rb
|
115
|
+
- lib/acts_as_learnable/version.rb
|
116
|
+
- lib/generators/acts_as_learnable/USAGE
|
117
|
+
- lib/generators/acts_as_learnable/templates/add_fields_migration.rb
|
118
|
+
- lib/generators/acts_as_learnable/templates/create_table_migration.rb
|
119
|
+
- test/test_base.rb
|
120
|
+
- test/test_helper.rb
|
121
|
+
homepage: http://github.com/itsdan/acts_as_learnable
|
122
|
+
licenses:
|
123
|
+
- MIT
|
124
|
+
metadata: {}
|
125
|
+
post_install_message:
|
126
|
+
rdoc_options: []
|
127
|
+
require_paths:
|
128
|
+
- lib
|
129
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
130
|
+
requirements:
|
131
|
+
- - ">="
|
132
|
+
- !ruby/object:Gem::Version
|
133
|
+
version: '0'
|
134
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - ">="
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
139
|
+
requirements: []
|
140
|
+
rubyforge_project:
|
141
|
+
rubygems_version: 2.4.5
|
142
|
+
signing_key:
|
143
|
+
specification_version: 4
|
144
|
+
summary: A simple way to create flashcards in your app.
|
145
|
+
test_files:
|
146
|
+
- test/test_base.rb
|
147
|
+
- test/test_helper.rb
|