forgetful 0.1.0 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,2 @@
1
+ Manifest
2
+ pkg
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2010 Jonathan Palardy
2
+
3
+ Permission is hereby granted, free of charge, to any person
4
+ obtaining a copy of this software and associated documentation
5
+ files (the "Software"), to deal in the Software without
6
+ restriction, including without limitation the rights to use,
7
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the
9
+ Software is furnished to do so, subject to the following
10
+ conditions:
11
+
12
+ The above copyright notice and this permission notice shall be
13
+ included in all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22
+ OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc CHANGED
@@ -1,4 +1,6 @@
1
- = Forgetful
1
+ = forgetful
2
+
3
+ (tested with ree-1.8.7 and 1.9.2dev)
2
4
 
3
5
  A minimal command-line app implementing the SuperMemo 2 algorithm.
4
6
 
@@ -49,26 +51,6 @@ your CSV files under source control to keep a history.
49
51
 
50
52
  Enjoy!
51
53
 
54
+ == Copyright
52
55
 
53
- == License
54
-
55
- Copyright (c) 2008 Jonathan Palardy
56
-
57
- Permission is hereby granted, free of charge, to any person obtaining
58
- a copy of this software and associated documentation files (the
59
- "Software"), to deal in the Software without restriction, including
60
- without limitation the rights to use, copy, modify, merge, publish,
61
- distribute, sublicense, and/or sell copies of the Software, and to
62
- permit persons to whom the Software is furnished to do so, subject to
63
- the following conditions:
64
-
65
- The above copyright notice and this permission notice shall be
66
- included in all copies or substantial portions of the Software.
67
-
68
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
69
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
70
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
71
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
72
- LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
73
- OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
74
- WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
56
+ Copyright (c) 2010 Jonathan Palardy. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,44 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "forgetful"
8
+ gem.summary = "A minimal command-line implementation of the SuperMemo 2 algorithm."
9
+ gem.description = "A minimal command-line implementation of the SuperMemo 2 algorithm."
10
+ gem.email = "jonathan.palardy@gmail.com"
11
+ gem.homepage = "http://github.com/jpalardy/forgetful"
12
+ gem.authors = ["Jonathan Palardy"]
13
+ gem.add_development_dependency "rspec", ">= 1.2.9"
14
+ end
15
+ Jeweler::GemcutterTasks.new
16
+ rescue LoadError
17
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
18
+ end
19
+
20
+ require 'spec/rake/spectask'
21
+ Spec::Rake::SpecTask.new(:spec) do |spec|
22
+ spec.libs << 'lib' << 'spec'
23
+ spec.spec_files = FileList['spec/**/*_spec.rb']
24
+ end
25
+
26
+ Spec::Rake::SpecTask.new(:rcov) do |spec|
27
+ spec.libs << 'lib' << 'spec'
28
+ spec.pattern = 'spec/**/*_spec.rb'
29
+ spec.rcov = true
30
+ end
31
+
32
+ task :spec => :check_dependencies
33
+
34
+ task :default => :spec
35
+
36
+ require 'rake/rdoctask'
37
+ Rake::RDocTask.new do |rdoc|
38
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
39
+
40
+ rdoc.rdoc_dir = 'rdoc'
41
+ rdoc.title = "forgetful #{version}"
42
+ rdoc.rdoc_files.include('README*')
43
+ rdoc.rdoc_files.include('lib/**/*.rb')
44
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.2.1
data/bin/forgetful CHANGED
@@ -1,9 +1,8 @@
1
1
  #!/usr/bin/env ruby
2
- $VERBOSE = true
3
2
 
4
3
  ############################################################
5
4
 
6
- require "#{File.dirname(__FILE__)}/../lib/forgetful"
5
+ require "forgetful"
7
6
 
8
7
  ############################################################
9
8
 
@@ -0,0 +1,50 @@
1
+ require "csv"
2
+ if CSV.const_defined? :Reader
3
+ # Ruby 1.8 compatible
4
+ require 'rubygems'
5
+ require 'fastercsv'
6
+ Object.send(:remove_const, :CSV)
7
+ CSV = FasterCSV
8
+ end
9
+
10
+ class Reminder
11
+ def to_csv
12
+ if history.empty?
13
+ [question, answer, due_on.to_s]
14
+ else
15
+ [question, answer, due_on.to_s, history.join]
16
+ end
17
+ end
18
+
19
+ def self.read_csv(filename)
20
+ File.open(filename) do |file|
21
+ self.parse_csv(file)
22
+ end
23
+ end
24
+
25
+ def self.parse_csv(io)
26
+ converters = [lambda { |question| question },
27
+ lambda { |answer| answer },
28
+ lambda { |due_on| Date.parse(due_on) },
29
+ lambda { |history| history.scan(/./).collect { |x| x.to_i } }]
30
+
31
+ CSV.parse(io, :skip_blanks => true).collect do |list|
32
+ list = list.zip(converters).collect { |col, converter| converter[col] }
33
+ new(*list)
34
+ end
35
+ end
36
+
37
+ def self.write_csv(filename, reminders)
38
+ File.open(filename, "w") do |file|
39
+ file.write(generate_csv(reminders))
40
+ end
41
+ end
42
+
43
+ def self.generate_csv(reminders)
44
+ CSV.generate do |csv|
45
+ reminders.each do |reminder|
46
+ csv << reminder.to_csv
47
+ end
48
+ end
49
+ end
50
+ end
@@ -1,4 +1,3 @@
1
-
2
1
  class Reminder
