Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions lib/okubo/deck_methods.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@ def deck
d = Okubo::Deck.where(:user_id => self.id, :user_type => self.class.name).first_or_create
d.source_class.module_eval do
def stats
Okubo::Item.first(:conditions => {:source_id => self.id, :source_type => self.class.name})
Okubo::Item.where(source_id: self.id, source_type: self.class.name).first
end
end
d
end

def remove_deck
deck = Okubo::Deck.first(:conditions => {:user_id => self.id, :user_type => self.class.name})
deck = Okubo::Deck.where(user_id: self.id, user_type: self.class.name).first
deck.destroy
end
end
end
end
10 changes: 5 additions & 5 deletions lib/okubo/models/item.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,25 @@ class Item < ActiveRecord::Base
belongs_to :source, :polymorphic => true
scope :untested, lambda{where(["box = ? and last_reviewed is null", 0])}
scope :failed, lambda{where(["box = ? and last_reviewed is not null", 0])}
scope :known, lambda{where(["box > ? and next_review > ?", 0, Time.now])}
scope :expired, lambda{where(["box > ? and next_review <= ?", 0, Time.now])}
scope :known, lambda{where(["box > ? and next_review > ?", 0, Time.current])}
scope :expired, lambda{where(["box > ? and next_review <= ?", 0, Time.current])}

DELAYS = [3, 7, 14, 30, 60, 120, 240]

def right!
self[:box] += 1
self.times_right += 1
self.last_reviewed = Time.now
self.last_reviewed = Time.current
self.next_review = last_reviewed + DELAYS[[DELAYS.count, box].min-1].days
self.save!
end

def wrong!
self[:box] = 0
self.times_wrong += 1
self.last_reviewed = Time.now
self.last_reviewed = Time.current
self.next_review = nil
self.save!
end
end
end
end
4 changes: 2 additions & 2 deletions spec/okubo/base_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@
context "Mixin" do
describe "#has_deck" do
it "should add a decks method with name" do
@user.respond_to?(:words).should be_true
expect(@user).to respond_to(:words)
end
end
end
end
end
4 changes: 2 additions & 2 deletions spec/okubo/deck_methods_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
context "Decks" do
describe "#words" do
it "should return an empty list when empty" do
@user.words.should == []
expect(@user.words).to eq([])
end

it "should allow access of word stats" do
Expand All @@ -19,4 +19,4 @@
end
end
end
end
end
38 changes: 19 additions & 19 deletions spec/okubo/models/deck_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
context "Managing word lists" do
it "should add words to the word list" do
@user.words << @word
@user.words.should == [@word]
expect(@user.words).to eq([@word])
end

it "should be an iterator of words" do
Expand All @@ -19,67 +19,67 @@
@user.words.each do |w|
a << w
end
a.should == [@word]
expect(a).to eq([@word])
end

it "should raise an error if a duplicate word exists" do
@user.words << @word
@user.words.should == [@word]
expect(@user.words).to eq([@word])
expect{@user.words << @word}.to raise_error(ArgumentError)
end

it "should remove words from the word list (but not the model itself)" do
@user.words.delete(@word)
@user.words.include?(@word).should be_false
@word.destroyed?.should be_false
expect(@user.words.include?(@word)).to be false
expect(@word.destroyed?).to be false
end

it "should tell you what word was last added to the deck" do
@user.words << @word
@user.words.last.should == @word
expect(@user.words.last).to eq(@word)
end
end

context "Reviewing" do
it "should return an array of words to review" do
@user.words << @word
@user.words << Word.create!(:kanji => "日本語1", :kana => "にほんご1", :translation => "Japanese language")
@user.words.known.count.should == 0
(@user.words.untested + @user.words.failed + @user.words.expired).sort.should == @user.words.review.sort
expect(@user.words.known.count).to be_zero
expect(@user.words.untested + @user.words.failed + @user.words.expired.sort).to eql(@user.words.review.sort)
end

it "marking words right/wrong" do
@user.words << @word
@user.words << Word.create!(:kanji => "日本語1", :kana => "にほんご1", :translation => "Japanese language")
@user.words.count.should == 2
@user.words.review.count.should == 2
expect(@user.words.count).to eql(2)
expect(@user.words.review.count).to eql(2)
@user.words.review.each_with_index do |word, index|
index.even? ? @user.right_answer_for!(word) : @user.wrong_answer_for!(word)
end
@user.words.review.count.should == 1
expect(@user.words.review.count).to eql(1)
end

