forgetful 0.1.0 → 0.2.1

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.
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
-