3
2
  attr_reader :question, :answer, :due_on, :history
4
3
 
@@ -10,20 +9,18 @@ class Reminder
10
9
  end
11
10
 
12
11
  def next(q)
13
- Reminder.new(self.question, self.answer, SuperMemo::next_date(Date.today, self.history + [q]), self.history + [q])
12
+ Reminder.new(question, answer, SuperMemo::next_date(Date.today, history + [q]), history + [q])
14
13
  end
15
14
 
16
15
  def to_a
17
- [self.question, self.answer, self.due_on, self.history]
16
+ [question, answer, due_on, history]
18
17
  end
19
18
 
20
19
  def <=>(other)
21
- self.to_a <=> other.to_a
20
+ to_a <=> other.to_a
22
21
  end
23
22
 
24
23
  def review?
25
- (self.history.last || 0) < 4
24
+ history.empty? || history.last < 4
26
25
  end
27
-
28
26
  end
29
-
@@ -1,6 +1,4 @@
1
-
2
1
  module SuperMemo
3
-
4
2
  def self.next_ef(q, ef)
5
3
  return [ef + (0.1 - (5-q) * (0.08 + (5-q) * 0.02)), 1.3].max
6
4
  end
@@ -13,8 +11,6 @@ module SuperMemo
13
11
  return q < 3 ? 1 : {0 => 1, 1 => 6}.fetch(i, interval * ef).round
14
12
  end
15
13
 
16
- #-------------------------------------------------
17
-
18
14
  # [] -> 2.5, 0, 0
19
15
  # [5,5,5] -> 2.8, 3, 16
20
16
  def self.traverse(qs, ef=2.5, i=0, interval=0)
@@ -28,7 +24,4 @@ module SuperMemo
28
24
  ef, i, interval = traverse(qs)
29
25
  return date + interval
30
26
  end
31
-
32
27
  end
33
-
34
-
data/lib/forgetful.rb CHANGED
@@ -1,5 +1,5 @@
1
1
 
2
- require "#{File.dirname(__FILE__)}/supermemo"
3
- require "#{File.dirname(__FILE__)}/reminder"
4
- require "#{File.dirname(__FILE__)}/csv_ext/reminder"
2
+ require "forgetful/supermemo"
3
+ require "forgetful/reminder"
4
+ require "forgetful/csv_ext/reminder"
5
5
 
