thinking_lobster 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (3) hide show
  1. checksums.yaml +7 -0
  2. data/lib/thinking_lobster.rb +226 -0
  3. 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: []