thinking_lobster 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/lib/thinking_lobster.rb +226 -0
- metadata +158 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 218e6e4196ddcf31ccba598e4245801c6de55daa
|
4
|
+
data.tar.gz: 20036c56a9646cee7d57263929cc5a9e2f0b0c03
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: def04bd5c774f5347cdd63e1c828959edfd62bdfd60e2c082ac68722c2e985638c482f08d1eb47caee437b49351e6627bd4f327d7f1413cf98e3fba364d3b7ba
|
7
|
+
data.tar.gz: 3174c4d377ce067467366860c8c7c7fac47d849efbc374e1801a4464ec6f82686556fb23124401cd7eb5d50d2fc20d1653baa79b75e2aba3b3914ad731ab9af5
|
@@ -0,0 +1,226 @@
|
|
1
|
+
require 'mongoid'
|
2
|
+
require 'active_support/time'
|
3
|
+
|
4
|
+
module ThinkingLobster
|
5
|
+
|
6
|
+
#TODO:
|
7
|
+
# [] Move all fixed numbers into user definable vars. eg. default review_due_at, intervals, etc
|
8
|
+
# [x] Make protected members protected again
|
9
|
+
# [x] Consider implementing a time_since_review method to replace time_since_due
|
10
|
+
# [x] Write documentation
|
11
|
+
# [x] Update the readme.md
|
12
|
+
# [] Github pages for sdoc documentation
|
13
|
+
# [] wiki pages for use cases, design philosophy.
|
14
|
+
# [] Run a code coverage tool
|
15
|
+
|
16
|
+
# Includes a set of fields and support methods into the base class. Mongoid
|
17
|
+
# must be included in the base class.
|
18
|
+
#
|
19
|
+
# ==== Examples
|
20
|
+
# class FlashCard
|
21
|
+
# include Mongoid::Document
|
22
|
+
# include ThinkingLobster
|
23
|
+
# field :question
|
24
|
+
# field :answer
|
25
|
+
# end
|
26
|
+
def self.included(collection)
|
27
|
+
#Make all of the hardcoded 2's a user defined variable.
|
28
|
+
collection.send :field, :times_reviewed, type: Integer, default: 0
|
29
|
+
collection.send :field, :winning_streak, type: Integer, default: 0
|
30
|
+
collection.send :field, :losing_streak, type: Integer, default: 0
|
31
|
+
collection.send :field, :review_due_at, type: Time, default: ->{Time.now}
|
32
|
+
collection.send :field, :previous_review, type: Time
|
33
|
+
collection.send :include, Mongoid::Timestamps
|
34
|
+
end
|
35
|
+
|
36
|
+
# Stores various information about scheduling and other attributes that a developer might wish to tweak.
|
37
|
+
#
|
38
|
+
# * (Float) :cuttoff - A cutoff time (in seconds) that determines wether an item will be considered a short term (new) or long term (old) memory. Default value is `36.hours`. Therefore, by default, all items less than 36 hours old are considered short term.
|
39
|
+
# * (Float) :first_interval - This is a float representing the time between the first and second interval of a new item (in seconds). Default value is `2.hours`.
|
40
|
+
# * (Float) :new_positive_multiplier - The number by which a new item's interval is multiplied by when correctly reviewed. Default value is 2. Increasing this number will shorten the number of short term reviews. Must be greater than 1.0 . Be aware that there is no such thing as a new_negative_multiplier because incorrect new items get reset to the system default when incorrect.
|
41
|
+
# * (Float) :old_positive_multiplier - The number by which an old item's interval is multiplied by when correctly reviewed. Default value is 1.25. Increasing this number will shorten the number of long term item reviews. Must be greater than 1.0 .
|
42
|
+
# * (Float) :Penalty - The number by which an old item's interval is multiplied by when incorrectly answered. Can be any number equal to or greater than 0 or less than 1.0. Default value is 0.25 .
|
43
|
+
|
44
|
+
mattr_accessor :config
|
45
|
+
|
46
|
+
self.config = {
|
47
|
+
cutoff: 36.hours,
|
48
|
+
first_interval: 2.hours,
|
49
|
+
new_positive_multiplier: 2.0,
|
50
|
+
old_positive_multiplier: 1.25,
|
51
|
+
penalty: 0.25
|
52
|
+
}
|
53
|
+
|
54
|
+
# Marks the item correct and increases the item's review intervals accordingly. Takes no action if review takes place before the scheduled review time.
|
55
|
+
#
|
56
|
+
# Example:
|
57
|
+
# flash_card = SomeDocument.new
|
58
|
+
# flash_card.mark_correct! # => #<SomeItem:0x123>
|
59
|
+
#
|
60
|
+
# Paramters:
|
61
|
+
# * (Time) current_time - Time object by which review times are set. This parameter is almost always left to its default value (Time.now) but may be useful for testing or special use cases.
|
62
|
+
#
|
63
|
+
# Returns an instance of the base class
|
64
|
+
def mark_correct!(current_time = Time.now)
|
65
|
+
return self if self.too_soon?(current_time)
|
66
|
+
increment_wins
|
67
|
+
if time_since_review(current_time) < @@config[:cutoff]
|
68
|
+
new_item_correct(current_time)
|
69
|
+
else
|
70
|
+
old_item_correct(current_time)
|
71
|
+
end
|
72
|
+
self.save
|
73
|
+
self
|
74
|
+
end
|
75
|
+
|
76
|
+
# Marks the item incorrect and shortens the review interval.
|
77
|
+
#
|
78
|
+
# Example:
|
79
|
+
# flash_card = SomeDocument.new
|
80
|
+
# flash_card.mark_incorrect! # => #<SomeItem:0x123>
|
81
|
+
#
|
82
|
+
# Parameters:
|
83
|
+
# * (Time) current_time - Time object by which review times are set. Defaults to Time.now This parameter is typically left to its default value but may be useful for testing or special use cases.
|
84
|
+
#
|
85
|
+
# Returns an instance of the base class.
|
86
|
+
def mark_incorrect!(current_time = Time.now)
|
87
|
+
increment_losses
|
88
|
+
if time_since_review(current_time) < @@config[:cutoff]
|
89
|
+
#the Review time after a new item's failure is Time.now
|
90
|
+
new_item_incorrect(current_time)
|
91
|
+
else
|
92
|
+
old_item_incorrect(current_time)
|
93
|
+
end
|
94
|
+
self.save
|
95
|
+
self
|
96
|
+
end
|
97
|
+
|
98
|
+
# Indicates quantity of time since the item became ready for a review. This method is under consideration for deprecation in favor of a method which returns time since the last review.
|
99
|
+
#
|
100
|
+
# Example:
|
101
|
+
# flash_card = SomeDocument.new
|
102
|
+
# flash_card.time_since_due
|
103
|
+
# # => 144000
|
104
|
+
#
|
105
|
+
# * (Time) current_time - Time object which defaults to +Time.now+. This is the time by which the method compares.
|
106
|
+
#
|
107
|
+
# Returns an Integer
|
108
|
+
def time_since_due(current_time = Time.now)
|
109
|
+
current_time - self.review_due_at
|
110
|
+
end
|
111
|
+
|
112
|
+
# Indicates quantity of time since the item was last reviewed review. Be aware that this method is not the same thing as time_since_due().
|
113
|
+
#
|
114
|
+
# Example:
|
115
|
+
# flash_card = SomeDocument.new
|
116
|
+
# flash_card.mark_correct!
|
117
|
+
# # Wait 5 seconds...
|
118
|
+
# flash_card.time_since_review
|
119
|
+
# # => 5.0
|
120
|
+
#
|
121
|
+
# * (Time) current_time - Time object which defaults to +Time.now+. This is the time by which the method compares. Usually there is no need to provide this parameter.
|
122
|
+
#
|
123
|
+
# Returns an Integer
|
124
|
+
def time_since_review(current_time = Time.now)
|
125
|
+
if self.previous_review?
|
126
|
+
return current_time - self.previous_review
|
127
|
+
else
|
128
|
+
return current_time - self.created_at
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
# Indicates if a review would be 'premature' at the indicated time, meaning that the application / user is trying to review a word too often.
|
133
|
+
#
|
134
|
+
# Example:
|
135
|
+
# flash_card = SomeDocument.new
|
136
|
+
# flash_card.mark_correct!
|
137
|
+
# flash_card.too_soon? # In this example, we just finished the review. So it won't be due for a few more hours.
|
138
|
+
# # => false
|
139
|
+
#
|
140
|
+
# * (Time) current_time - Time object which defaults to Time.now. This is the time by which the method compares.
|
141
|
+
#
|
142
|
+
# Returns a Boolean
|
143
|
+
def too_soon?(current_time = Time.now)
|
144
|
+
too_soon = current_time < self.review_due_at
|
145
|
+
if too_soon then true else false end
|
146
|
+
end
|
147
|
+
|
148
|
+
# Indicates if the item has a previous review set (which would indicate wether it is a newly added item).
|
149
|
+
#
|
150
|
+
# Example:
|
151
|
+
# flash_card = SomeDocument.new
|
152
|
+
# flash_card.previous_review?
|
153
|
+
# # => false
|
154
|
+
#
|
155
|
+
# No parameters
|
156
|
+
#
|
157
|
+
# Returns a Boolean
|
158
|
+
def previous_review?
|
159
|
+
self.previous_review != nil
|
160
|
+
end
|
161
|
+
# Resets all attributes related to spaced repetition. You still need to call save on the item to persist.
|
162
|
+
def reset(current_time = Time.now)
|
163
|
+
self.times_reviewed = 0
|
164
|
+
self.winning_streak = 0
|
165
|
+
self.losing_streak = 0
|
166
|
+
self.review_due_at = current_time
|
167
|
+
self.previous_review = nil
|
168
|
+
end
|
169
|
+
|
170
|
+
protected
|
171
|
+
|
172
|
+
def new_item_correct(current_time = Time.now)
|
173
|
+
#Takes the interval between current_time and the time the item was due
|
174
|
+
# And doubles that amount of time.
|
175
|
+
if self.previous_review?
|
176
|
+
self.set_previous_review!
|
177
|
+
self.review_due_at += time_since_review(current_time) * @@config[:new_positive_multiplier]
|
178
|
+
else
|
179
|
+
self.set_previous_review!(current_time)
|
180
|
+
self.review_due_at = current_time + @@config[:first_interval]
|
181
|
+
end
|
182
|
+
|
183
|
+
end
|
184
|
+
|
185
|
+
def new_item_incorrect(current_time = Time.now)
|
186
|
+
old_reviews = self.times_reviewed
|
187
|
+
self.reset(current_time)
|
188
|
+
self.increment_losses
|
189
|
+
self.times_reviewed= old_reviews
|
190
|
+
self.save
|
191
|
+
end
|
192
|
+
|
193
|
+
def old_item_correct(current_time = Time.now)
|
194
|
+
hours_ago = time_since_review(current_time)/60/60
|
195
|
+
new_interval = (hours_ago * @@config[:old_positive_multiplier]).hours
|
196
|
+
self.review_due_at = current_time + new_interval
|
197
|
+
end
|
198
|
+
|
199
|
+
def old_item_incorrect(current_time = Time.now)
|
200
|
+
hours_ago = time_since_review(current_time)/60/60
|
201
|
+
new_interval = (hours_ago * @@config[:penalty]).hours
|
202
|
+
self.review_due_at = current_time + new_interval
|
203
|
+
end
|
204
|
+
|
205
|
+
def increment_wins
|
206
|
+
self.times_reviewed += 1
|
207
|
+
self.winning_streak += 1
|
208
|
+
self.losing_streak = 0
|
209
|
+
end
|
210
|
+
|
211
|
+
def increment_losses
|
212
|
+
self.times_reviewed += 1
|
213
|
+
self.winning_streak = 0
|
214
|
+
self.losing_streak += 1
|
215
|
+
end
|
216
|
+
|
217
|
+
def set_previous_review!(current_time = Time.now)
|
218
|
+
if self.previous_review?
|
219
|
+
self.previous_review = self.review_due_at
|
220
|
+
else
|
221
|
+
#For items that don't have a previous review time.
|
222
|
+
self.previous_review = current_time
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
end
|
metadata
ADDED
@@ -0,0 +1,158 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: thinking_lobster
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Rick Carlino
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2013-10-21 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bson_ext
|
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: mongo
|
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: mongoid
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - '>='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: bson_ext
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - '>='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: mongo
|
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: pry
|
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
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: mongoid
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - '>='
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - '>='
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: sdoc
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - '>='
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - '>='
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
description: Automatically schedules review times for learning material stored in
|
126
|
+
a mongoDB database by way of a spaced repetition algorithm. Resembles the Leitner
|
127
|
+
Method.
|
128
|
+
email: please-use-github@rickcarlino.com
|
129
|
+
executables: []
|
130
|
+
extensions: []
|
131
|
+
extra_rdoc_files: []
|
132
|
+
files:
|
133
|
+
- lib/thinking_lobster.rb
|
134
|
+
homepage: https://github.com/rickcarlino/Thinking-Lobster
|
135
|
+
licenses:
|
136
|
+
- MIT
|
137
|
+
metadata: {}
|
138
|
+
post_install_message:
|
139
|
+
rdoc_options: []
|
140
|
+
require_paths:
|
141
|
+
- lib
|
142
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
143
|
+
requirements:
|
144
|
+
- - '>='
|
145
|
+
- !ruby/object:Gem::Version
|
146
|
+
version: '0'
|
147
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
148
|
+
requirements:
|
149
|
+
- - '>='
|
150
|
+
- !ruby/object:Gem::Version
|
151
|
+
version: '0'
|
152
|
+
requirements: []
|
153
|
+
rubyforge_project:
|
154
|
+
rubygems_version: 2.1.9
|
155
|
+
signing_key:
|
156
|
+
specification_version: 4
|
157
|
+
summary: A leitner based spaced repetition system algorithm for MongoDB.
|
158
|
+
test_files: []
|