it "should allow you to get one word only" do
@user.words << @word
@user.words << Word.create!(:kanji => "日本語1", :kana => "にほんご1", :translation => "Japanese language")
@user.words.known.count.should == 0
expect(@user.words.known.count).to be_zero
word = @user.words.next
@user.words.untested.include?(word).should be_true
expect(@user.words.untested.include?(word)).to be true
@user.right_answer_for!(word)
word = @user.words.next
@user.words.untested.include?(word).should be_true
expect(@user.words.untested.include?(word)).to be true
@user.right_answer_for!(word)
@user.words.next.should be_nil
expect(@user.words.next).to be nil
end
end

context "Cleaning up" do
it "should delete itself and all item information when the source model is deleted" do
deck = @user.words
@user.destroy
Okubo::Deck.exists?(:user_id => @user.id).should be_false
Okubo::Item.exists?(:deck_id => deck.id).should be_false
@word.destroyed?.should be_false
expect(Okubo::Deck.exists?(:user_id => @user.id)).to be false
expect(Okubo::Item.exists?(:deck_id => deck.id)).to be false
expect(@word.destroyed?).to be false
end
end
end
end
78 changes: 40 additions & 38 deletions spec/okubo/models/item_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,69 +10,71 @@

context "Leitner box movement" do
it "should present a list of all words" do
@user.words.should == [@word]
@user.words.count.should == 1
expect(@user.words.to_a).to eql([@word])
expect(@user.words.count).to eql(1)
end

it "should start off in the untested stack" do
@user.words.untested.should == [@word]
expect(@user.words.untested).to eql([@word])
end

it "correct answer should move it up one stack" do
@user.right_answer_for!(@word)
@user.words.untested.should == []
@user.words.box(1).should == [@word]
@user.words.known.should == [@word]
expect(@user.words.untested).to eql([])
expect(@user.words.box(1)).to eql([@word])
expect(@user.words.known).to eql([@word])
end

it "incorrect answer should move it to the failed stack" do
@user.wrong_answer_for!(@word)
@user.words.untested.should == []
@user.words.failed.should == [@word]
@word.stats.times_wrong.should == 1
expect(@user.words.untested).to eql([])
expect(@user.words.failed).to eql([@word])
expect(@word.stats.times_wrong).to eql(1)
end
end

context "Study schedule" do
it "when untested, next study time should be nil" do
@word.stats.next_review.should be_nil
expect(@word.stats.next_review).to be_nil
end

it "when correct, next study time should gradually increase" do
Timecop.freeze(Time.now) do
@user.right_answer_for!(@word)
stats = @word.stats
stats.next_review.should == stats.last_reviewed + 3.days
stats.times_right.should == 1
@user.right_answer_for!(@word)
stats.reload
stats.next_review.should == stats.last_reviewed + 7.days
stats.times_right.should == 2
@user.right_answer_for!(@word)
stats.reload
stats.next_review.should == stats.last_reviewed + 14.days
@user.right_answer_for!(@word)
stats.reload
stats.next_review.should == stats.last_reviewed + 30.days
@user.right_answer_for!(@word)
stats.reload
stats.next_review.should == stats.last_reviewed + 60.days
@user.right_answer_for!(@word)
stats.reload
stats.next_review.should == stats.last_reviewed + 120.days
@user.right_answer_for!(@word)
stats.reload
stats.next_review.should == stats.last_reviewed + 240.days
Time.use_zone('UTC') do
Timecop.freeze(Time.current) do
@user.right_answer_for!(@word)
stats = @word.stats
expect(stats.next_review).to eq(stats.last_reviewed + 3.days)
expect(stats.times_right).to eq(1)
@user.right_answer_for!(@word)
stats.reload
expect(stats.next_review).to eq(stats.last_reviewed + 7.days)
expect(stats.times_right).to eq(2)
@user.right_answer_for!(@word)
stats.reload
expect(stats.next_review).to eq(stats.last_reviewed + 14.days)
@user.right_answer_for!(@word)
stats.reload
expect(stats.next_review).to eq((stats.last_reviewed + 30.days))
@user.right_answer_for!(@word)
stats.reload
expect(stats.next_review).to eq(stats.last_reviewed + 60.days)
@user.right_answer_for!(@word)
stats.reload
expect(stats.next_review).to eq(stats.last_reviewed + 120.days)
@user.right_answer_for!(@word)
stats.reload
expect(stats.next_review).to eq(stats.last_reviewed + 240.days)
end
end
end

it "words should expire and move from known to expired" do
@user.right_answer_for!(@word)
@user.words.known.should == [@word]
expect(@user.words.known).to eq([@word])
Timecop.travel(4.days)
@user.words.known.should == []
@user.words.expired.should == [@word]
expect(@user.words.known).to be_empty
expect(@user.words.expired).to eq([@word])
Timecop.return
end
end
end
end