nametrainer 0.2.1 → 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
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