@@ -0,0 +1,67 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe "reminder (csv)" do
4
+ describe "parsing the bare format" do
5
+ before do
6
+ csv = <<END
7
+ carbon,6
8
+ nitrogen,7
9
+ oxygen,8
10
+ END
11
+ @reminders = Reminder.parse_csv(csv)
12
+ end
13
+
14
+ it "should have the right number of lines" do
15
+ @reminders.size.should == 3
16
+ end
17
+
18
+ it "should have the right data" do
19
+ @reminders[0].to_a.should == ['carbon', '6',Date.today, []]
20
+ @reminders[1].to_a.should == ['nitrogen','7',Date.today, []]
21
+ @reminders[2].to_a.should == ['oxygen', '8',Date.today, []]
22
+ end
23
+ end
24
+
25
+ describe "parsing the full format" do
26
+ before do
27
+ csv = <<END
28
+ carbon,6,2008-11-11,5
29
+ nitrogen,7,2008-11-11,535
30
+
31
+ oxygen,8,2008-11-11,5042
32
+ END
33
+ @reminders = Reminder.parse_csv(csv)
34
+ end
35
+
36
+ it "should have the right number of lines" do
37
+ @reminders.size.should == 3
38
+ end
39
+
40
+ it "should have the right data" do
41
+ @reminders[0].to_a.should == ['carbon', '6',Date.parse('2008-11-11'), [5]]
42
+ @reminders[1].to_a.should == ['nitrogen','7',Date.parse('2008-11-11'), [5,3,5]]
43
+ @reminders[2].to_a.should == ['oxygen', '8',Date.parse('2008-11-11'), [5,0,4,2]]
44
+ end
45
+ end
46
+
47
+ describe "generating csv" do
48
+ before do
49
+ @reminders = []
50
+ @reminders << Reminder.new('carbon','6')
51
+ @reminders << Reminder.new('nitrogen','7').next(5).next(5).next(5)
52
+ @reminders << Reminder.new('oxygen','8').next(1).next(1).next(1)
53
+ @reminders << Reminder.new('fluorine','9').next(1).next(5).next(1).next(5)
54
+ @reminders << Reminder.new('neon','10',Date.today+5)
55
+ end
56
+
57
+ it "should generate appropriate csv" do
58
+ Reminder.generate_csv(@reminders).should == <<END
59
+ carbon,6,#{Date.today}
60
+ nitrogen,7,#{(Date.today+16)},555
61
+ oxygen,8,#{(Date.today+1)},111
62
+ fluorine,9,#{(Date.today+1)},1515
63
+ neon,10,#{(Date.today+5)}
64
+ END
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,69 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe "reminder" do
4
+ before do
5
+ @question = 'carbon'
6
+ @answer = '6'
7
+ @due_on = Date.today - 2
8
+ @history = [5,5,5]
9
+ end
10
+
11
+ describe "with default constructor" do
12
+ before do
13
+ @reminder = Reminder.new(@question, @answer)
14
+ end
15
+
16
+ it "should be valid" do
17
+ @reminder.to_a.should == [@question, @answer, Date.today, []]
18
+ end
19
+ end
20
+
21
+ describe "with full constructor" do
22
+ before do
23
+ @reminder = Reminder.new(@question, @answer, @due_on, @history)
24
+ end
25
+
26
+ it "should be valid" do
27
+ @reminder.to_a.should == [@question, @answer, @due_on, @history]
28
+ end
29
+
30
+ it "should not allow its history to be modified" do
31
+ error_type = RUBY_VERSION =~ /^1.8/ ? TypeError : RuntimeError
32
+ lambda {
33
+ @reminder.history.push(5)
34
+ }.should raise_error(error_type, "can't modify frozen array")
35
+ end
36
+ end
37
+
38
+ describe "next" do
39
+ before do
40
+ @quality = 5
41
+ @reminder = Reminder.new(@question, @answer, @due_on, @history)
42
+ end
43
+
44
+ it "should create a new reminder scheduled appropriately" do
45
+ @reminder.next(@quality).to_a.should == [@question, @answer, Date.today+45, @history + [@quality]]
46
+ end
47
+ end
48
+
49
+ describe "review (w/o history)" do
50
+ subject { Reminder.new(@question, @answer) }
51
+
52
+ it { should be_review}
53
+ end
54
+
55
+ describe "review (w/ history)" do
56
+ before do
57
+ @reminder = Reminder.new(@question, @answer, @due_on, @history)
58
+ end
59
+
60
+ it "should qualify for review if quality < 4" do
61
+ @reminder.next(0).should be_review
62
+ @reminder.next(1).should be_review
63
+ @reminder.next(2).should be_review
64
+ @reminder.next(3).should be_review
65
+ @reminder.next(4).should_not be_review
66
+ @reminder.next(5).should_not be_review
67
+ end
68
+ end
69
+ end
data/spec/spec.opts ADDED
@@ -0,0 +1 @@
1
+ --color --backtrace
@@ -0,0 +1,8 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
2
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
3
+ require 'forgetful'
4
+ require 'spec'
5
+ require 'spec/autorun'
6
+
7
+ Spec::Runner.configure do |config|
8
+ end
@@ -0,0 +1,95 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe "supermemo" do
4
+ describe "next_i" do
5
+ qs = [5,4,3,2,1,0]
6
+ results = [4,4,4,0,0,0]
7
+
8
+ qs.zip(results).each do |q,result|
9
+ it "-- next_i(#{q}, 3) = #{result}" do
10
+ SuperMemo::next_i(q, 3).should == result
11
+ end
12
+ end
13
+ end
14
+
15
+ describe "next_interval (for i = 0)" do
16
+ qs = [5,4,3,2,1,0]
17
+ results = [1,1,1,1,1,1]
18
+
19
+ qs.zip(results).each do |q,result|
20
+ it "-- next_interval(#{q}, 2.5, 0, 0) = #{result}" do
21
+ SuperMemo::next_interval(q, 2.5, 0, 0).should == result
22
+ end
23
+ end
24
+ end
25
+
26
+ describe "next_interval (for i = 1)" do
27
+ qs = [5,4,3,2,1,0]
28
+ results = [6,6,6,1,1,1]
29
+
30
+ qs.zip(results).each do |q,result|
31
+ it "-- next_interval(#{q}, 2.5, 1, 1) = #{result}" do
32
+ SuperMemo::next_interval(q, 2.5, 1, 1).should == result
33
+ end
34
+ end
35
+ end
36
+
37
+ describe "next_interval (for i > 1)" do
38
+ qs = [5,4,3,2,1,0]
39
+ results = [16,16,16,1,1,1]
40
+
41
+ qs.zip(results).each do |q,result|
42
+ it "-- next_interval(#{q}, 2.7, 2, 6) = #{result}" do
43
+ SuperMemo::next_interval(q, 2.7, 2, 6).should == result
44
+ end
45
+ end
46
+ end
47
+
48
+ describe "next_interval (for changing efs)" do
49
+ efs = [1.3,2.0,2.5,2.6,2.7]
50
+ results = [8,12,15,16,16]
51
+
52
+ efs.zip(results).each do |ef,result|
53
+ it "-- next_interval(5, #{ef}, 2, 6) = #{result}" do
54
+ SuperMemo::next_interval(5, ef, 2, 6).should == result
55
+ end
56
+ end
57
+ end
58
+
59
+ describe "next_ef (normal)" do
60
+ qs = [5,4,3,2,1,0]
61
+ results = [2.6,2.5,2.36,2.18,1.96,1.7]
62
+
63
+ qs.zip(results).each do |q,result|
64
+ it "-- next_ef(q, 2.5) = #{result}" do
65
+ SuperMemo::next_ef(q, 2.5).should be_close(result, 0.00001)
66
+ end
67
+ end
68
+ end
69
+
70
+ describe "next_ef (minimum)" do
71
+ qs = [5,4,3,2,1,0]
72
+ results = [1.4,1.3,1.3,1.3,1.3,1.3]
73
+
74
+ qs.zip(results).each do |q,result|
75
+ it "-- next_ef(q, 1.3) = #{result}" do
76
+ SuperMemo::next_ef(q, 1.3).should be_close(result, 0.00001)
77
+ end
78
+ end
79
+ end
80
+
81
+ describe "traverse" do
82
+ histories = [[], [5], [5,5], [5,5,5], [5,5,5,5]]
83
+ results = [[2.5,0,0], [2.6,1,1], [2.7,2,6], [2.8,3,16], [2.9,4,45]]
84
+
85
+ histories.zip(results).each do |history,result|
86
+ it "-- traverse(#{history.inspect}) = #{result.inspect}" do
87
+ ef, i, interval = SuperMemo::traverse(history)
88
+
89
+ ef.should be_close(result[0], 0.00001)
90
+ i.should == result[1]
91
+ interval.should == result[2]
92
+ end
93
+ end
94
+ end
95
+ end
metadata CHANGED
@@ -1,7 +1,12 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: forgetful
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 2
8
+ - 1
9
+ version: 0.2.1
5
10
  platform: ruby
