anki-importer 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.project +12 -0
- data/.rspec +1 -0
- data/Gemfile +10 -0
- data/Gemfile.lock +34 -0
- data/LICENSE.txt +20 -0
- data/README.rdoc +19 -0
- data/Rakefile +55 -0
- data/VERSION +1 -0
- data/anki-importer.gemspec +95 -0
- data/lib/anki/importer/card_model.rb +98 -0
- data/lib/anki/importer/deck.rb +88 -0
- data/lib/anki/importer/fact.rb +59 -0
- data/lib/anki/importer/field.rb +57 -0
- data/lib/anki/importer/model.rb +92 -0
- data/lib/anki/importer.rb +11 -0
- data/spec/anki_importer_spec.rb +152 -0
- data/spec/fixtures/deck.anki +0 -0
- data/spec/spec_helper.rb +10 -0
- metadata +276 -0
data/.document
ADDED
data/.project
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<projectDescription>
|
3
|
+
<name>anki-import</name>
|
4
|
+
<comment></comment>
|
5
|
+
<projects>
|
6
|
+
</projects>
|
7
|
+
<buildSpec>
|
8
|
+
</buildSpec>
|
9
|
+
<natures>
|
10
|
+
<nature>org.radrails.rails.core.railsnature</nature>
|
11
|
+
</natures>
|
12
|
+
</projectDescription>
|
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/Gemfile
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
source "http://rubygems.org"
|
2
|
+
gem 'activesupport', '>= 3.0.0', :require => ['active_support/core_ext']
|
3
|
+
gem 'sqlite3-ruby', '>= 1.3.1', :require => 'sqlite3'
|
4
|
+
|
5
|
+
group :development do
|
6
|
+
gem "rspec", "~> 2.0.0"
|
7
|
+
gem "bundler", "~> 1.0.0"
|
8
|
+
gem "jeweler", "~> 1.5.0.pre5"
|
9
|
+
gem "rcov", ">= 0"
|
10
|
+
end
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://rubygems.org/
|
3
|
+
specs:
|
4
|
+
activesupport (3.0.1)
|
5
|
+
diff-lcs (1.1.2)
|
6
|
+
git (1.2.5)
|
7
|
+
jeweler (1.5.0.pre5)
|
8
|
+
bundler (~> 1.0.0)
|
9
|
+
git (>= 1.2.5)
|
10
|
+
rake
|
11
|
+
rake (0.8.7)
|
12
|
+
rcov (0.9.9)
|
13
|
+
rspec (2.0.0)
|
14
|
+
rspec-core (= 2.0.0)
|
15
|
+
rspec-expectations (= 2.0.0)
|
16
|
+
rspec-mocks (= 2.0.0)
|
17
|
+
rspec-core (2.0.0)
|
18
|
+
rspec-expectations (2.0.0)
|
19
|
+
diff-lcs (>= 1.1.2)
|
20
|
+
rspec-mocks (2.0.0)
|
21
|
+
rspec-core (= 2.0.0)
|
22
|
+
rspec-expectations (= 2.0.0)
|
23
|
+
sqlite3-ruby (1.3.1)
|
24
|
+
|
25
|
+
PLATFORMS
|
26
|
+
ruby
|
27
|
+
|
28
|
+
DEPENDENCIES
|
29
|
+
activesupport (>= 3.0.0)
|
30
|
+
bundler (~> 1.0.0)
|
31
|
+
jeweler (~> 1.5.0.pre5)
|
32
|
+
rcov
|
33
|
+
rspec (~> 2.0.0)
|
34
|
+
sqlite3-ruby (>= 1.3.1)
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2010 Victor Costan
|
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.
|
data/README.rdoc
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
= anki_importer
|
2
|
+
|
3
|
+
Imports data from Anki deck databases.
|
4
|
+
|
5
|
+
== Contributing to anki_importer
|
6
|
+
|
7
|
+
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
|
8
|
+
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
|
9
|
+
* Fork the project
|
10
|
+
* Start a feature/bugfix branch
|
11
|
+
* Commit and push until you are happy with your contribution
|
12
|
+
* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
|
13
|
+
* Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
|
14
|
+
|
15
|
+
== Copyright
|
16
|
+
|
17
|
+
Copyright (c) 2010 Victor Costan. See LICENSE.txt for
|
18
|
+
further details.
|
19
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler'
|
3
|
+
begin
|
4
|
+
Bundler.setup(:default, :development)
|
5
|
+
rescue Bundler::BundlerError => e
|
6
|
+
$stderr.puts e.message
|
7
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
8
|
+
exit e.status_code
|
9
|
+
end
|
10
|
+
require 'rake'
|
11
|
+
|
12
|
+
require 'jeweler'
|
13
|
+
Jeweler::Tasks.new do |gem|
|
14
|
+
# gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
|
15
|
+
gem.name = "anki-importer"
|
16
|
+
gem.summary = %Q{Importer for Anki decks}
|
17
|
+
gem.description = %Q{Extracts models, facts and cards from Anki deck databases.}
|
18
|
+
gem.email = "victor@costan.us"
|
19
|
+
gem.homepage = "http://github.com/pwnall/anki_importer"
|
20
|
+
gem.authors = ["Victor Costan"]
|
21
|
+
# Include your dependencies below. Runtime dependencies are required when using your gem,
|
22
|
+
# and development dependencies are only needed for development (ie running rake tasks, tests, etc)
|
23
|
+
# spec.add_runtime_dependency 'jabber4r', '> 0.1'
|
24
|
+
# spec.add_development_dependency 'rspec', '> 1.2.3'
|
25
|
+
gem.add_runtime_dependency 'sqlite3-ruby', '>= 1.3.1'
|
26
|
+
gem.add_runtime_dependency 'activesupport', '>= 3.0.0'
|
27
|
+
gem.add_development_dependency "rspec", "~> 2.0.0"
|
28
|
+
gem.add_development_dependency "bundler", "~> 1.0.0"
|
29
|
+
gem.add_development_dependency "jeweler", "~> 1.5.0.pre5"
|
30
|
+
gem.add_development_dependency "rcov", ">= 0"
|
31
|
+
end
|
32
|
+
Jeweler::RubygemsDotOrgTasks.new
|
33
|
+
|
34
|
+
require 'rspec/core'
|
35
|
+
require 'rspec/core/rake_task'
|
36
|
+
RSpec::Core::RakeTask.new(:spec) do |spec|
|
37
|
+
spec.pattern = FileList['spec/**/*_spec.rb']
|
38
|
+
end
|
39
|
+
|
40
|
+
RSpec::Core::RakeTask.new(:rcov) do |spec|
|
41
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
42
|
+
spec.rcov = true
|
43
|
+
end
|
44
|
+
|
45
|
+
task :default => :spec
|
46
|
+
|
47
|
+
require 'rake/rdoctask'
|
48
|
+
Rake::RDocTask.new do |rdoc|
|
49
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
50
|
+
|
51
|
+
rdoc.rdoc_dir = 'rdoc'
|
52
|
+
rdoc.title = "anki-importer #{version}"
|
53
|
+
rdoc.rdoc_files.include('README*')
|
54
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
55
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.1
|
@@ -0,0 +1,95 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{anki-importer}
|
8
|
+
s.version = "0.1.1"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Victor Costan"]
|
12
|
+
s.date = %q{2010-10-26}
|
13
|
+
s.description = %q{Extracts models, facts and cards from Anki deck databases.}
|
14
|
+
s.email = %q{victor@costan.us}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE.txt",
|
17
|
+
"README.rdoc"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
".document",
|
21
|
+
".project",
|
22
|
+
".rspec",
|
23
|
+
"Gemfile",
|
24
|
+
"Gemfile.lock",
|
25
|
+
"LICENSE.txt",
|
26
|
+
"README.rdoc",
|
27
|
+
"Rakefile",
|
28
|
+
"VERSION",
|
29
|
+
"anki-importer.gemspec",
|
30
|
+
"lib/anki/importer.rb",
|
31
|
+
"lib/anki/importer/card_model.rb",
|
32
|
+
"lib/anki/importer/deck.rb",
|
33
|
+
"lib/anki/importer/fact.rb",
|
34
|
+
"lib/anki/importer/field.rb",
|
35
|
+
"lib/anki/importer/model.rb",
|
36
|
+
"spec/anki_importer_spec.rb",
|
37
|
+
"spec/fixtures/deck.anki",
|
38
|
+
"spec/spec_helper.rb"
|
39
|
+
]
|
40
|
+
s.homepage = %q{http://github.com/pwnall/anki_importer}
|
41
|
+
s.require_paths = ["lib"]
|
42
|
+
s.rubygems_version = %q{1.3.7}
|
43
|
+
s.summary = %q{Importer for Anki decks}
|
44
|
+
s.test_files = [
|
45
|
+
"spec/anki_importer_spec.rb",
|
46
|
+
"spec/spec_helper.rb"
|
47
|
+
]
|
48
|
+
|
49
|
+
if s.respond_to? :specification_version then
|
50
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
51
|
+
s.specification_version = 3
|
52
|
+
|
53
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
54
|
+
s.add_runtime_dependency(%q<activesupport>, [">= 3.0.0"])
|
55
|
+
s.add_runtime_dependency(%q<sqlite3-ruby>, [">= 1.3.1"])
|
56
|
+
s.add_development_dependency(%q<rspec>, ["~> 2.0.0"])
|
57
|
+
s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
|
58
|
+
s.add_development_dependency(%q<jeweler>, ["~> 1.5.0.pre5"])
|
59
|
+
s.add_development_dependency(%q<rcov>, [">= 0"])
|
60
|
+
s.add_runtime_dependency(%q<sqlite3-ruby>, [">= 1.3.1"])
|
61
|
+
s.add_runtime_dependency(%q<activesupport>, [">= 3.0.0"])
|
62
|
+
s.add_development_dependency(%q<rspec>, ["~> 2.0.0"])
|
63
|
+
s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
|
64
|
+
s.add_development_dependency(%q<jeweler>, ["~> 1.5.0.pre5"])
|
65
|
+
s.add_development_dependency(%q<rcov>, [">= 0"])
|
66
|
+
else
|
67
|
+
s.add_dependency(%q<activesupport>, [">= 3.0.0"])
|
68
|
+
s.add_dependency(%q<sqlite3-ruby>, [">= 1.3.1"])
|
69
|
+
s.add_dependency(%q<rspec>, ["~> 2.0.0"])
|
70
|
+
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
71
|
+
s.add_dependency(%q<jeweler>, ["~> 1.5.0.pre5"])
|
72
|
+
s.add_dependency(%q<rcov>, [">= 0"])
|
73
|
+
s.add_dependency(%q<sqlite3-ruby>, [">= 1.3.1"])
|
74
|
+
s.add_dependency(%q<activesupport>, [">= 3.0.0"])
|
75
|
+
s.add_dependency(%q<rspec>, ["~> 2.0.0"])
|
76
|
+
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
77
|
+
s.add_dependency(%q<jeweler>, ["~> 1.5.0.pre5"])
|
78
|
+
s.add_dependency(%q<rcov>, [">= 0"])
|
79
|
+
end
|
80
|
+
else
|
81
|
+
s.add_dependency(%q<activesupport>, [">= 3.0.0"])
|
82
|
+
s.add_dependency(%q<sqlite3-ruby>, [">= 1.3.1"])
|
83
|
+
s.add_dependency(%q<rspec>, ["~> 2.0.0"])
|
84
|
+
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
85
|
+
s.add_dependency(%q<jeweler>, ["~> 1.5.0.pre5"])
|
86
|
+
s.add_dependency(%q<rcov>, [">= 0"])
|
87
|
+
s.add_dependency(%q<sqlite3-ruby>, [">= 1.3.1"])
|
88
|
+
s.add_dependency(%q<activesupport>, [">= 3.0.0"])
|
89
|
+
s.add_dependency(%q<rspec>, ["~> 2.0.0"])
|
90
|
+
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
91
|
+
s.add_dependency(%q<jeweler>, ["~> 1.5.0.pre5"])
|
92
|
+
s.add_dependency(%q<rcov>, [">= 0"])
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
@@ -0,0 +1,98 @@
|
|
1
|
+
# :nodoc: namespace
|
2
|
+
module Anki
|
3
|
+
# :nodoc: namespace
|
4
|
+
module Importer
|
5
|
+
|
6
|
+
# Schema for Anki cards.
|
7
|
+
#
|
8
|
+
# Each model has multiple card models, and card is generated by exactly one
|
9
|
+
# model, from one fact.
|
10
|
+
class CardModel
|
11
|
+
# Name assigned in the Anki UI.
|
12
|
+
attr_reader :name
|
13
|
+
# Generally empty.
|
14
|
+
attr_reader :description
|
15
|
+
|
16
|
+
# True if the model is cards are generated based on this model.
|
17
|
+
attr_reader :active
|
18
|
+
|
19
|
+
# Shown during the query stage and maybe during the answer stage.
|
20
|
+
#
|
21
|
+
# %(field_name)s inlines field values.
|
22
|
+
attr_reader :question_template
|
23
|
+
|
24
|
+
# Shown during the answer stage.
|
25
|
+
#
|
26
|
+
# %(field_name)s inlines field values. This isn't necessarily related what the
|
27
|
+
# user's answer is verified against.
|
28
|
+
attr_reader :answer_template
|
29
|
+
|
30
|
+
# Hash with the following keys: :font_family, :font_size, :color, :text_align.
|
31
|
+
attr_reader :question_style
|
32
|
+
|
33
|
+
# Hash with the following keys: :font_family, :font_size, :color, :text_align.
|
34
|
+
attr_reader :answer_style
|
35
|
+
|
36
|
+
# If true, the question is showed in the answer stage.
|
37
|
+
#
|
38
|
+
# Otherwise, the answer presumably contains the question.
|
39
|
+
attr_reader :question_in_answer
|
40
|
+
|
41
|
+
# The field that the user's answer is checked against.
|
42
|
+
#
|
43
|
+
# If this is true, the user is asked to type an answer in the query stage, and
|
44
|
+
# their answer is checked against the field value here.
|
45
|
+
attr_reader :answer_field
|
46
|
+
|
47
|
+
# Unique ID in the fields table.
|
48
|
+
attr_reader :anki_id
|
49
|
+
# The model that this field belongs to.
|
50
|
+
attr_reader :model
|
51
|
+
|
52
|
+
# Reads the card models from an Anki deck.
|
53
|
+
#
|
54
|
+
# Args:
|
55
|
+
# deck_db:: a Sqlite3::Datbase
|
56
|
+
# deck: the (under construction) Anki::Importer::Deck for deck_db
|
57
|
+
#
|
58
|
+
# Returns an array of Field instances.
|
59
|
+
def self.from_db(deck_db, deck)
|
60
|
+
query = 'SELECT id, modelId, name, description, active, qformat, aformat, questionInAnswer, questionFontFamily, questionFontSize, questionFontColour, questionAlign, answerFontFamily, answerFontSize, answerFontColour, answerAlign, typeAnswer FROM cardModels ORDER BY ordinal'
|
61
|
+
models = deck_db.execute(query).map do |anki_id, model_id, name,
|
62
|
+
description, active, question_template, answer_template,
|
63
|
+
question_in_answer, question_font_family, question_font_size,
|
64
|
+
question_font_color, question_align, answer_font_family,
|
65
|
+
answer_font_size, answer_font_color, answer_align, answer_field_name|
|
66
|
+
|
67
|
+
model = deck.models_by_id[model_id]
|
68
|
+
question_style = { :font_family => question_font_family,
|
69
|
+
:font_size => question_font_size, :color => question_font_color,
|
70
|
+
:text_align => question_align }
|
71
|
+
answer_style = { :font_family => answer_font_family,
|
72
|
+
:font_size => answer_font_size, :color => answer_font_color,
|
73
|
+
:text_align => answer_align }
|
74
|
+
answer_field = model.fields.find { |f| f.name == answer_field_name }
|
75
|
+
|
76
|
+
self.new anki_id, model, name, description, active == 1,
|
77
|
+
question_template, answer_template, question_style, answer_style,
|
78
|
+
answer_field
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
# :nodoc: private
|
83
|
+
def initialize(anki_id, model, name, description, active, question_template,
|
84
|
+
answer_template, question_style, answer_style, answer_field)
|
85
|
+
@anki_id = anki_id
|
86
|
+
@model = model
|
87
|
+
@name = name
|
88
|
+
@description = description
|
89
|
+
@active = active
|
90
|
+
@question_template = question_template
|
91
|
+
@answer_template = answer_template
|
92
|
+
@question_style = question_style
|
93
|
+
@answer_style = answer_style
|
94
|
+
@answer_field = answer_field
|
95
|
+
end
|
96
|
+
end # class Anki::Importer::CardModel
|
97
|
+
end # namespace Anki::Importer
|
98
|
+
end # namespace Anki
|
@@ -0,0 +1,88 @@
|
|
1
|
+
# :nodoc: namespace
|
2
|
+
module Anki
|
3
|
+
# :nodoc: namespace
|
4
|
+
module Importer
|
5
|
+
|
6
|
+
# The root of the Anki object graph.
|
7
|
+
class Deck
|
8
|
+
# All the facts (associations) in the deck.
|
9
|
+
attr_reader :facts
|
10
|
+
# All the models in the deck.
|
11
|
+
attr_reader :models
|
12
|
+
# All the fields (fact schema elements) in the deck.
|
13
|
+
attr_reader :fields
|
14
|
+
# All the card models (schemas for cards) in the deck.
|
15
|
+
attr_reader :card_models
|
16
|
+
|
17
|
+
# Reads an Anki deck database.
|
18
|
+
#
|
19
|
+
# Args:
|
20
|
+
# deck_path:: path to an Anki deck on the filesystem
|
21
|
+
#
|
22
|
+
# Returns a deck.
|
23
|
+
def self.from_file(deck_path)
|
24
|
+
deck_db = SQLite3::Database.new deck_path
|
25
|
+
from_db deck_db
|
26
|
+
end
|
27
|
+
|
28
|
+
# Reads an Anki deck database.
|
29
|
+
#
|
30
|
+
# Args:
|
31
|
+
# deck_db:: a Sqlite3::Datbase
|
32
|
+
#
|
33
|
+
# Returns a deck.
|
34
|
+
def self.from_db(deck_db)
|
35
|
+
deck = self.new
|
36
|
+
deck.models = Model.from_db deck_db
|
37
|
+
deck.fields = Field.from_db deck_db, deck
|
38
|
+
deck.fields.each { |field| field.model.add_field field }
|
39
|
+
deck.card_models = CardModel.from_db deck_db, deck
|
40
|
+
deck.card_models.each { |cmodel| cmodel.model.add_card_model cmodel }
|
41
|
+
deck.facts = Fact.from_db deck_db, deck
|
42
|
+
deck.facts.each { |fact| fact.model.add_fact fact }
|
43
|
+
|
44
|
+
deck
|
45
|
+
end
|
46
|
+
|
47
|
+
# :nodoc: private
|
48
|
+
def initialize
|
49
|
+
@models_by_id = {}
|
50
|
+
@fields_by_id = {}
|
51
|
+
@card_models_by_id = {}
|
52
|
+
@facts_by_id = {}
|
53
|
+
end
|
54
|
+
|
55
|
+
# :nodoc: private
|
56
|
+
def models=(new_models)
|
57
|
+
@models = new_models
|
58
|
+
@models_by_id = new_models.index_by(&:anki_id)
|
59
|
+
end
|
60
|
+
# :nodoc: private
|
61
|
+
attr_reader :models_by_id
|
62
|
+
|
63
|
+
# :nodoc: private
|
64
|
+
def fields=(new_fields)
|
65
|
+
@fields = new_fields
|
66
|
+
@fields_by_id = new_fields.index_by(&:anki_id)
|
67
|
+
end
|
68
|
+
# :nodoc: private
|
69
|
+
attr_reader :fields_by_id
|
70
|
+
|
71
|
+
# :nodoc: private
|
72
|
+
def card_models=(new_card_models)
|
73
|
+
@card_models = new_card_models
|
74
|
+
@card_models_by_id = new_card_models.index_by(&:anki_id)
|
75
|
+
end
|
76
|
+
# :nodoc: private
|
77
|
+
attr_reader :card_models_by_id
|
78
|
+
|
79
|
+
# :nodoc: private
|
80
|
+
def facts=(new_facts)
|
81
|
+
@facts = new_facts
|
82
|
+
@facts_by_id = new_facts.index_by(&:anki_id)
|
83
|
+
end
|
84
|
+
# :nodoc: private
|
85
|
+
attr_reader :facts_by_id
|
86
|
+
end # class Anki::Importer::Deck
|
87
|
+
end # namespace Anki::Importer
|
88
|
+
end # namespace Anki
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# :nodoc: namespace
|
2
|
+
module Anki
|
3
|
+
# :nodoc: namespace
|
4
|
+
module Importer
|
5
|
+
|
6
|
+
# An association tested by flash cards.
|
7
|
+
class Fact
|
8
|
+
# Assigned in the Anki UI. Space-separated string.
|
9
|
+
attr_reader :tags
|
10
|
+
# Fact creation time.
|
11
|
+
attr_reader :created_at
|
12
|
+
# Fact modification time.
|
13
|
+
attr_reader :modified_at
|
14
|
+
|
15
|
+
# Maps Fields with their values.
|
16
|
+
attr_reader :fields
|
17
|
+
|
18
|
+
# Unique ID in the facts table.
|
19
|
+
attr_reader :anki_id
|
20
|
+
# The model that this field belongs to.
|
21
|
+
attr_reader :model
|
22
|
+
|
23
|
+
# Reads the models from an Anki deck.
|
24
|
+
#
|
25
|
+
# Args:
|
26
|
+
# deck_db:: a Sqlite3::Datbase
|
27
|
+
# deck: the (under construction) Anki::Importer::Deck for deck_db
|
28
|
+
#
|
29
|
+
# Returns an array of Field instances.
|
30
|
+
def self.from_db(deck_db, deck)
|
31
|
+
fact_query = 'SELECT id, modelId, tags, created, modified FROM facts'
|
32
|
+
facts = deck_db.execute(fact_query).map do |anki_id, model_id, tags,
|
33
|
+
t_created, t_modified|
|
34
|
+
self.new anki_id, deck.models_by_id[model_id], tags, Time.at(t_created),
|
35
|
+
Time.at(t_modified)
|
36
|
+
end
|
37
|
+
facts_by_id = facts.index_by(&:anki_id)
|
38
|
+
|
39
|
+
field = 'SELECT factId, fieldModelId, value FROM fields'
|
40
|
+
deck_db.execute(field) do |fact_id, field_model_id, value|
|
41
|
+
fact = facts_by_id[fact_id]
|
42
|
+
field = deck.fields_by_id[field_model_id]
|
43
|
+
fact.fields[field] = value
|
44
|
+
end
|
45
|
+
facts
|
46
|
+
end
|
47
|
+
|
48
|
+
# :nodoc: private
|
49
|
+
def initialize(anki_id, model, tags, created_at, modified_at)
|
50
|
+
@anki_id = anki_id
|
51
|
+
@model = model
|
52
|
+
@tags = tags
|
53
|
+
@created_at = created_at
|
54
|
+
@modified_at = modified_at
|
55
|
+
@fields = {}
|
56
|
+
end
|
57
|
+
end # class Anki::Importer::Field
|
58
|
+
end # namespace Anki::Importer
|
59
|
+
end # namespace Anki
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# :nodoc: namespace
|
2
|
+
module Anki
|
3
|
+
# :nodoc: namespace
|
4
|
+
module Importer
|
5
|
+
|
6
|
+
# Schema element for Anki facts.
|
7
|
+
#
|
8
|
+
# Each model has multiple fields. Each fact obeys the schema set by fields.
|
9
|
+
class Field
|
10
|
+
# Name assigned in the Anki UI.
|
11
|
+
attr_reader :name
|
12
|
+
# Generally empty.
|
13
|
+
attr_reader :description
|
14
|
+
|
15
|
+
# True if no empty field values are allowed.
|
16
|
+
attr_reader :required
|
17
|
+
# True if all facts must have unique values for the field.
|
18
|
+
attr_reader :unique
|
19
|
+
# True if the field is a sort key (number to sort the cards by).
|
20
|
+
attr_reader :numeric
|
21
|
+
|
22
|
+
# Unique ID in the fields table.
|
23
|
+
attr_reader :anki_id
|
24
|
+
# The model that this field belongs to.
|
25
|
+
attr_reader :model
|
26
|
+
|
27
|
+
# Reads the fields from an Anki deck.
|
28
|
+
#
|
29
|
+
# Args:
|
30
|
+
# deck_db:: a Sqlite3::Datbase
|
31
|
+
# deck: the (under construction) Anki::Importer::Deck for deck_db
|
32
|
+
#
|
33
|
+
# Returns an array of Field instances.
|
34
|
+
def self.from_db(deck_db, deck)
|
35
|
+
query = 'SELECT id, modelId, name, description, features, required, "unique", numeric FROM fieldModels ORDER BY ordinal'
|
36
|
+
models = deck_db.execute(query).map do |anki_id, model_id, name,
|
37
|
+
description, features, required, unique, numeric|
|
38
|
+
self.new anki_id, deck.models_by_id[model_id], name, description,
|
39
|
+
features, required == 1, unique == 1, numeric == 1
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# :nodoc: private
|
44
|
+
def initialize(anki_id, model, name, description, features, required, unique,
|
45
|
+
numeric)
|
46
|
+
@anki_id = anki_id
|
47
|
+
@model = model
|
48
|
+
@name = name
|
49
|
+
@description = description
|
50
|
+
@features = features
|
51
|
+
@required = required
|
52
|
+
@unique = unique
|
53
|
+
@numeric = numeric
|
54
|
+
end
|
55
|
+
end # class Anki::Importer::Field
|
56
|
+
end # namespace Anki::Importer
|
57
|
+
end # namespace Anki
|
@@ -0,0 +1,92 @@
|
|
1
|
+
# :nodoc: namespace
|
2
|
+
module Anki
|
3
|
+
# :nodoc: namespace
|
4
|
+
module Importer
|
5
|
+
|
6
|
+
# Schema for Anki cards.
|
7
|
+
#
|
8
|
+
# A deck can have multiple models.
|
9
|
+
class Model
|
10
|
+
# Name assigned in the Anki UI.
|
11
|
+
attr_reader :name
|
12
|
+
# Generally empty.
|
13
|
+
attr_reader :description
|
14
|
+
# Assigned in the Anki UI. Space-separated string.
|
15
|
+
attr_reader :tags
|
16
|
+
# Unique ID in the models table.
|
17
|
+
attr_reader :anki_id
|
18
|
+
|
19
|
+
# Facts implementing this model.
|
20
|
+
attr_reader :facts
|
21
|
+
|
22
|
+
# Fields belonging to this model.
|
23
|
+
attr_reader :fields
|
24
|
+
|
25
|
+
# Schema for cards based off this model.
|
26
|
+
attr_reader :card_models
|
27
|
+
|
28
|
+
# Reads the models from an Anki deck.
|
29
|
+
#
|
30
|
+
# Args:
|
31
|
+
# deck_db:: a Sqlite3::Datbase
|
32
|
+
#
|
33
|
+
# Returns an array of Model instances.
|
34
|
+
def self.from_db(deck_db)
|
35
|
+
query = 'SELECT id, name, description, tags, features FROM models'
|
36
|
+
models = deck_db.execute(query).map do |anki_id, name, description, tags,
|
37
|
+
features|
|
38
|
+
self.new anki_id, name, description, tags, features
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# :nodoc: private
|
43
|
+
def initialize(anki_id, name, description, tags, features)
|
44
|
+
@anki_id = anki_id
|
45
|
+
@name = name
|
46
|
+
@description = description
|
47
|
+
@tags = tags
|
48
|
+
@features = features
|
49
|
+
|
50
|
+
@fields = []
|
51
|
+
@fields_by_id = {}
|
52
|
+
@card_models = []
|
53
|
+
@card_models_by_id = {}
|
54
|
+
@facts = []
|
55
|
+
@facts_by_id = {}
|
56
|
+
end
|
57
|
+
|
58
|
+
# :nodoc: private
|
59
|
+
def add_field(field)
|
60
|
+
if @fields_by_id[field.anki_id]
|
61
|
+
raise ArgumentError, 'The Field has a duplicate Anki ID.'
|
62
|
+
end
|
63
|
+
@fields << field
|
64
|
+
@fields_by_id[field.anki_id] = field
|
65
|
+
end
|
66
|
+
# :nodoc: private
|
67
|
+
attr_reader :fields_by_id
|
68
|
+
|
69
|
+
# :nodoc: private
|
70
|
+
def add_card_model(card_model)
|
71
|
+
if @card_models_by_id[card_model.anki_id]
|
72
|
+
raise ArgumentError, 'The CardModel has a duplicate Anki ID.'
|
73
|
+
end
|
74
|
+
@card_models << card_model
|
75
|
+
@card_models_by_id[card_model.anki_id] = card_model
|
76
|
+
end
|
77
|
+
# :nodoc: private
|
78
|
+
attr_reader :card_models_by_id
|
79
|
+
|
80
|
+
# :nodoc: private
|
81
|
+
def add_fact(fact)
|
82
|
+
if @facts_by_id[fact.anki_id]
|
83
|
+
raise ArgumentError, 'The Fact has a duplicate Anki ID.'
|
84
|
+
end
|
85
|
+
@facts << fact
|
86
|
+
@facts_by_id[fact.anki_id] = fact
|
87
|
+
end
|
88
|
+
# :nodoc: private
|
89
|
+
attr_reader :facts_by_id
|
90
|
+
end # class Anki::Importer::Model
|
91
|
+
end # namespace Anki::Importer
|
92
|
+
end # namespace Anki
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# NOTE: can't do granular requires because active_support crashes
|
2
|
+
# require 'active_support/core_ext/enumerable'
|
3
|
+
# require 'active_support/core_ext/string'
|
4
|
+
require 'active_support/core_ext'
|
5
|
+
require 'sqlite3'
|
6
|
+
|
7
|
+
require 'anki/importer/card_model.rb'
|
8
|
+
require 'anki/importer/deck.rb'
|
9
|
+
require 'anki/importer/fact.rb'
|
10
|
+
require 'anki/importer/field.rb'
|
11
|
+
require 'anki/importer/model.rb'
|
@@ -0,0 +1,152 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
describe "anki-importer" do
|
4
|
+
let(:deck) do
|
5
|
+
deck_path = File.join File.dirname(__FILE__), 'fixtures', 'deck.anki'
|
6
|
+
Anki::Importer::Deck.from_file deck_path
|
7
|
+
end
|
8
|
+
|
9
|
+
describe 'Deck' do
|
10
|
+
it 'should have a Model' do
|
11
|
+
deck.should have(1).model
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'should have Fields' do
|
15
|
+
deck.should have(3).fields
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'should have CardModels' do
|
19
|
+
deck.should have(3).card_models
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
let(:model) { deck.models.first }
|
24
|
+
|
25
|
+
describe 'Model' do
|
26
|
+
it 'should extract the name correctly' do
|
27
|
+
model.name.should == 'CSS Color'
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'should extract tags correctly' do
|
31
|
+
model.tags.should == 'css html color'
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'should be associated with fields' do
|
35
|
+
model.should have(3).fields
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'should be associated with card models' do
|
39
|
+
model.should have(3).card_models
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'should be associated with facts' do
|
43
|
+
model.should have(5).facts
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
let(:name_field) { deck.fields.find { |f| f.name == 'Color name' } }
|
48
|
+
let(:code_field) { deck.fields.find { |f| f.name == 'HTML color code' } }
|
49
|
+
let(:rgb_field) { deck.fields.find { |f| f.name == 'RGB color code' } }
|
50
|
+
|
51
|
+
describe 'Field' do
|
52
|
+
it 'should decode names correctly' do
|
53
|
+
name_field.should_not be_nil
|
54
|
+
code_field.should_not be_nil
|
55
|
+
rgb_field.should_not be_nil
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'should be associated with the right model' do
|
59
|
+
name_field.model.should == model
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'should decode required correctly' do
|
63
|
+
name_field.required.should be_true
|
64
|
+
rgb_field.required.should be_false
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'should decode unique correctly' do
|
68
|
+
name_field.unique.should be_false
|
69
|
+
code_field.unique.should be_true
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'should decode numeric correctly' do
|
73
|
+
name_field.numeric.should be_false
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'should order fields correctly' do
|
77
|
+
model.fields.should == [name_field, code_field, rgb_field]
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
let(:front_cm) { deck.card_models.find { |cm| cm.name == 'Name to code' } }
|
82
|
+
let(:back_cm) { deck.card_models.find { |cm| cm.name == 'Code to name' } }
|
83
|
+
let(:disabled_cm) do
|
84
|
+
deck.card_models.find { |cm| cm.name == 'Disabled card' }
|
85
|
+
end
|
86
|
+
describe 'CardModel' do
|
87
|
+
it 'should decode names correctly' do
|
88
|
+
front_cm.should_not be_nil
|
89
|
+
back_cm.should_not be_nil
|
90
|
+
disabled_cm.should_not be_nil
|
91
|
+
end
|
92
|
+
|
93
|
+
it 'should be associated with the right model' do
|
94
|
+
front_cm.model.should == model
|
95
|
+
end
|
96
|
+
|
97
|
+
it 'should decode active' do
|
98
|
+
front_cm.active.should be_true
|
99
|
+
disabled_cm.active.should be_false
|
100
|
+
end
|
101
|
+
|
102
|
+
it 'should decode the question template' do
|
103
|
+
front_cm.question_template.should == '%(Color name)s'
|
104
|
+
end
|
105
|
+
|
106
|
+
it 'should decode the answer template' do
|
107
|
+
front_cm.answer_template.should == '%(HTML color code)s'
|
108
|
+
end
|
109
|
+
|
110
|
+
it 'should decode question formatting' do
|
111
|
+
golden = { :font_family => 'Arial', :font_size => 20,
|
112
|
+
:color => '#000000', :text_align => 0 }
|
113
|
+
front_cm.question_style.should == golden
|
114
|
+
end
|
115
|
+
|
116
|
+
it 'should decode answer formatting' do
|
117
|
+
golden = { :font_family => 'Arial', :font_size => 20,
|
118
|
+
:color => '#000000', :text_align => 0 }
|
119
|
+
front_cm.answer_style.should == golden
|
120
|
+
end
|
121
|
+
|
122
|
+
it 'should associate the answer field correctly' do
|
123
|
+
front_cm.answer_field.should == code_field
|
124
|
+
back_cm.answer_field.should be_nil
|
125
|
+
end
|
126
|
+
|
127
|
+
it 'should order models correctly' do
|
128
|
+
model.card_models.should == [front_cm, back_cm, disabled_cm]
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
let(:white_fact) do
|
133
|
+
deck.facts.find { |fact| fact.fields[name_field] == 'white' }
|
134
|
+
end
|
135
|
+
let(:black_fact) do
|
136
|
+
black_hanzi = [233, 187, 145].pack('C*') # 黑
|
137
|
+
deck.facts.find { |fact| fact.fields[name_field] == black_hanzi }
|
138
|
+
end
|
139
|
+
describe 'Fact' do
|
140
|
+
it 'should decode field values correctly' do
|
141
|
+
white_fact.should_not be_nil
|
142
|
+
end
|
143
|
+
|
144
|
+
it 'should decode unicode field values correctly' do
|
145
|
+
black_fact.should_not be_nil
|
146
|
+
end
|
147
|
+
|
148
|
+
it 'should be associated with the right model' do
|
149
|
+
white_fact.model.should == model
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
Binary file
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
require 'anki/importer'
|
2
|
+
require 'rspec'
|
3
|
+
|
4
|
+
# Requires supporting files with custom matchers and macros, etc,
|
5
|
+
# in ./support/ and its subdirectories.
|
6
|
+
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
|
7
|
+
|
8
|
+
RSpec.configure do |config|
|
9
|
+
|
10
|
+
end
|
metadata
ADDED
@@ -0,0 +1,276 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: anki-importer
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 25
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
- 1
|
10
|
+
version: 0.1.1
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Victor Costan
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2010-10-26 00:00:00 -04:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
prerelease: false
|
23
|
+
name: activesupport
|
24
|
+
version_requirements: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 7
|
30
|
+
segments:
|
31
|
+
- 3
|
32
|
+
- 0
|
33
|
+
- 0
|
34
|
+
version: 3.0.0
|
35
|
+
requirement: *id001
|
36
|
+
type: :runtime
|
37
|
+
- !ruby/object:Gem::Dependency
|
38
|
+
prerelease: false
|
39
|
+
name: sqlite3-ruby
|
40
|
+
version_requirements: &id002 !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ">="
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
hash: 25
|
46
|
+
segments:
|
47
|
+
- 1
|
48
|
+
- 3
|
49
|
+
- 1
|
50
|
+
version: 1.3.1
|
51
|
+
requirement: *id002
|
52
|
+
type: :runtime
|
53
|
+
- !ruby/object:Gem::Dependency
|
54
|
+
prerelease: false
|
55
|
+
name: rspec
|
56
|
+
version_requirements: &id003 !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
hash: 15
|
62
|
+
segments:
|
63
|
+
- 2
|
64
|
+
- 0
|
65
|
+
- 0
|
66
|
+
version: 2.0.0
|
67
|
+
requirement: *id003
|
68
|
+
type: :development
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
prerelease: false
|
71
|
+
name: bundler
|
72
|
+
version_requirements: &id004 !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ~>
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
hash: 23
|
78
|
+
segments:
|
79
|
+
- 1
|
80
|
+
- 0
|
81
|
+
- 0
|
82
|
+
version: 1.0.0
|
83
|
+
requirement: *id004
|
84
|
+
type: :development
|
85
|
+
- !ruby/object:Gem::Dependency
|
86
|
+
prerelease: false
|
87
|
+
name: jeweler
|
88
|
+
version_requirements: &id005 !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ~>
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
hash: -1876988220
|
94
|
+
segments:
|
95
|
+
- 1
|
96
|
+
- 5
|
97
|
+
- 0
|
98
|
+
- pre5
|
99
|
+
version: 1.5.0.pre5
|
100
|
+
requirement: *id005
|
101
|
+
type: :development
|
102
|
+
- !ruby/object:Gem::Dependency
|
103
|
+
prerelease: false
|
104
|
+
name: rcov
|
105
|
+
version_requirements: &id006 !ruby/object:Gem::Requirement
|
106
|
+
none: false
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
hash: 3
|
111
|
+
segments:
|
112
|
+
- 0
|
113
|
+
version: "0"
|
114
|
+
requirement: *id006
|
115
|
+
type: :development
|
116
|
+
- !ruby/object:Gem::Dependency
|
117
|
+
prerelease: false
|
118
|
+
name: sqlite3-ruby
|
119
|
+
version_requirements: &id007 !ruby/object:Gem::Requirement
|
120
|
+
none: false
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
hash: 25
|
125
|
+
segments:
|
126
|
+
- 1
|
127
|
+
- 3
|
128
|
+
- 1
|
129
|
+
version: 1.3.1
|
130
|
+
requirement: *id007
|
131
|
+
type: :runtime
|
132
|
+
- !ruby/object:Gem::Dependency
|
133
|
+
prerelease: false
|
134
|
+
name: activesupport
|
135
|
+
version_requirements: &id008 !ruby/object:Gem::Requirement
|
136
|
+
none: false
|
137
|
+
requirements:
|
138
|
+
- - ">="
|
139
|
+
- !ruby/object:Gem::Version
|
140
|
+
hash: 7
|
141
|
+
segments:
|
142
|
+
- 3
|
143
|
+
- 0
|
144
|
+
- 0
|
145
|
+
version: 3.0.0
|
146
|
+
requirement: *id008
|
147
|
+
type: :runtime
|
148
|
+
- !ruby/object:Gem::Dependency
|
149
|
+
prerelease: false
|
150
|
+
name: rspec
|
151
|
+
version_requirements: &id009 !ruby/object:Gem::Requirement
|
152
|
+
none: false
|
153
|
+
requirements:
|
154
|
+
- - ~>
|
155
|
+
- !ruby/object:Gem::Version
|
156
|
+
hash: 15
|
157
|
+
segments:
|
158
|
+
- 2
|
159
|
+
- 0
|
160
|
+
- 0
|
161
|
+
version: 2.0.0
|
162
|
+
requirement: *id009
|
163
|
+
type: :development
|
164
|
+
- !ruby/object:Gem::Dependency
|
165
|
+
prerelease: false
|
166
|
+
name: bundler
|
167
|
+
version_requirements: &id010 !ruby/object:Gem::Requirement
|
168
|
+
none: false
|
169
|
+
requirements:
|
170
|
+
- - ~>
|
171
|
+
- !ruby/object:Gem::Version
|
172
|
+
hash: 23
|
173
|
+
segments:
|
174
|
+
- 1
|
175
|
+
- 0
|
176
|
+
- 0
|
177
|
+
version: 1.0.0
|
178
|
+
requirement: *id010
|
179
|
+
type: :development
|
180
|
+
- !ruby/object:Gem::Dependency
|
181
|
+
prerelease: false
|
182
|
+
name: jeweler
|
183
|
+
version_requirements: &id011 !ruby/object:Gem::Requirement
|
184
|
+
none: false
|
185
|
+
requirements:
|
186
|
+
- - ~>
|
187
|
+
- !ruby/object:Gem::Version
|
188
|
+
hash: -1876988220
|
189
|
+
segments:
|
190
|
+
- 1
|
191
|
+
- 5
|
192
|
+
- 0
|
193
|
+
- pre5
|
194
|
+
version: 1.5.0.pre5
|
195
|
+
requirement: *id011
|
196
|
+
type: :development
|
197
|
+
- !ruby/object:Gem::Dependency
|
198
|
+
prerelease: false
|
199
|
+
name: rcov
|
200
|
+
version_requirements: &id012 !ruby/object:Gem::Requirement
|
201
|
+
none: false
|
202
|
+
requirements:
|
203
|
+
- - ">="
|
204
|
+
- !ruby/object:Gem::Version
|
205
|
+
hash: 3
|
206
|
+
segments:
|
207
|
+
- 0
|
208
|
+
version: "0"
|
209
|
+
requirement: *id012
|
210
|
+
type: :development
|
211
|
+
description: Extracts models, facts and cards from Anki deck databases.
|
212
|
+
email: victor@costan.us
|
213
|
+
executables: []
|
214
|
+
|
215
|
+
extensions: []
|
216
|
+
|
217
|
+
extra_rdoc_files:
|
218
|
+
- LICENSE.txt
|
219
|
+
- README.rdoc
|
220
|
+
files:
|
221
|
+
- .document
|
222
|
+
- .project
|
223
|
+
- .rspec
|
224
|
+
- Gemfile
|
225
|
+
- Gemfile.lock
|
226
|
+
- LICENSE.txt
|
227
|
+
- README.rdoc
|
228
|
+
- Rakefile
|
229
|
+
- VERSION
|
230
|
+
- anki-importer.gemspec
|
231
|
+
- lib/anki/importer.rb
|
232
|
+
- lib/anki/importer/card_model.rb
|
233
|
+
- lib/anki/importer/deck.rb
|
234
|
+
- lib/anki/importer/fact.rb
|
235
|
+
- lib/anki/importer/field.rb
|
236
|
+
- lib/anki/importer/model.rb
|
237
|
+
- spec/anki_importer_spec.rb
|
238
|
+
- spec/fixtures/deck.anki
|
239
|
+
- spec/spec_helper.rb
|
240
|
+
has_rdoc: true
|
241
|
+
homepage: http://github.com/pwnall/anki_importer
|
242
|
+
licenses: []
|
243
|
+
|
244
|
+
post_install_message:
|
245
|
+
rdoc_options: []
|
246
|
+
|
247
|
+
require_paths:
|
248
|
+
- lib
|
249
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
250
|
+
none: false
|
251
|
+
requirements:
|
252
|
+
- - ">="
|
253
|
+
- !ruby/object:Gem::Version
|
254
|
+
hash: 3
|
255
|
+
segments:
|
256
|
+
- 0
|
257
|
+
version: "0"
|
258
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
259
|
+
none: false
|
260
|
+
requirements:
|
261
|
+
- - ">="
|
262
|
+
- !ruby/object:Gem::Version
|
263
|
+
hash: 3
|
264
|
+
segments:
|
265
|
+
- 0
|
266
|
+
version: "0"
|
267
|
+
requirements: []
|
268
|
+
|
269
|
+
rubyforge_project:
|
270
|
+
rubygems_version: 1.3.7
|
271
|
+
signing_key:
|
272
|
+
specification_version: 3
|
273
|
+
summary: Importer for Anki decks
|
274
|
+
test_files:
|
275
|
+
- spec/anki_importer_spec.rb
|
276
|
+
- spec/spec_helper.rb
|