anki_record 0.3.2 → 0.4
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.
- checksums.yaml +4 -4
- data/.rspec +0 -1
- data/.rubocop.yml +2 -7
- data/CHANGELOG.md +9 -1
- data/Gemfile +4 -0
- data/Gemfile.lock +9 -2
- data/README.md +79 -132
- data/anki_record.gemspec +2 -2
- data/lib/anki_record/anki21_database/anki21_database.rb +138 -0
- data/lib/anki_record/anki21_database/anki21_database_attributes.rb +31 -0
- data/lib/anki_record/anki21_database/anki21_database_constructors.rb +52 -0
- data/lib/anki_record/anki2_database/anki2_database.rb +44 -0
- data/lib/anki_record/anki_package/anki_package.rb +110 -176
- data/lib/anki_record/card/card.rb +17 -33
- data/lib/anki_record/card/card_attributes.rb +3 -34
- data/lib/anki_record/card_template/card_template.rb +2 -2
- data/lib/anki_record/card_template/card_template_attributes.rb +11 -11
- data/lib/anki_record/collection/collection.rb +20 -154
- data/lib/anki_record/collection/collection_attributes.rb +2 -30
- data/lib/anki_record/deck/deck.rb +11 -10
- data/lib/anki_record/deck/deck_attributes.rb +6 -8
- data/lib/anki_record/deck/deck_defaults.rb +1 -1
- data/lib/anki_record/deck_options_group/deck_options_group.rb +8 -6
- data/lib/anki_record/deck_options_group/deck_options_group_attributes.rb +5 -7
- data/lib/anki_record/helpers/anki_guid_helper.rb +20 -0
- data/lib/anki_record/media/media.rb +36 -0
- data/lib/anki_record/note/note.rb +62 -86
- data/lib/anki_record/note/note_attributes.rb +18 -17
- data/lib/anki_record/note_field/note_field.rb +3 -3
- data/lib/anki_record/note_field/note_field_attributes.rb +9 -9
- data/lib/anki_record/note_type/note_type.rb +13 -14
- data/lib/anki_record/note_type/note_type_attributes.rb +17 -21
- data/lib/anki_record/version.rb +1 -1
- metadata +11 -7
- data/lib/anki_record/helpers/data_query_helper.rb +0 -15
- data/lib/anki_record/note/note_guid_helper.rb +0 -10
@@ -2,236 +2,170 @@
|
|
2
2
|
|
3
3
|
require "pathname"
|
4
4
|
|
5
|
+
require_relative "../anki2_database/anki2_database"
|
6
|
+
require_relative "../anki21_database/anki21_database"
|
7
|
+
require_relative "../media/media"
|
5
8
|
require_relative "../card/card"
|
6
9
|
require_relative "../collection/collection"
|
7
10
|
require_relative "../note/note"
|
8
11
|
require_relative "../database_setup_constants"
|
9
12
|
|
10
|
-
# rubocop:disable Metrics/ClassLength
|
11
13
|
module AnkiRecord
|
12
14
|
##
|
13
|
-
# AnkiPackage represents
|
15
|
+
# AnkiPackage represents the Anki deck package file which has the .apkg file extension
|
16
|
+
#
|
17
|
+
# This is a zip file containing two SQLite databases (collection.anki21 and collection.anki2),
|
18
|
+
# a media file, and possibly the media (images and sound files). The gem currently does not
|
19
|
+
# have any support for adding or changing media in the Anki package.
|
14
20
|
class AnkiPackage
|
15
|
-
|
21
|
+
attr_accessor :anki21_database, :anki2_database, :media, :tmpdir, :tmpfiles, :target_directory, :name # :nodoc:
|
16
22
|
|
17
23
|
##
|
18
|
-
#
|
19
|
-
|
24
|
+
# Creates a new Anki package file (see the README)
|
25
|
+
def self.create(name:, target_directory: Dir.pwd, &closure)
|
26
|
+
anki_package = new
|
27
|
+
anki_package.create_initialize(name:, target_directory:, &closure)
|
28
|
+
anki_package
|
29
|
+
end
|
20
30
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
# See the README for usage details.
|
25
|
-
def initialize(name:, target_directory: Dir.pwd, data: nil, open_path: nil, &closure)
|
26
|
-
check_name_argument_is_valid(name:)
|
27
|
-
@name = name.end_with?(".apkg") ? name[0, name.length - 5] : name
|
31
|
+
def create_initialize(name:, target_directory: Dir.pwd, &closure) # :nodoc:
|
32
|
+
validate_arguments(name:, target_directory:)
|
33
|
+
@name = new_apkg_name(name:)
|
28
34
|
@target_directory = target_directory
|
29
|
-
@
|
30
|
-
|
31
|
-
|
32
|
-
|
35
|
+
@tmpdir = Dir.mktmpdir
|
36
|
+
@tmpfiles = [Anki21Database::FILENAME, Anki2Database::FILENAME, Media::FILENAME]
|
37
|
+
@anki21_database = Anki21Database.create_new(anki_package: self)
|
38
|
+
@anki2_database = Anki2Database.create_new(anki_package: self)
|
39
|
+
@media = Media.create_new(anki_package: self)
|
33
40
|
|
34
|
-
execute_closure_and_zip(
|
41
|
+
execute_closure_and_zip(anki21_database, &closure) if closure
|
35
42
|
end
|
36
43
|
|
37
|
-
|
38
|
-
#
|
39
|
-
|
40
|
-
|
41
|
-
|
44
|
+
##
|
45
|
+
# Opens an existing Anki package file to update its contents (see the README)
|
46
|
+
def self.update(path:, &closure)
|
47
|
+
anki_package = new
|
48
|
+
anki_package.update_initialize(path:, &closure)
|
49
|
+
anki_package
|
42
50
|
end
|
43
51
|
|
44
|
-
|
45
|
-
|
46
|
-
def execute_closure_and_zip(collection, &closure)
|
47
|
-
closure.call(collection)
|
48
|
-
rescue StandardError => e
|
49
|
-
destroy_temporary_directory
|
50
|
-
puts_error_and_standard_message(error: e)
|
51
|
-
else
|
52
|
-
zip
|
53
|
-
end
|
54
|
-
|
55
|
-
def setup_other_package_instance_variables
|
56
|
-
@tmpdir = Dir.mktmpdir
|
57
|
-
@tmp_files = []
|
58
|
-
@anki21_database = setup_anki21_database_object
|
59
|
-
@anki2_database = setup_anki2_database_object
|
60
|
-
@media_file = setup_media
|
61
|
-
@collection = Collection.new(anki_package: self)
|
62
|
-
end
|
52
|
+
def update_initialize(path:, &closure) # :nodoc:
|
53
|
+
validate_path(path:)
|
63
54
|
|
64
|
-
|
65
|
-
|
55
|
+
@tmpdir = Dir.mktmpdir
|
56
|
+
unzip_apkg_into_tmpdir(path:)
|
57
|
+
@tmpfiles = [Anki21Database::FILENAME, Anki2Database::FILENAME, Media::FILENAME]
|
58
|
+
@anki21_database = Anki21Database.update_new(anki_package: self)
|
59
|
+
@anki2_database = Anki2Database.update_new(anki_package: self)
|
60
|
+
@media = Media.update_new(anki_package: self)
|
66
61
|
|
67
|
-
|
68
|
-
|
62
|
+
@updating_existing_apkg = true
|
63
|
+
execute_closure_and_zip(anki21_database, &closure) if closure
|
64
|
+
end
|
69
65
|
|
70
|
-
|
71
|
-
|
72
|
-
|
66
|
+
# :nodoc:
|
67
|
+
def zip
|
68
|
+
@updating_existing_apkg ? replace_zip_file : create_zip_file
|
69
|
+
destroy_temporary_directory
|
70
|
+
end
|
73
71
|
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
db.execute INSERT_COLLECTION_ANKI_21_COL_RECORD
|
80
|
-
db.results_as_hash = true
|
81
|
-
db
|
82
|
-
end
|
72
|
+
# :nocov:
|
73
|
+
def inspect
|
74
|
+
"[= AnkiPackage name: #{name} target_directory: #{target_directory} =]"
|
75
|
+
end
|
76
|
+
# :nocov:
|
83
77
|
|
84
|
-
|
85
|
-
anki2_file_name = "collection.anki2"
|
86
|
-
db = SQLite3::Database.new "#{@tmpdir}/#{anki2_file_name}", options: {}
|
87
|
-
@tmp_files << anki2_file_name
|
88
|
-
db.execute_batch ANKI_SCHEMA_DEFINITION
|
89
|
-
db.execute INSERT_COLLECTION_ANKI_2_COL_RECORD
|
90
|
-
db.close
|
91
|
-
db
|
92
|
-
end
|
78
|
+
private
|
93
79
|
|
94
|
-
def
|
95
|
-
|
96
|
-
|
97
|
-
media_file.write("{}")
|
98
|
-
media_file.close
|
99
|
-
@tmp_files << "media"
|
100
|
-
media_file
|
101
|
-
end
|
80
|
+
def validate_path(path:)
|
81
|
+
pathname = Pathname.new(path)
|
82
|
+
raise "*No .apkg file was found at the given path." unless pathname.file? && pathname.extname == ".apkg"
|
102
83
|
|
103
|
-
|
104
|
-
@
|
105
|
-
copy_over_notes_and_cards(note_ids: data[:note_ids])
|
84
|
+
@name = File.basename(pathname.to_s, ".apkg")
|
85
|
+
@target_directory = pathname.expand_path.dirname.to_s
|
106
86
|
end
|
107
87
|
|
108
|
-
def
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
AnkiRecord::Note.new(collection: @collection, data: note_cards_data).save
|
88
|
+
def unzip_apkg_into_tmpdir(path:)
|
89
|
+
Zip::File.open(path) do |zip_file|
|
90
|
+
zip_file.each do |entry|
|
91
|
+
entry.extract("#{tmpdir}/#{entry.name}")
|
113
92
|
end
|
114
93
|
end
|
115
94
|
end
|
116
95
|
|
117
|
-
def
|
118
|
-
|
96
|
+
def validate_arguments(name:, target_directory:)
|
97
|
+
check_name_argument_is_valid(name:)
|
98
|
+
check_target_directory_argument_is_valid(target_directory:)
|
119
99
|
end
|
120
100
|
|
121
|
-
def
|
122
|
-
|
123
|
-
puts "#{error}\n#{standard_error_thrown_in_block_message}"
|
124
|
-
end
|
125
|
-
|
126
|
-
public
|
101
|
+
def check_name_argument_is_valid(name:)
|
102
|
+
return if name.instance_of?(String) && !name.empty? && !name.include?(" ")
|
127
103
|
|
128
|
-
|
129
|
-
# Instantiates a new Anki package object seeded with data from the opened Anki package.
|
130
|
-
#
|
131
|
-
# See the README for details.
|
132
|
-
def self.open(path:, target_directory: nil, &closure)
|
133
|
-
pathname = Pathname.new(path)
|
134
|
-
raise "*No .apkg file was found at the given path." unless pathname.file? && pathname.extname == ".apkg"
|
135
|
-
|
136
|
-
new_apkg_name = "#{File.basename(pathname.to_s, ".apkg")}-#{seconds_since_epoch}"
|
137
|
-
data = col_record_and_note_ids_to_copy_over(pathname: pathname)
|
138
|
-
|
139
|
-
if target_directory
|
140
|
-
new(name: new_apkg_name, data: data, open_path: pathname,
|
141
|
-
target_directory: target_directory, &closure)
|
142
|
-
else
|
143
|
-
new(name: new_apkg_name, data: data, open_path: pathname, &closure)
|
104
|
+
raise ArgumentError, "The package name must be a string without spaces."
|
144
105
|
end
|
145
|
-
end
|
146
|
-
|
147
|
-
def was_instantiated_from_existing_apkg? # :nodoc:
|
148
|
-
!@open_path.nil?
|
149
|
-
end
|
150
|
-
|
151
|
-
# rubocop:disable Metrics/MethodLength
|
152
|
-
# :nodoc:
|
153
|
-
def temporarily_unzip_source_apkg
|
154
|
-
raise ArgumentError unless @open_path && block_given?
|
155
106
|
|
156
|
-
|
157
|
-
|
158
|
-
next unless entry.name == "collection.anki21"
|
107
|
+
def check_target_directory_argument_is_valid(target_directory:)
|
108
|
+
return if File.directory?(target_directory)
|
159
109
|
|
160
|
-
|
161
|
-
source_collection_anki21 = SQLite3::Database.open "collection.anki21"
|
162
|
-
source_collection_anki21.results_as_hash = true
|
163
|
-
|
164
|
-
yield source_collection_anki21
|
165
|
-
end
|
110
|
+
raise ArgumentError, "No directory was found at the given path."
|
166
111
|
end
|
167
|
-
File.delete("collection.anki21")
|
168
|
-
end
|
169
|
-
# rubocop:enable Metrics/MethodLength
|
170
112
|
|
171
|
-
|
172
|
-
|
113
|
+
def new_apkg_name(name:)
|
114
|
+
name.end_with?(".apkg") ? name[0, name.length - 5] : name
|
115
|
+
end
|
173
116
|
|
174
117
|
# rubocop:disable Metrics/MethodLength
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
data = { col_record: col_record, note_ids: note_ids }
|
188
|
-
end
|
189
|
-
end
|
190
|
-
File.delete("collection.anki21")
|
191
|
-
data
|
118
|
+
def execute_closure_and_zip(anki21_database)
|
119
|
+
yield(anki21_database)
|
120
|
+
rescue StandardError => e
|
121
|
+
destroy_temporary_directory
|
122
|
+
puts e.backtrace.reverse
|
123
|
+
puts e
|
124
|
+
puts "An error occurred within the block argument."
|
125
|
+
puts "The temporary files have been deleted."
|
126
|
+
puts "If you were creating a new Anki package, nothing was saved."
|
127
|
+
puts "If you were updating an existing one, it was not changed."
|
128
|
+
else
|
129
|
+
zip
|
192
130
|
end
|
193
|
-
# rubocop:enable Metrics/AbcSize
|
194
131
|
# rubocop:enable Metrics/MethodLength
|
195
|
-
end
|
196
|
-
|
197
|
-
##
|
198
|
-
# Zips the temporary files (collection.anki21, collection.anki2, and media) into a new *.apkg package file.
|
199
|
-
#
|
200
|
-
# The temporary files, and the temporary directory they were in, are deleted after zipping.
|
201
|
-
def zip
|
202
|
-
create_zip_file && destroy_temporary_directory
|
203
|
-
end
|
204
132
|
|
205
|
-
|
133
|
+
def destroy_temporary_directory
|
134
|
+
FileUtils.rm_rf(tmpdir)
|
135
|
+
end
|
206
136
|
|
207
137
|
def create_zip_file
|
208
138
|
Zip::File.open(target_zip_file, create: true) do |zip_file|
|
209
|
-
|
210
|
-
zip_file.add(file_name, File.join(
|
139
|
+
tmpfiles.each do |file_name|
|
140
|
+
zip_file.add(file_name, File.join(tmpdir, file_name))
|
211
141
|
end
|
212
142
|
end
|
213
143
|
true
|
214
144
|
end
|
215
145
|
|
216
|
-
|
217
|
-
|
146
|
+
# rubocop:disable Metrics/MethodLength
|
147
|
+
def replace_zip_file
|
148
|
+
File.rename(target_zip_file, tmp_original_zip_file)
|
149
|
+
begin
|
150
|
+
create_zip_file
|
151
|
+
FileUtils.rm(tmp_original_zip_file)
|
152
|
+
rescue StandardError => e
|
153
|
+
puts e.backtrace.reverse
|
154
|
+
puts e
|
155
|
+
puts "An error occurred during zipping the new version of the Anki package."
|
156
|
+
puts "The original package has not been changed"
|
157
|
+
File.rename(tmp_original_zip_file, target_zip_file)
|
158
|
+
end
|
159
|
+
true
|
218
160
|
end
|
161
|
+
# rubocop:enable Metrics/MethodLength
|
219
162
|
|
220
|
-
def
|
221
|
-
|
163
|
+
def target_zip_file
|
164
|
+
"#{target_directory}/#{name}.apkg"
|
222
165
|
end
|
223
166
|
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
def open?
|
228
|
-
!closed?
|
229
|
-
end
|
230
|
-
|
231
|
-
# :nodoc:
|
232
|
-
def closed?
|
233
|
-
@anki21_database.closed?
|
234
|
-
end
|
167
|
+
def tmp_original_zip_file
|
168
|
+
"#{target_zip_file}-old"
|
169
|
+
end
|
235
170
|
end
|
236
171
|
end
|
237
|
-
# rubocop:enable Metrics/ClassLength
|
@@ -6,39 +6,38 @@ require_relative "card_attributes"
|
|
6
6
|
|
7
7
|
module AnkiRecord
|
8
8
|
##
|
9
|
-
# Card represents an Anki card.
|
9
|
+
# Card represents an Anki card. The cards are indirectly created when creating notes.
|
10
10
|
class Card
|
11
11
|
include CardAttributes
|
12
12
|
include Helpers::TimeHelper
|
13
13
|
include Helpers::SharedConstantsHelper
|
14
14
|
|
15
|
-
|
15
|
+
# :nodoc:
|
16
|
+
|
17
|
+
def initialize(note:, card_template: nil, card_data: nil)
|
16
18
|
@note = note
|
17
19
|
if card_template
|
18
|
-
setup_instance_variables_for_new_card(card_template:
|
20
|
+
setup_instance_variables_for_new_card(card_template:)
|
19
21
|
elsif card_data
|
20
|
-
setup_instance_variables_from_existing(card_data:
|
22
|
+
setup_instance_variables_from_existing(card_data:)
|
21
23
|
else
|
22
24
|
raise ArgumentError
|
23
25
|
end
|
24
26
|
end
|
25
27
|
|
28
|
+
def save(note_exists_already: false)
|
29
|
+
note_exists_already ? update_card_in_collection_anki21 : insert_new_card_in_collection_anki21
|
30
|
+
end
|
31
|
+
|
26
32
|
private
|
27
33
|
|
34
|
+
# rubocop:disable Metrics/MethodLength
|
28
35
|
def setup_instance_variables_for_new_card(card_template:)
|
29
36
|
raise ArgumentError unless @note.note_type == card_template.note_type
|
30
37
|
|
31
|
-
setup_collaborator_object_instance_variables_for_new_card(card_template: card_template)
|
32
|
-
setup_simple_instance_variables_for_new_card
|
33
|
-
end
|
34
|
-
|
35
|
-
def setup_collaborator_object_instance_variables_for_new_card(card_template:)
|
36
38
|
@card_template = card_template
|
37
39
|
@deck = @note.deck
|
38
|
-
@
|
39
|
-
end
|
40
|
-
|
41
|
-
def setup_simple_instance_variables_for_new_card
|
40
|
+
@anki21_database = @deck.anki21_database
|
42
41
|
@id = milliseconds_since_epoch
|
43
42
|
@last_modified_timestamp = seconds_since_epoch
|
44
43
|
@usn = NEW_OBJECT_USN
|
@@ -47,37 +46,22 @@ module AnkiRecord
|
|
47
46
|
end
|
48
47
|
@data = "{}"
|
49
48
|
end
|
49
|
+
# rubocop:enable Metrics/MethodLength
|
50
50
|
|
51
51
|
def setup_instance_variables_from_existing(card_data:)
|
52
|
-
|
53
|
-
|
54
|
-
end
|
55
|
-
|
56
|
-
def setup_collaborator_object_instance_variables_from_existing(card_data:)
|
57
|
-
@collection = note.note_type.collection
|
58
|
-
@deck = collection.find_deck_by id: card_data["did"]
|
52
|
+
@anki21_database = note.anki21_database
|
53
|
+
@deck = anki21_database.find_deck_by id: card_data["did"]
|
59
54
|
@card_template = note.note_type.card_templates.find do |card_template|
|
60
55
|
card_template.ordinal_number == card_data["ord"]
|
61
56
|
end
|
62
|
-
end
|
63
|
-
|
64
|
-
def setup_simple_instance_variables_from_existing(card_data:)
|
65
57
|
@last_modified_timestamp = card_data["mod"]
|
66
58
|
%w[id usn type queue due ivl factor reps lapses left odue odid flags data].each do |instance_variable_name|
|
67
59
|
instance_variable_set "@#{instance_variable_name}", card_data[instance_variable_name]
|
68
60
|
end
|
69
61
|
end
|
70
62
|
|
71
|
-
public
|
72
|
-
|
73
|
-
def save(note_exists_already: false) # :nodoc:
|
74
|
-
note_exists_already ? update_card_in_collection_anki21 : insert_new_card_in_collection_anki21
|
75
|
-
end
|
76
|
-
|
77
|
-
private
|
78
|
-
|
79
63
|
def update_card_in_collection_anki21
|
80
|
-
statement =
|
64
|
+
statement = anki21_database.prepare <<~SQL
|
81
65
|
update cards set nid = ?, did = ?, ord = ?, mod = ?, usn = ?, type = ?,
|
82
66
|
queue = ?, due = ?, ivl = ?, factor = ?, reps = ?, lapses = ?,
|
83
67
|
left = ?, odue = ?, odid = ?, flags = ?, data = ? where id = ?
|
@@ -89,7 +73,7 @@ module AnkiRecord
|
|
89
73
|
end
|
90
74
|
|
91
75
|
def insert_new_card_in_collection_anki21
|
92
|
-
statement =
|
76
|
+
statement = anki21_database.prepare <<~SQL
|
93
77
|
insert into cards (id, nid, did, ord,
|
94
78
|
mod, usn, type, queue,
|
95
79
|
due, ivl, factor, reps,
|
@@ -1,39 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module AnkiRecord
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
##
|
8
|
-
# The card's note object.
|
9
|
-
attr_reader :note
|
10
|
-
|
11
|
-
##
|
12
|
-
# The card's deck object.
|
13
|
-
attr_reader :deck
|
14
|
-
|
15
|
-
##
|
16
|
-
# The card's collection object.
|
17
|
-
attr_reader :collection
|
18
|
-
|
19
|
-
##
|
20
|
-
# The card's card template object.
|
21
|
-
attr_reader :card_template
|
22
|
-
|
23
|
-
##
|
24
|
-
# The card's id.
|
25
|
-
#
|
26
|
-
# This is also the number of milliseconds since the 1970 epoch at which the card was created.
|
27
|
-
attr_reader :id
|
28
|
-
|
29
|
-
##
|
30
|
-
# The number of seconds since the 1970 epoch at which the card was last modified.
|
31
|
-
attr_reader :last_modified_timestamp
|
32
|
-
|
33
|
-
##
|
34
|
-
# The card's update sequence number.
|
35
|
-
attr_reader :usn
|
36
|
-
|
37
|
-
attr_reader :type, :queue, :due, :ivl, :factor, :reps, :lapses, :left, :odue, :odid, :flags, :data
|
4
|
+
module CardAttributes # :nodoc:
|
5
|
+
attr_reader :anki21_database, :note, :deck, :card_template, :id, :last_modified_timestamp, :usn, :type, :queue,
|
6
|
+
:due, :ivl, :factor, :reps, :lapses, :left, :odue, :odid, :flags, :data
|
38
7
|
end
|
39
8
|
end
|
@@ -14,9 +14,9 @@ module AnkiRecord
|
|
14
14
|
|
15
15
|
@note_type = note_type
|
16
16
|
if args
|
17
|
-
setup_card_template_instance_variables_from_existing(args:
|
17
|
+
setup_card_template_instance_variables_from_existing(args:)
|
18
18
|
else
|
19
|
-
setup_card_template_instance_variables(name:
|
19
|
+
setup_card_template_instance_variables(name:)
|
20
20
|
end
|
21
21
|
|
22
22
|
@note_type.add_card_template self
|
@@ -5,25 +5,25 @@ module AnkiRecord
|
|
5
5
|
# Module with the CardTemplate class's attribute readers, writers, and accessors.
|
6
6
|
module CardTemplateAttributes
|
7
7
|
##
|
8
|
-
# The card template's name
|
8
|
+
# The card template's name
|
9
9
|
attr_accessor :name
|
10
10
|
|
11
11
|
##
|
12
|
-
# The card template's font style in the browser
|
12
|
+
# The card template's font style in the browser
|
13
13
|
attr_accessor :browser_font_style
|
14
14
|
|
15
15
|
##
|
16
|
-
# The card template's font size used in the browser
|
16
|
+
# The card template's font size used in the browser
|
17
17
|
attr_accessor :browser_font_size
|
18
18
|
|
19
19
|
##
|
20
|
-
# The card template's question format
|
20
|
+
# The card template's question format
|
21
21
|
attr_reader :question_format
|
22
22
|
|
23
23
|
##
|
24
|
-
# Sets the question format of the card template
|
24
|
+
# Sets the question format of the card template
|
25
25
|
#
|
26
|
-
# Raises an ArgumentError if the specified format attempts to use invalid fields
|
26
|
+
# Raises an ArgumentError if the specified format attempts to use invalid fields
|
27
27
|
def question_format=(format)
|
28
28
|
fields_in_specified_format = format.scan(/{{.+?}}/).map do |capture|
|
29
29
|
capture.chomp("}}").reverse.chomp("{{").reverse
|
@@ -38,13 +38,13 @@ module AnkiRecord
|
|
38
38
|
end
|
39
39
|
|
40
40
|
##
|
41
|
-
# The card template's answer format
|
41
|
+
# The card template's answer format
|
42
42
|
attr_reader :answer_format
|
43
43
|
|
44
44
|
##
|
45
|
-
# Sets the answer format of the card template
|
45
|
+
# Sets the answer format of the card template
|
46
46
|
#
|
47
|
-
# Raises an ArgumentError if the specified format attempts to use invalid fields
|
47
|
+
# Raises an ArgumentError if the specified format attempts to use invalid fields
|
48
48
|
def answer_format=(format)
|
49
49
|
fields_in_specified_format = format.scan(/{{.+?}}/).map do |capture|
|
50
50
|
capture.chomp("}}").reverse.chomp("{{").reverse
|
@@ -59,11 +59,11 @@ module AnkiRecord
|
|
59
59
|
end
|
60
60
|
|
61
61
|
##
|
62
|
-
# The card template's note type object
|
62
|
+
# The card template's note type object
|
63
63
|
attr_reader :note_type
|
64
64
|
|
65
65
|
##
|
66
|
-
# 0 for the first card template of the note type, 1 for the second, etc
|
66
|
+
# 0 for the first card template of the note type, 1 for the second, etc
|
67
67
|
attr_reader :ordinal_number
|
68
68
|
end
|
69
69
|
end
|