6
11
  authors:
7
12
  - Jonathan Palardy
@@ -9,22 +14,24 @@ autorequire:
9
14
  bindir: bin
10
15
  cert_chain: []
11
16
 
12
- date: 2008-11-21 00:00:00 -05:00
13
- default_executable:
17
+ date: 2010-05-02 00:00:00 -04:00
18
+ default_executable: forgetful
14
19
  dependencies:
15
20
  - !ruby/object:Gem::Dependency
16
- name: fastercsv
17
- type: :runtime
18
- version_requirement:
19
- version_requirements: !ruby/object:Gem::Requirement
21
+ name: rspec
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
20
25
  requirements:
21
26
  - - ">="
22
27
  - !ruby/object:Gem::Version
23
- version: "0"
24
- - - "="
25
- - !ruby/object:Gem::Version
26
- version: "1.4"
27
- version:
28
+ segments:
29
+ - 1
30
+ - 2
31
+ - 9
32
+ version: 1.2.9
33
+ type: :development
34
+ version_requirements: *id001
28
35
  description: A minimal command-line implementation of the SuperMemo 2 algorithm.
29
36
  email: jonathan.palardy@gmail.com
30
37
  executables:
@@ -32,59 +39,60 @@ executables:
32
39
  extensions: []
33
40
 
34
41
  extra_rdoc_files:
35
- - bin/forgetful
36
- - lib/csv_ext/reminder.rb
37
- - lib/forgetful.rb
38
- - lib/reminder.rb
39
- - lib/supermemo.rb
42
+ - LICENSE
40
43
  - README.rdoc
41
44
  files:
45
+ - .gitignore
46
+ - LICENSE
47
+ - README.rdoc
48
+ - Rakefile
49
+ - VERSION
42
50
  - bin/forgetful
43
51
  - examples/katakana_romanji.csv
44
52
  - examples/romanji_katakana.csv
45
- - lib/csv_ext/reminder.rb
46
53
  - lib/forgetful.rb
47
- - lib/reminder.rb
48
- - lib/supermemo.rb
49
- - Manifest
50
- - MIT-LICENSE
51
- - README.rdoc
52
- - test/test_reminder.rb
53
- - test/test_reminder_csv.rb
54
- - test/test_supermemo.rb
55
- - forgetful.gemspec
56
- has_rdoc: false
54
+ - lib/forgetful/csv_ext/reminder.rb
55
+ - lib/forgetful/reminder.rb
56
+ - lib/forgetful/supermemo.rb
57
+ - spec/reminder_csv_spec.rb
58
+ - spec/reminder_spec.rb
59
+ - spec/spec.opts
60
+ - spec/spec_helper.rb
61
+ - spec/supermemo_spec.rb
62
+ has_rdoc: true
57
63
  homepage: http://github.com/jpalardy/forgetful
64
+ licenses: []
65
+
58
66
  post_install_message:
59
67
  rdoc_options:
60
- - --line-numbers
61
- - --inline-source
62
- - --title
63
- - Forgetful
64
- - --main
65
- - README.rdoc
68
+ - --charset=UTF-8
66
69
  require_paths:
67
70
  - lib
68
71
  required_ruby_version: !ruby/object:Gem::Requirement
72
+ none: false
69
73
  requirements:
70
74
  - - ">="
71
75
  - !ruby/object:Gem::Version
76
+ segments:
77
+ - 0
72
78
  version: "0"
73
- version:
74
79
  required_rubygems_version: !ruby/object:Gem::Requirement
80
+ none: false
75
81
  requirements:
76
82
  - - ">="
77
83
  - !ruby/object:Gem::Version
78
- version: "1.2"
79
- version:
84
+ segments:
85
+ - 0
86
+ version: "0"
80
87
  requirements: []
81
88
 
82
- rubyforge_project: forgetful
83
- rubygems_version: 1.2.0
89
+ rubyforge_project:
90
+ rubygems_version: 1.3.6.1
84
91
  signing_key:
85
- specification_version: 2
92
+ specification_version: 3
86
93
  summary: A minimal command-line implementation of the SuperMemo 2 algorithm.
87
94
  test_files:
88
- - test/test_reminder.rb
89
- - test/test_reminder_csv.rb
90
- - test/test_supermemo.rb
95
+ - spec/reminder_csv_spec.rb
96
+ - spec/reminder_spec.rb
97
+ - spec/spec_helper.rb
98
+ - spec/supermemo_spec.rb
data/MIT-LICENSE DELETED
@@ -1,21 +0,0 @@
1
- Copyright (c) 2008 Jonathan Palardy
2
-
3
- Permission is hereby granted, free of charge, to any person obtaining
4
- a copy of this software and associated documentation files (the
5
- "Software"), to deal in the Software without restriction, including
6
- without limitation the rights to use, copy, modify, merge, publish,
7
- distribute, sublicense, and/or sell copies of the Software, and to
8
- permit persons to whom the Software is furnished to do so, subject to
9
- the following conditions:
10
-
11
- The above copyright notice and this permission notice shall be
12
- included in all copies or substantial portions of the Software.
13
-
14
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
- LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
- OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
- WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
-
data/Manifest DELETED
@@ -1,13 +0,0 @@
1
- bin/forgetful
2
- examples/katakana_romanji.csv
3
- examples/romanji_katakana.csv
4
- lib/csv_ext/reminder.rb
5
- lib/forgetful.rb
6
- lib/reminder.rb
7
- lib/supermemo.rb
8
- Manifest
9
- MIT-LICENSE
10
- README.rdoc
11
- test/test_reminder.rb
12
- test/test_reminder_csv.rb
13
- test/test_supermemo.rb
data/forgetful.gemspec DELETED
@@ -1,34 +0,0 @@
1
- Gem::Specification.new do |s|
2
- s.name = %q{forgetful}
3
- s.version = "0.1.0"
4
-
5
- s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
6
- s.authors = ["Jonathan Palardy"]
7
- s.date = %q{2008-11-21}
8
- s.default_executable = %q{forgetful}
9
- s.description = %q{A minimal command-line implementation of the SuperMemo 2 algorithm.}
10
- s.email = %q{jonathan.palardy@gmail.com}
11
- s.executables = ["forgetful"]
12
- s.extra_rdoc_files = ["bin/forgetful", "lib/csv_ext/reminder.rb", "lib/forgetful.rb", "lib/reminder.rb", "lib/supermemo.rb", "README.rdoc"]
13
- s.files = ["bin/forgetful", "examples/katakana_romanji.csv", "examples/romanji_katakana.csv", "lib/csv_ext/reminder.rb", "lib/forgetful.rb", "lib/reminder.rb", "lib/supermemo.rb", "Manifest", "MIT-LICENSE", "README.rdoc", "test/test_reminder.rb", "test/test_reminder_csv.rb", "test/test_supermemo.rb", "forgetful.gemspec"]
14
- s.homepage = %q{http://github.com/jpalardy/forgetful}
15
- s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Forgetful", "--main", "README.rdoc"]
16
- s.require_paths = ["lib"]
17
- s.rubyforge_project = %q{forgetful}
18
- s.rubygems_version = %q{1.2.0}
19
- s.summary = %q{A minimal command-line implementation of the SuperMemo 2 algorithm.}
20
- s.test_files = ["test/test_reminder.rb", "test/test_reminder_csv.rb", "test/test_supermemo.rb"]
21
-
22
- if s.respond_to? :specification_version then
23
- current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
24
- s.specification_version = 2
25
-
26
- if current_version >= 3 then
27
- s.add_runtime_dependency(%q<fastercsv>, [">= 0", "= 1.4"])
28
- else
29
- s.add_dependency(%q<fastercsv>, [">= 0", "= 1.4"])
30
- end
31
- else
32
- s.add_dependency(%q<fastercsv>, [">= 0", "= 1.4"])
33
- end
34
- end
@@ -1,47 +0,0 @@
1
-
2
- require 'rubygems'
3
- require 'fastercsv'
4
-
5
- class Reminder
6
-
7
- def self.read_csv(filename)
8
- File.open(filename) do |file|
9
- self.parse_csv(file)
10
- end
11
- end
12
-
13
- def self.parse_csv(io)
14
- converters = [lambda {|question| question},
15
- lambda {|answer| answer},
16
- lambda {|due_on| Date.parse(due_on)},
17
- lambda {|history| history.scan(/./).collect {|x| x.to_i}}
18
- ]
19
-
20
- FasterCSV.parse(io, :skip_blanks => true).collect do |list|
21
- list = list.zip(converters).collect {|col, converter| converter[col]}
22
- self.new(*list)
23
- end
24
- end
25
-
26
- ############################################################
27
-
28
- def self.write_csv(filename, reminders)
29
- File.open(filename, "w") do |file|
30
- file.write(generate_csv(reminders))
31
- end
32
- end
33
-
34
- def self.generate_csv(reminders)
35
- FasterCSV.generate do |csv|
36
- reminders.each do |reminder|
37
- if reminder.history.empty?
38
- csv << reminder.to_a.first(3)
39
- else
40
- csv << reminder.to_a
41
- end
42
- end
43
- end
44
- end
45
-
46
- end
47
-
@@ -1,99 +0,0 @@
1
-
2
- require 'test/unit'
3
- require "#{File.dirname(__FILE__)}/../lib/forgetful"
4
-
5
- class TestReminder < Test::Unit::TestCase
6
-
7
- def assert_raise_message(error, re)
8
- exception = assert_raise(error) do
9
- yield
10
- end
11
- assert_match re, exception.message
12
- end
13
-
14
- #-------------------------------------------------
15
-
16
- def setup
17
- @carbon_question = Reminder.new 'carbon', '6'
18
- end
19
-
20
- #-------------------------------------------------
21
-
22
- def test_empty
23
- assert_raise_message ArgumentError, /wrong number of arguments \(0 for 2\)/ do
24
- Reminder.new
25
- end
26
- end
27
-
28
- def test_just_question
29
- assert_raise_message ArgumentError, /wrong number of arguments \(1 for 2\)/ do
30
- Reminder.new 'carbon'
31
- end
32
- end
33
-
34
- #-------------------------------------------------
35
-
36
- def test_minimum
37
- assert_equal 'carbon', @carbon_question.question
38
- assert_equal '6', @carbon_question.answer
39
- assert_equal Date.today, @carbon_question.due_on
40
- assert_equal [], @carbon_question.history
41
- end
42
-
43
- def test_minium_array
44
- assert_equal ['carbon', '6', Date.today, []], @carbon_question.to_a
45
- end
46
-
47
- #-------------------------------------------------
48
-
49
- def test_history_cannot_be_changed
50
- history = [5,4,2]
51
-
52
- reminder = Reminder.new('carbon','6',Date.today, history)
53
-
54
- assert_equal [5,4,2], reminder.history
55
-
56
- assert_raise_message TypeError, /can't modify frozen array/ do
57
- @carbon_question.history.push(5)
58
- end
59
-
60
- history.push(5)
61
-
62
- assert_equal [], @carbon_question.history
63
- end
64
-
65
- #-------------------------------------------------
66
-
67
- def test_next_clamped
68
- assert_equal ['carbon', '6', Date.today+1, [1]], @carbon_question.next(1).to_a
69
- assert_equal ['carbon', '6', Date.today+1, [4]], @carbon_question.next(4).to_a
70
- end
71
-
72
- def test_next_unclamped
73
- reminder = Reminder.new('carbon', '6', Date.today, [5,5])
74
-
75
- assert_equal ['carbon', '6', Date.today+1, [5,5,1]], reminder.next(1).to_a
76
- assert_equal ['carbon', '6', Date.today+16, [5,5,4]], reminder.next(4).to_a
77
- end
78
-
79
- #-------------------------------------------------
80
-
81
- def test_review
82
- assert_equal true, @carbon_question.review?
83
- assert_equal true, @carbon_question.next(0).review?
84
- assert_equal true, @carbon_question.next(1).review?
85
- assert_equal true, @carbon_question.next(2).review?
86
- assert_equal true, @carbon_question.next(3).review?
87
- assert_equal false, @carbon_question.next(4).review?
88
- assert_equal false, @carbon_question.next(5).review?
89
-
90
- assert_equal true, @carbon_question.next(5).next(0).review?
91
- assert_equal true, @carbon_question.next(5).next(1).review?
92
- assert_equal true, @carbon_question.next(5).next(2).review?
93
- assert_equal true, @carbon_question.next(5).next(3).review?
94
- assert_equal false, @carbon_question.next(5).next(4).review?
95
- assert_equal false, @carbon_question.next(5).next(5).review?
96
- end
97
-
98
- end
99
-
@@ -1,97 +0,0 @@
1
-
2
- require 'test/unit'
3
- require "#{File.dirname(__FILE__)}/../lib/forgetful"
4
-
5
- class TestReminderCSV < Test::Unit::TestCase
6
-
7
- def test_parse_csv_bare
8
- csv = <<END
9
- carbon,6
10
- nitrogen,7
11
- oxygen,8
12
- END
13
-
14
- reminders = Reminder.parse_csv(csv)
15
-
16
- assert_equal 3, reminders.length
17
-
18
- assert_equal ['carbon', '6',Date.today, []], reminders[0].to_a
19
- assert_equal ['nitrogen','7',Date.today, []], reminders[1].to_a
20
- assert_equal ['oxygen', '8',Date.today, []], reminders[2].to_a
21
- end
22
-
23
- def test_parse_csv_with_dates
24
- csv = <<END
25
- carbon,6,2008-11-11
26
- nitrogen,7,2008-11-11
27
- oxygen,8,2008-11-11
28
- END
29
-
30
- reminders = Reminder.parse_csv(csv)
31
-
32
- assert_equal 3, reminders.length
33
-
34
- assert_equal ['carbon', '6',Date.parse('2008-11-11'), []], reminders[0].to_a
35
- assert_equal ['nitrogen','7',Date.parse('2008-11-11'), []], reminders[1].to_a
36
- assert_equal ['oxygen', '8',Date.parse('2008-11-11'), []], reminders[2].to_a
37
- end
38
-
39
- def test_parse_csv_full
40
- csv = <<END
41
- carbon,6,2008-11-11,5
42
- nitrogen,7,2008-11-11,535
43
- oxygen,8,2008-11-11,5042
44
- END
45
-
46
- reminders = Reminder.parse_csv(csv)
47
-
48
- assert_equal 3, reminders.length
49
-
50
- assert_equal ['carbon', '6',Date.parse('2008-11-11'), [5]], reminders[0].to_a
51
- assert_equal ['nitrogen','7',Date.parse('2008-11-11'), [5,3,5]], reminders[1].to_a
52
- assert_equal ['oxygen', '8',Date.parse('2008-11-11'), [5,0,4,2]], reminders[2].to_a
53
- end
54
-
55
- def test_parse_csv_full_with_spaces
56
- csv = <<END
57
-
58
- carbon,6,2008-11-11,035
59
-
60
-
61
- nitrogen,7,2008-11-11,55555
62
- oxygen,8,2008-11-11,1
63
-
64
- END
65
-
66
- reminders = Reminder.parse_csv(csv)
67
-
68
- assert_equal 3, reminders.length
69
-
70
- assert_equal ['carbon', '6',Date.parse('2008-11-11'), [0,3,5]], reminders[0].to_a
71
- assert_equal ['nitrogen','7',Date.parse('2008-11-11'), [5,5,5,5,5]], reminders[1].to_a
72
- assert_equal ['oxygen', '8',Date.parse('2008-11-11'), [1]], reminders[2].to_a
73
- end
74
-
75
- ############################################################
76
-
77
- def test_generate_csv
78
- reminders = []
79
- reminders << Reminder.new('carbon','6')
80
- reminders << Reminder.new('nitrogen','7').next(5).next(5).next(5)
81
- reminders << Reminder.new('oxygen','8').next(1).next(1).next(1)
82
- reminders << Reminder.new('fluorine','9').next(1).next(5).next(1).next(5)
83
- reminders << Reminder.new('neon','10',Date.today+5)
84
-
85
- expected =<<END
86
- carbon,6,#{Date.today}
87
- nitrogen,7,#{(Date.today+16)},555
88
- oxygen,8,#{(Date.today+1)},111
89
- fluorine,9,#{(Date.today+1)},1515
90
- neon,10,#{(Date.today+5)}
91
- END
92
-
93
- assert_equal expected, Reminder.generate_csv(reminders)
94
- end
95
-
96
- end
97
-
@@ -1,85 +0,0 @@
1
-
2
- require 'test/unit'
3
- require "#{File.dirname(__FILE__)}/../lib/forgetful"
4
-
5
- class TestReminder < Test::Unit::TestCase
6
-
7
- def test_next_i
8
- qs = [5,4,3,2,1,0]
9
- results = [4,4,4,0,0,0]
10
-
11
- qs.zip(results).each do |q,result|
12
- assert_equal result, SuperMemo::next_i(q, 3), "next_i for q=#{q} when i=3"
13
- end
14
- end
15
-
16
- def test_next_interval_normal
17
- qs = [5,4,3,2,1,0]
18
- results = [16,16,16,1,1,1]
19
-
20
- qs.zip(results).each do |q,result|
21
- assert_equal result, SuperMemo::next_interval(q, 2.7, 2, 6), "next_interval for q=#{q}"
22
- end
23
- end
24
-
25
- def test_next_interval_for_i0
26
- qs = [5,4,3,2,1,0]
27
- results = [1,1,1,1,1,1]
28
-
29
- qs.zip(results).each do |q,result|
30
- assert_equal result, SuperMemo::next_interval(q, 2.5, 0, 0), "next_interval for q=#{q}"
31
- end
32
- end
33
-
34
- def test_next_interval_for_i1
35
- qs = [5,4,3,2,1,0]
36
- results = [6,6,6,1,1,1]
37
-
38
- qs.zip(results).each do |q,result|
39
- assert_equal result, SuperMemo::next_interval(q, 2.5, 1, 1), "next_interval for q=#{q}"
40
- end
41
- end
42
-
43
- def test_next_interval_for_changing_efs
44
- efs = [1.3, 2.0, 2.5, 2.6, 2.7]
45
- results = [8,12,15,16,16]
46
-
47
- efs.zip(results).each do |ef,result|
48
- assert_equal result, SuperMemo::next_interval(5, ef, 2, 6), "next_interval for ef=#{ef}"
49
- end
50
- end
51
-
52
- def test_next_ef_normal
53
- qs = [5,4,3,2,1,0]
54
- results = [2.6,2.5,2.36,2.18,1.96,1.7]
55
-
56
- qs.zip(results).each do |q,result|
57
- assert_in_delta result, SuperMemo::next_ef(q, 2.5), 0.00001, "next_ef for q=#{q}"
58
- end
59
- end
60
-
61
- def test_next_ef_minimum
62
- qs = [5,4,3,2,1,0]
63
- results = [1.4,1.3,1.3,1.3,1.3,1.3]
64
-
65
- qs.zip(results).each do |q,result|
66
- assert_in_delta result, SuperMemo::next_ef(q, 1.3), 0.00001, "next_ef for q=#{q}"
67
- end
68
- end
69
-
70
- #-------------------------------------------------
71
-
72
- def assert_equal_inspect(expected, actual)
73
- assert_equal expected.inspect, actual.inspect
74
- end
75
-
76
- def test_traverse
77
- assert_equal_inspect [2.5, 0, 0], SuperMemo::traverse([])
78
- assert_equal_inspect [2.6, 1, 1], SuperMemo::traverse([5])
79
- assert_equal_inspect [2.7, 2, 6], SuperMemo::traverse([5,5])
80
- assert_equal_inspect [2.8, 3, 16], SuperMemo::traverse([5,5,5])
81
- assert_equal_inspect [2.9, 4, 45], SuperMemo::traverse([5,5,5,5])
82
- end
83
-
84
- end
85
-