nametrainer 0.2.1 → 0.2.2

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/README.md CHANGED
@@ -65,15 +65,13 @@ Install with `gem install nametrainer`.
65
65
 
66
66
  You will also have to install Qt bindings for Ruby:
67
67
 
68
- - On Windows, the `qtbindings` gem should get you up and running.
69
-
70
68
  - On Linux, install the appropriate package for your distribution,
71
- e. g. `qt4-ruby` on Ubuntu.
72
-
73
- You can also compile the bindings using the `qtbindings` gem
69
+ or compile the bindings using the `qtbindings` gem
74
70
  (see the [qtbindings README](https://github.com/ryanmelt/qtbindings#readme)
75
71
  for instructions).
76
72
 
73
+ - On Windows, the `qtbindings` gem should get you up and running.
74
+
77
75
  Acknowledgements
78
76
  ----------------
79
77
 
data/Rakefile CHANGED
@@ -1,6 +1,6 @@
1
1
  # Rakefile for the nametrainer program.
2
2
  #
3
- # Copyright (C) 2012 Marcus Stollsteimer
3
+ # Copyright (C) 2012-2013 Marcus Stollsteimer
4
4
 
5
5
  require 'rake/testtask'
6
6
 
@@ -1,4 +1,4 @@
1
- #!/usr/bin/ruby -w
1
+ #!/usr/bin/env ruby
2
2
 
3
3
  require 'nametrainer'
4
4
 
@@ -10,7 +10,7 @@ module Nametrainer
10
10
 
11
11
  # Prints an error message, then exits with +code+
12
12
  # (<tt>:general</tt> or <tt>:usage</tt>).
13
- def self.fail(message, code = :general) #
13
+ def self.fail(message, code = :general)
14
14
  warn "#{PROGNAME}: #{message}"
15
15
  exit ERRORCODE[code]
16
16
  end
@@ -16,7 +16,7 @@
16
16
  #
17
17
  # == Author
18
18
  #
19
- # Copyright (C) 2012 Marcus Stollsteimer
19
+ # Copyright (C) 2012-2013 Marcus Stollsteimer
20
20
  #
21
21
  # License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
22
22
 
@@ -30,9 +30,11 @@ rescue LoadError => e
30
30
  end
31
31
 
32
32
  require 'nametrainer/gui' unless Nametrainer::QT_LOAD_ERROR
33
- require 'nametrainer/person'
34
33
  require 'nametrainer/collection'
34
+ require 'nametrainer/collectionloader'
35
35
  require 'nametrainer/optionparser'
36
+ require 'nametrainer/person'
37
+ require 'nametrainer/rng'
36
38
  require 'nametrainer/statistics'
37
39
  require 'nametrainer/version'
38
40
 
@@ -1,69 +1,61 @@
1
- require 'set'
2
1
  require 'yaml'
2
+ require 'forwardable'
3
3
 
4
- require 'nametrainer/person'
4
+ require 'nametrainer/rng'
5
5
 
6
6
  module Nametrainer
7
7
 
8
- # A Class for a collection of persons, each with a name,
8
+ # A class for a collection of persons, each with a name,
9
9
  # a corresponding (image) file, and a score.
10
- # A collection is an array of instances of the Person class.
11
10
  #
12
- # Open a collection with
13
- # collection = Collection.new(directory, extensions)
14
- #
15
- # For each file with an extension from the +extensions+ array a Person
16
- # instance is created, using the file name as the person's name
17
- # (extension is removed, underscore is converted to space)
18
- # or the content of a corresponding `txt' file, and the file name as
19
- # the +image+ attribute.
11
+ # Create a Collection instance with
12
+ # collection = CollectionLoader.load(
13
+ # :directory => directory,
14
+ # :extensions => extensions
15
+ # )
20
16
  #
21
17
  # You can get a random person from a collection with
22
18
  # person = collection.sample
23
19
  #
24
20
  # Persons with a lower score are chosen more often
25
21
  # than persons with a higher score.
26
- class Collection < Array
22
+ class Collection
23
+
24
+ extend Forwardable
25
+ def_delegators :@collection,
26
+ :size, :empty?, :[], :last, :index, :shuffle, :map, :each
27
+
28
+ include Enumerable
27
29
 
28
30
  SCORE_FILE = 'nametrainer.dat'
29
31
 
30
- # Creates a Collection instance.
31
- # It searches in +directory+ for files with the given
32
- # file extensions (also in upper case) and populates
33
- # the collection with corresponding Person instances.
32
+ # Creates a Collection instance. Expects an argument hash with:
34
33
  #
35
- # +directory+ - collection directory
36
- # +extension+ - array of file extensions
37
- def initialize(directory, extensions)
38
- super()
39
- @directory = directory
40
- @extensions = extensions.to_set
41
- @extensions.merge extensions.map {|i| i.upcase }
42
- self.concat self.class.load(@directory, @extensions.to_a)
34
+ # +:persons+ - array of Person instances
35
+ # +:directory+ - collection directory
36
+ # +:rng+ - random number generator, defaults to RNG
37
+ def initialize(args)
38
+ @collection = args[:persons]
39
+ @directory = args[:directory]
40
+ @rng = args[:rng] || RNG.new(:size => @collection.size, :weighting_factor => 6)
43
41
  end
44
42
 
45
43
  # Returns an array of all names.
46
44
  def names
47
- all_names = Array.new
48
- self.each {|person| all_names << person.name }
49
-
50
- all_names
45
+ map {|person| person.name }
51
46
  end
52
47
 
53
48
  # Returns a hash with the score of all persons (name => score).
54
49
  def scores
55
- all_scores = {}
56
- self.each {|person| all_scores[person.name] = person.score }
57
-
58
- all_scores
50
+ Hash[map {|person| [person.name, person.score] }]
59
51
  end
60
52
 
61
53
  # Sets the score of some or all persons.
62
54
  #
63
55
  # +new_scores+ - hash with (name => score) values
64
56
  def set_scores(new_scores)
65
- self.each do |person|
66
- person.score = new_scores[person.name] unless new_scores[person.name].nil?
57
+ each do |person|
58
+ person.score = new_scores[person.name] if new_scores[person.name]
67
59
  end
68
60
  end
69
61
 
@@ -80,78 +72,26 @@ module Nametrainer
80
72
  File.open(filename, 'w') {|f| f.write(scores.to_yaml) }
81
73
  end
82
74
 
75
+ # Delete score file.
76
+ def delete_scores
77
+ filename = File.expand_path("#{@directory}/#{SCORE_FILE}")
78
+ File.delete(filename)
79
+ end
80
+
83
81
  # Returns a random element, preferring persons with a smaller score.
84
82
  def sample
85
- self.shuffle.sort[weighted_random_index] # shuffle first, so that elements with equal scores get mixed up
83
+ shuffle.sort[@rng.rand] # shuffle first to mix up elements with equal scores
86
84
  end
87
85
 
88
- # Returns the successor of the specified element.
89
- # The successor of the last element is the first element.
86
+ # Returns the successor of the specified element, if possible,
87
+ # or the first element.
90
88
  #
91
89
  # +element+ - element whose successor should be returned
92
90
  def successor(element)
93
- index = self.index(element)
94
- return nil if index.nil?
95
-
96
- (index == self.size - 1) ? self[0] : self[index + 1]
97
- end
98
-
99
- private
100
-
101
- # Returns a random index (obsolete).
102
- def random_index
103
- rand(self.size)
104
- end
91
+ element_index = index(element)
92
+ return first unless element_index
105
93
 
106
- # Returns a random index, preferring smaller indices.
107
- def weighted_random_index
108
- indices = indices_urn(:size => self.size, :weighting_factor => 6)
109
-
110
- indices[rand(indices.size)]
111
- end
112
-
113
- # Returns an array of all indices from 0 to :size - 1, where lower indices are
114
- # more frequent than higher indices. Index 0 will be about :weighting_factor
115
- # times more probable then the highest index.
116
- def indices_urn(options)
117
- size = options[:size]
118
- weighting_factor = options[:weighting_factor]
119
- urn = Array.new
120
- # repeatedly add partial ranges of indices to urn
121
- 1.upto(weighting_factor) do |i|
122
- count = (i.to_f/weighting_factor * size).ceil
123
- urn += (0...count).to_a
124
- end
125
-
126
- urn
127
- end
128
-
129
- # class methods
130
-
131
- # Load a collection. Returns an array of Person instances.
132
- def self.load(directory, extensions)
133
- extension_list = extensions.join(',')
134
- files = Dir.glob("#{directory}/*.{#{extension_list}}").sort
135
- result = Array.new
136
- files.each do |file|
137
- name = get_name(file, extensions)
138
- result << Person.new(name, file)
139
- end
140
-
141
- result
142
- end
143
-
144
- # Get name from corresponding `txt' file or
145
- # from file name (remove extension, convert underscores).
146
- def self.get_name(file, extensions)
147
- name = file.dup
148
- extensions.each {|ext| name.gsub!(/\.#{ext}\Z/, '') } # remove file extension
149
- begin
150
- info_file = "#{name}.txt"
151
- File.read(info_file).chomp
152
- rescue
153
- File.basename(name).tr('_', ' ')
154
- end
94
+ self[element_index + 1] || first
155
95
  end
156
96
  end
157
97
  end
@@ -0,0 +1,55 @@
1
+ require 'set'
2
+
3
+ require 'nametrainer/person'
4
+ require 'nametrainer/collection'
5
+
6
+ module Nametrainer
7
+
8
+ # CollectionLoader.load loads a collection from a directory
9
+ # and returns a Collection instance.
10
+ #
11
+ # It searches in the directory for files with the given
12
+ # file extensions (ignoring case) and for each file creates
13
+ # a Person instance, using the file name as the person's +name+
14
+ # (extension is removed, underscore is converted to space)
15
+ # or the content of a corresponding `txt' file,
16
+ # and the file name as the +image+ attribute.
17
+ module CollectionLoader
18
+
19
+ # Loads a collection. Expects an argument hash with:
20
+ #
21
+ # +:directory+ - collection directory
22
+ # +:extensions+ - array of file extensions
23
+ # +:collection_class+ - defaults to Collection
24
+ # +:person_class+ - defaults to Person
25
+ #
26
+ # Returns a Collection instance.
27
+ def self.load(args)
28
+ directory = args[:directory]
29
+ extensions = args[:extensions]
30
+ collection_class = args[:collection_class] || Collection
31
+ person_class = args[:person_class] || Person
32
+
33
+ pattern = extensions.map {|ext| ext.downcase }.uniq.join('|')
34
+ valid_extensions = /\.(#{pattern})\Z/i
35
+ files = Dir.glob("#{directory}/*").grep(valid_extensions)
36
+ persons = files.sort.map {|file| person_class.new(get_name(file), file) }
37
+
38
+ collection_class.new(:persons => persons, :directory => directory)
39
+ end
40
+
41
+ private
42
+
43
+ # Get name from corresponding `txt' file or
44
+ # from file name (remove extension, convert underscores).
45
+ def self.get_name(file)
46
+ name = file.gsub(/#{File.extname(file)}\Z/, '')
47
+ begin
48
+ info_file = "#{name}.txt"
49
+ File.read(info_file).chomp
50
+ rescue
51
+ File.basename(name).tr('_', ' ')
52
+ end
53
+ end
54
+ end
55
+ end
@@ -39,7 +39,7 @@ module Nametrainer
39
39
  show
40
40
 
41
41
  collection_dir = options[:collection]
42
- init_collection File.expand_path(collection_dir) unless collection_dir.nil?
42
+ init_collection File.expand_path(collection_dir) if collection_dir
43
43
  end
44
44
 
45
45
  # Initializes the GUI layout and functions.
@@ -127,15 +127,18 @@ module Nametrainer
127
127
  # Opens a file dialog and tries to load a collection.
128
128
  def load_collection
129
129
  collection_dir = Qt::FileDialog.get_existing_directory self, 'Load Collection'
130
- return if collection_dir.nil?
131
- init_collection File.expand_path(collection_dir)
130
+ init_collection File.expand_path(collection_dir) if collection_dir
132
131
  end
133
132
 
134
133
  # Tries to load a collection (does not change anything if load fails).
135
134
  #
136
135
  # +collection_dir+ - path to collection
137
136
  def init_collection(collection_dir)
138
- collection = Nametrainer::Collection.new(collection_dir, Nametrainer::FILE_EXTENSIONS)
137
+ args = {
138
+ :directory => collection_dir,
139
+ :extensions => Nametrainer::FILE_EXTENSIONS
140
+ }
141
+ collection = Nametrainer::CollectionLoader.load(args)
139
142
  if collection.nil? or collection.empty?
140
143
  Qt::MessageBox.warning self, 'Error', Nametrainer.collection_empty_message
141
144
  return
@@ -236,7 +239,7 @@ module Nametrainer
236
239
  def choose_person
237
240
  @name_label.set_text ''
238
241
  if @ordered_checkbox.is_checked
239
- @person = @person.nil? ? @collection[0] : @collection.successor(@person)
242
+ @person = @collection.successor(@person)
240
243
  else
241
244
  @person = @collection.sample
242
245
  end
@@ -248,7 +251,7 @@ module Nametrainer
248
251
 
249
252
  # Repaints the image when the application window is resized.
250
253
  def resizeEvent(event)
251
- return if @image.null? || @image.nil?
254
+ return if (@image.nil? || @image.null?)
252
255
  scaledSize = @image.size.scale(@image_label.size, Qt::KeepAspectRatio)
253
256
  show_image if scaledSize != @image_label.pixmap.size
254
257
  end
@@ -0,0 +1,63 @@
1
+ module Nametrainer
2
+
3
+ # A random number generator.
4
+ #
5
+ # Get a random index from 0 to (size - 1), where smaller indices
6
+ # are preferred, with
7
+ #
8
+ # rng = RNG.new(:size => size, :weighting_factor => 6)
9
+ # rng.rand
10
+ #
11
+ # Index 0 will be :weighting_factor times more probable
12
+ # than the highest index.
13
+ class RNG
14
+
15
+ def initialize(args)
16
+ @size = args[:size]
17
+ @weighting_factor = args[:weighting_factor]
18
+
19
+ @limits = normalized_limits
20
+ end
21
+
22
+ #--
23
+ # Weighted random numbers in the range from 0 to (size - 1).
24
+ #
25
+ # Divide the range [0;1) into intervals with size depending
26
+ # on the desired weight. Choose a (evenly distributed)
27
+ # random number and return the number that corresponds to
28
+ # the interval within it lies.
29
+ #++
30
+ def rand
31
+ x = Kernel.rand
32
+ interval = @limits.find_index {|limit| x < limit }
33
+
34
+ interval || (@size - 1)
35
+ end
36
+
37
+ private
38
+
39
+ # Returns a list of weighting factors from @weighting_factor to 1.
40
+ def weighting_factors
41
+ delta = (@weighting_factor - 1).to_f / (@size - 1)
42
+
43
+ Array.new(@size) {|index| @weighting_factor - index * delta}
44
+ end
45
+
46
+ # Returns a list of upper limits for all intervals.
47
+ def raw_limits
48
+ limits = weighting_factors.dup
49
+ 1.upto(@size - 1) do |index|
50
+ limits[index] = limits[index-1] + limits[index]
51
+ end
52
+
53
+ limits
54
+ end
55
+
56
+ # Returns a list of upper limits normalized to the range [0;1).
57
+ def normalized_limits
58
+ max = raw_limits.last
59
+
60
+ raw_limits.map {|limit| limit.to_f / max }
61
+ end
62
+ end
63
+ end
@@ -1,11 +1,11 @@
1
1
  module Nametrainer
2
2
 
3
3
  PROGNAME = 'nametrainer'
4
- VERSION = '0.2.1'
5
- DATE = '2012-10-03'
4
+ VERSION = '0.2.2'
5
+ DATE = '2013-01-04'
6
6
  HOMEPAGE = 'https://github.com/stomar/nametrainer/'
7
7
 
8
- COPYRIGHT = "Copyright (C) 2012 Marcus Stollsteimer.\n" +
8
+ COPYRIGHT = "Copyright (C) 2012-2013 Marcus Stollsteimer.\n" +
9
9
  "License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.\n" +
10
10
  "This is free software: you are free to change and redistribute it.\n" +
11
11
  "There is NO WARRANTY, to the extent permitted by law."
@@ -2,14 +2,18 @@ require 'minitest/spec'
2
2
  require 'minitest/autorun'
3
3
 
4
4
  require 'nametrainer/collection'
5
+ require 'nametrainer/collectionloader'
5
6
 
6
- SRCPATH = File.dirname(__FILE__)
7
+ SRCPATH = File.dirname(__FILE__) unless defined?(SRCPATH)
7
8
 
8
9
  describe Nametrainer::Collection do
9
10
 
10
11
  before do
11
- extensions = %w{png jpg}
12
- @collection = Nametrainer::Collection.new("#{SRCPATH}/collection", extensions)
12
+ args = {
13
+ :directory => "#{SRCPATH}/collection",
14
+ :extensions => %w{png jpg}
15
+ }
16
+ @collection = Nametrainer::CollectionLoader.load(args)
13
17
  @sample_scores = {
14
18
  'Albert Einstein' => 3,
15
19
  'Paul Dirac' => 1,
@@ -41,12 +45,13 @@ describe Nametrainer::Collection do
41
45
  @collection.scores.must_equal @sample_scores
42
46
  end
43
47
 
44
- it 'can save the scores to a file and load them again' do
48
+ it 'can save the scores to a file and load them again and delete them' do
45
49
  @collection.set_scores(@sample_scores)
46
50
  @collection.export_scores
47
51
  @collection.first.score = nil
48
52
  @collection.import_scores
49
53
  @collection.scores.must_equal @sample_scores
54
+ @collection.delete_scores
50
55
  end
51
56
 
52
57
  it 'can get sorted by the scores' do
@@ -66,30 +71,6 @@ describe Nametrainer::Collection do
66
71
  @collection.successor(p1).name.must_equal p2.name
67
72
  @collection.successor(p2).name.must_equal p3.name
68
73
  @collection.successor(p3).name.must_equal p1.name
69
- end
70
-
71
- it 'can increase the score for a specified person' do
72
- person = @collection.last
73
- person.score.must_equal 0
74
- person.increase_score
75
- @collection.last.score.must_equal 1
76
- end
77
-
78
- # private methods
79
- it 'can return a weighted index list' do
80
- expected = [0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2,
81
- 3, 3, 3, 4, 4, 4, 5, 5, 5,
82
- 6, 6, 7, 7, 8, 8,
83
- 9, 10, 11]
84
- indices = @collection.send(:indices_urn, :size => 12, :weighting_factor => 4)
85
- indices.sort.must_equal expected
86
- end
87
-
88
- it 'returns random indices in the correct range' do
89
- samples = 100
90
- indices = Array.new(samples) { @collection.send(:weighted_random_index) }
91
- indices.size.must_equal samples
92
- indices.sort.first.must_be :>=, 0
93
- indices.sort.last.must_be :<, @collection.size
74
+ @collection.successor(nil).name.must_equal p1.name
94
75
  end
95
76
  end
@@ -0,0 +1,26 @@
1
+ require 'minitest/spec'
2
+ require 'minitest/autorun'
3
+
4
+ require 'nametrainer/collectionloader'
5
+
6
+ SRCPATH = File.dirname(__FILE__) unless defined?(SRCPATH)
7
+
8
+ describe Nametrainer::CollectionLoader do
9
+
10
+ before do
11
+ args = {
12
+ :directory => "#{SRCPATH}/collection",
13
+ :extensions => %w{png JPG}
14
+ }
15
+ @collection = Nametrainer::CollectionLoader.load(args)
16
+ end
17
+
18
+ it 'returns a Collection instance' do
19
+ @collection.class.must_equal Nametrainer::Collection
20
+ @collection.first.class.must_equal Nametrainer::Person
21
+ end
22
+
23
+ it 'returns a collection with the correct size' do
24
+ @collection.size.must_equal 3
25
+ end
26
+ end
@@ -0,0 +1,37 @@
1
+ require 'minitest/spec'
2
+ require 'minitest/autorun'
3
+
4
+ require 'nametrainer/rng'
5
+
6
+ describe Nametrainer::RNG do
7
+
8
+ before do
9
+ @size = 7
10
+ @rng = Nametrainer::RNG.new(:size => @size, :weighting_factor => 4)
11
+ end
12
+
13
+ it 'returns random indices in the correct range' do
14
+ samples = 100
15
+ indices = Array.new(samples) { @rng.rand }
16
+ indices.size.must_equal samples
17
+ indices.sort.first.must_be :>=, 0
18
+ indices.sort.last.must_be :<, @size
19
+ end
20
+
21
+ it 'returns random indices with the expected frequency' do
22
+ samples = 100000
23
+ indices = Array.new(samples) { @rng.rand }
24
+
25
+ frequency = Hash.new {|h, k| h[k] = 0 }
26
+ indices.each do |index|
27
+ frequency[index] += 1
28
+ end
29
+ relative_frequency = frequency.sort.map {|ind, freq| freq.to_f/samples }
30
+
31
+ expected = [4.0/17.5, 3.5/17.5, 3.0/17.5, 2.5/17.5,
32
+ 2.0/17.5, 1.5/17.5, 1.0/17.5]
33
+ relative_frequency.each_with_index do |freq, index|
34
+ freq.must_be_within_epsilon expected[index], 0.05
35
+ end
36
+ end
37
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: nametrainer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.2.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-10-03 00:00:00.000000000 Z
12
+ date: 2013-01-04 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rake
16
- requirement: &80165180 !ruby/object:Gem::Requirement
16
+ requirement: !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,10 +21,15 @@ dependencies:
21
21
  version: '0'
22
22
  type: :development
23
23
  prerelease: false
24
- version_requirements: *80165180
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
25
30
  - !ruby/object:Gem::Dependency
26
31
  name: minitest
27
- requirement: &80164650 !ruby/object:Gem::Requirement
32
+ requirement: !ruby/object:Gem::Requirement
28
33
  none: false
29
34
  requirements:
30
35
  - - ! '>='
@@ -32,7 +37,12 @@ dependencies:
32
37
  version: '0'
33
38
  type: :development
34
39
  prerelease: false
35
- version_requirements: *80164650
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
36
46
  description: nametrainer is a name learning trainer using the Qt GUI toolkit. It will
37
47
  assist you in learning people's names from a collection of images.
38
48
  email: sto.mar@web.de
@@ -55,12 +65,16 @@ files:
55
65
  - demo_collection/Werner_Heisenberg.png
56
66
  - demo_collection/Albert_Einstein.png
57
67
  - lib/nametrainer/optionparser.rb
68
+ - lib/nametrainer/collectionloader.rb
58
69
  - lib/nametrainer/gui.rb
59
70
  - lib/nametrainer/version.rb
71
+ - lib/nametrainer/rng.rb
60
72
  - lib/nametrainer/person.rb
61
73
  - lib/nametrainer/collection.rb
62
74
  - lib/nametrainer/statistics.rb
63
75
  - lib/nametrainer.rb
76
+ - test/test_rng.rb
77
+ - test/test_collectionloader.rb
64
78
  - test/test_person.rb
65
79
  - test/test_statistics.rb
66
80
  - test/test_collection.rb
@@ -70,7 +84,6 @@ files:
70
84
  - test/collection/001.txt
71
85
  - test/collection/Max_Born.png
72
86
  - test/collection/002.JPG
73
- - test/collection/nametrainer.dat
74
87
  homepage: https://github.com/stomar/nametrainer/
75
88
  licenses:
76
89
  - GPL-3
@@ -94,11 +107,13 @@ required_rubygems_version: !ruby/object:Gem::Requirement
94
107
  requirements:
95
108
  - the Qt toolkit and Qt bindings for Ruby
96
109
  rubyforge_project: nametrainer
97
- rubygems_version: 1.8.11
110
+ rubygems_version: 1.8.24
98
111
  signing_key:
99
112
  specification_version: 3
100
113
  summary: nametrainer is a name learning trainer using the Qt GUI toolkit.
101
114
  test_files:
115
+ - test/test_rng.rb
116
+ - test/test_collectionloader.rb
102
117
  - test/test_person.rb
103
118
  - test/test_statistics.rb
104
119
  - test/test_collection.rb
@@ -1,4 +0,0 @@
1
- ---
2
- Paul Dirac: 1
3
- Max Born: 2
4
- Albert Einstein: 3