stellr 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +4 -0
- data/Manifest.txt +36 -0
- data/README.txt +109 -0
- data/Rakefile +28 -0
- data/bin/stellr +64 -0
- data/bin/stellr-search +50 -0
- data/config/stellr.yml +8 -0
- data/lib/stellr.rb +37 -0
- data/lib/stellr/client.rb +78 -0
- data/lib/stellr/collections.rb +6 -0
- data/lib/stellr/collections/base.rb +79 -0
- data/lib/stellr/collections/multi_collection.rb +32 -0
- data/lib/stellr/collections/rsync.rb +38 -0
- data/lib/stellr/collections/searchable_collection.rb +166 -0
- data/lib/stellr/collections/static.rb +97 -0
- data/lib/stellr/collections/writeable_collection.rb +119 -0
- data/lib/stellr/config.rb +88 -0
- data/lib/stellr/search.rb +2 -0
- data/lib/stellr/search/search_result.rb +21 -0
- data/lib/stellr/search/search_results.rb +50 -0
- data/lib/stellr/server.rb +166 -0
- data/lib/stellr/strategies.rb +4 -0
- data/lib/stellr/strategies/base.rb +16 -0
- data/lib/stellr/strategies/blocking.rb +13 -0
- data/lib/stellr/strategies/queueing.rb +78 -0
- data/lib/stellr/utils.rb +24 -0
- data/lib/stellr/utils/observable.rb +20 -0
- data/lib/stellr/utils/shutdown.rb +30 -0
- data/test/fixtures/movies.yml +4 -0
- data/test/stellr_test.rb +38 -0
- data/test/test_client.rb +27 -0
- data/test/test_collections_base.rb +25 -0
- data/test/test_helper.rb +1 -0
- data/test/test_rsync_collection.rb +72 -0
- data/test/test_server.rb +94 -0
- data/test/test_static_collection.rb +40 -0
- data/test/test_stellr.rb +11 -0
- metadata +110 -0
data/lib/stellr/utils.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'stellr/utils/shutdown'
|
2
|
+
require 'stellr/utils/observable'
|
3
|
+
|
4
|
+
class String
|
5
|
+
|
6
|
+
# Constantize tries to find a declared constant with the name specified
|
7
|
+
# in the string. It raises a NameError when the name is not in CamelCase
|
8
|
+
# or is not initialized.
|
9
|
+
#
|
10
|
+
# Examples
|
11
|
+
# "Module".constantize #=> Module
|
12
|
+
# "Class".constantize #=> Class
|
13
|
+
#
|
14
|
+
# Blatantly stolen from rails' activesupport
|
15
|
+
def constantize
|
16
|
+
unless /\A(?:::)?([A-Z]\w*(?:::[A-Z]\w*)*)\z/ =~ self
|
17
|
+
raise NameError, "#{self.inspect} is not a valid constant name!"
|
18
|
+
end
|
19
|
+
classname = $1
|
20
|
+
classname.untaint
|
21
|
+
Object.module_eval("::#{classname}", __FILE__, __LINE__)
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Stellr
|
2
|
+
module Utils
|
3
|
+
module Observable
|
4
|
+
|
5
|
+
def listeners
|
6
|
+
@listeners ||= []
|
7
|
+
end
|
8
|
+
|
9
|
+
def add_listener( &block )
|
10
|
+
listeners << block
|
11
|
+
end
|
12
|
+
|
13
|
+
def notify_listeners( event )
|
14
|
+
listeners.each do |l|
|
15
|
+
l.call event
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Stellr
|
2
|
+
module Utils
|
3
|
+
module Shutdown
|
4
|
+
|
5
|
+
# shutdown modes:
|
6
|
+
# - :abort - stops immediately
|
7
|
+
# - :graceful process all remaining queue entries and stop
|
8
|
+
def shutdown( mode = :abort )
|
9
|
+
@shutdown = mode
|
10
|
+
on_shutdown mode
|
11
|
+
end
|
12
|
+
|
13
|
+
def shutting_down?( mode = nil )
|
14
|
+
if defined? @shutdown
|
15
|
+
mode.nil? || @shutdown == mode
|
16
|
+
else
|
17
|
+
false
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
protected
|
22
|
+
|
23
|
+
# override to hook into the shutdown process
|
24
|
+
def on_shutdown
|
25
|
+
end
|
26
|
+
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
data/test/stellr_test.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'stellr'
|
3
|
+
require 'fileutils'
|
4
|
+
require 'test_helper'
|
5
|
+
|
6
|
+
class StellrTest < Test::Unit::TestCase
|
7
|
+
INDEX_TMP_TEST_DIR = "/tmp/stellr/test"
|
8
|
+
|
9
|
+
@@data = {}
|
10
|
+
|
11
|
+
def setup
|
12
|
+
FileUtils.rm_rf INDEX_TMP_TEST_DIR
|
13
|
+
end
|
14
|
+
|
15
|
+
def teardown
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_truth
|
19
|
+
assert true
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.fixtures( fixture_name )
|
23
|
+
file_name = File.join( File.dirname(__FILE__), "/fixtures/#{fixture_name}.yml" )
|
24
|
+
@@data[fixture_name] = YAML.load( IO.read( file_name ) )
|
25
|
+
define_method( fixture_name.to_sym ) do |key|
|
26
|
+
@@data[fixture_name][key.to_s].symbolize_keys
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
class Hash
|
32
|
+
def symbolize_keys
|
33
|
+
inject({}) do |options, (key, value)|
|
34
|
+
options[key.to_sym] = value
|
35
|
+
options
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
data/test/test_client.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'simple_collection_tests'
|
3
|
+
|
4
|
+
require 'stellr/client'
|
5
|
+
|
6
|
+
class ClientTest < StellrTest
|
7
|
+
include SimpleCollectionTests
|
8
|
+
fixtures :movies
|
9
|
+
|
10
|
+
DRB_URI = "druby://localhost:99999"
|
11
|
+
def setup
|
12
|
+
super
|
13
|
+
@server_obj = Stellr::Server.new Stellr::Config.new( nil, :base_dir => INDEX_TMP_TEST_DIR )
|
14
|
+
@server = DRb.start_service DRB_URI, @server_obj
|
15
|
+
|
16
|
+
@client = Stellr::Client.new DRB_URI
|
17
|
+
@collection = @client.connect 'default', :collection => :static, :fields => { :title => {}}
|
18
|
+
end
|
19
|
+
|
20
|
+
def teardown
|
21
|
+
@server_obj.send :shutdown, :abort
|
22
|
+
@server.stop_service
|
23
|
+
@server.thread.join
|
24
|
+
end
|
25
|
+
|
26
|
+
|
27
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class CollectionsBaseTest < StellrTest
|
4
|
+
|
5
|
+
def test_create_without_strategy
|
6
|
+
%w( Static RSync ).each do |coll_class|
|
7
|
+
c = Stellr::Collections::Base.create('test', :collection_class => "Stellr::Collections::#{coll_class}",
|
8
|
+
:path => INDEX_TMP_TEST_DIR )
|
9
|
+
assert c.class.name =~ /#{coll_class}$/
|
10
|
+
c.close
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_create_with_strategy
|
15
|
+
%w( Static RSync ).each do |coll_class|
|
16
|
+
%w( Queueing Blocking ).each do |strategy_class|
|
17
|
+
c = Stellr::Collections::Base.create('test', :collection_class => "Stellr::Collections::#{coll_class}",
|
18
|
+
:strategy_class => "Stellr::Strategies::#{strategy_class}",
|
19
|
+
:path => INDEX_TMP_TEST_DIR )
|
20
|
+
assert c.class.name =~ /#{strategy_class}$/
|
21
|
+
c.close
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'stellr_test'
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class RSyncCollectionTest < StellrTest
|
4
|
+
fixtures :movies
|
5
|
+
|
6
|
+
def setup
|
7
|
+
super
|
8
|
+
@collection = Stellr::Collections::RSync.new( 'default', default_collection_options )
|
9
|
+
end
|
10
|
+
|
11
|
+
def teardown
|
12
|
+
super
|
13
|
+
@collection.close
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_create
|
17
|
+
@collection.close
|
18
|
+
c = Stellr::Collections::Base.create('test', default_collection_options(:collection => :rsync))
|
19
|
+
assert_equal Stellr::Collections::RSync, c.class
|
20
|
+
c.close
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_create_index
|
24
|
+
assert_equal Ferret::Index::IndexWriter, @collection.send(:writer).class
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_create_index_directories
|
28
|
+
@collection.send(:writer)
|
29
|
+
assert File.directory?( File.join( INDEX_TMP_TEST_DIR, '0' ) )
|
30
|
+
assert File.directory?( File.join( INDEX_TMP_TEST_DIR, '1' ) )
|
31
|
+
assert File.symlink?( File.join( INDEX_TMP_TEST_DIR, 'searching' ) )
|
32
|
+
assert File.symlink?( File.join( INDEX_TMP_TEST_DIR, 'indexing' ) )
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_switch_index
|
36
|
+
@collection.send(:writer)
|
37
|
+
assert File.symlink?( File.join( INDEX_TMP_TEST_DIR, 'indexing') )
|
38
|
+
target = File.readlink( File.join( INDEX_TMP_TEST_DIR, 'indexing') )
|
39
|
+
@collection.switch
|
40
|
+
assert_equal target, File.readlink( File.join( INDEX_TMP_TEST_DIR, 'searching') )
|
41
|
+
end
|
42
|
+
|
43
|
+
def test_index
|
44
|
+
assert_equal 0, @collection.size
|
45
|
+
@collection << { :id => 1, :title => movies(:caligari)[:title] }
|
46
|
+
@collection << { :id => 1, :title => movies(:caligari)[:title] }
|
47
|
+
@collection << { :id => 2, :title => movies(:caligari)[:title] }
|
48
|
+
assert_equal 0, @collection.size
|
49
|
+
@collection.switch
|
50
|
+
assert_equal 2, @collection.size
|
51
|
+
@collection.switch
|
52
|
+
assert_equal 2, @collection.size
|
53
|
+
end
|
54
|
+
|
55
|
+
def test_delete
|
56
|
+
@collection << { :id => 1, :title => movies(:caligari)[:title] }
|
57
|
+
@collection << { :id => 2, :title => movies(:caligari)[:title] }
|
58
|
+
@collection.switch
|
59
|
+
assert_equal 2, @collection.size
|
60
|
+
@collection.delete_record :id => 1
|
61
|
+
assert_equal 2, @collection.size
|
62
|
+
@collection.switch
|
63
|
+
assert_equal 1, @collection.size
|
64
|
+
@collection.switch
|
65
|
+
assert_equal 1, @collection.size
|
66
|
+
end
|
67
|
+
|
68
|
+
def default_collection_options( options = {} )
|
69
|
+
{ :recreate => false,
|
70
|
+
:path => INDEX_TMP_TEST_DIR }.update( options )
|
71
|
+
end
|
72
|
+
end
|
data/test/test_server.rb
ADDED
@@ -0,0 +1,94 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class Stellr::Strategies::Queueing
|
4
|
+
|
5
|
+
# used in test cases to give the queueing thread some time for
|
6
|
+
# auto-rotating the indexes
|
7
|
+
def wait_for
|
8
|
+
sleep 0.1 while !@queue.empty?
|
9
|
+
sleep 0.1
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
13
|
+
|
14
|
+
class ServerTest < StellrTest
|
15
|
+
|
16
|
+
def setup
|
17
|
+
super
|
18
|
+
@server = Stellr::Server.new Stellr::Config.new( nil, :base_dir => INDEX_TMP_TEST_DIR )
|
19
|
+
end
|
20
|
+
|
21
|
+
def teardown
|
22
|
+
@server.send :shutdown, :abort
|
23
|
+
@server = nil
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_register_collection_defaults
|
27
|
+
c = @server.register 'default'
|
28
|
+
assert c
|
29
|
+
assert_equal Stellr::Collections::RSync, c.class
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_register_static_collection
|
33
|
+
c = @server.register 'static', :collection_class => 'Stellr::Collections::Static'
|
34
|
+
assert c
|
35
|
+
assert_equal Stellr::Collections::Static, c.class
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_register_queueing_collection
|
39
|
+
c = @server.register 'queue', :strategy_class => 'Stellr::Strategies::Queueing'
|
40
|
+
assert c
|
41
|
+
assert_equal Stellr::Strategies::Queueing, c.class
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_default_options
|
45
|
+
@server.register 'default'
|
46
|
+
config = @server.instance_variable_get "@config"
|
47
|
+
assert_equal 9010, config.port
|
48
|
+
assert_equal Stellr::Collections::RSync, @server.collection( 'default' ).class
|
49
|
+
end
|
50
|
+
|
51
|
+
def test_index_data
|
52
|
+
@server.register 'default'
|
53
|
+
@server.add_record 'default', { :id => 1, :text => 'hello world' }
|
54
|
+
@server.add_record 'default', { :id => 2, :text => 'hello world' }
|
55
|
+
@server.batch_finished 'default'
|
56
|
+
assert_equal 2, @server.size( 'default' )
|
57
|
+
end
|
58
|
+
|
59
|
+
def test_delete_data_queued
|
60
|
+
coll = "del-queued"
|
61
|
+
@server.register coll, :strategy => :queueing
|
62
|
+
assert_equal 0, @server.size( coll )
|
63
|
+
@server.add_record coll, { :id => 1, :text => 'hello world' }
|
64
|
+
@server.add_record coll, { :id => 2, :text => 'hello world' }
|
65
|
+
@server.wait_for coll
|
66
|
+
assert_equal 2, @server.size( coll )
|
67
|
+
@server.delete_record coll, { :id => 2, :text => 'hello world' }
|
68
|
+
assert_equal 2, @server.size( coll )
|
69
|
+
@server.wait_for coll
|
70
|
+
assert_equal 1, @server.size( coll )
|
71
|
+
end
|
72
|
+
|
73
|
+
def test_delete_data_blocking
|
74
|
+
coll = "del-blocking"
|
75
|
+
@server.register coll, :strategy => nil
|
76
|
+
assert_equal 0, @server.size( coll )
|
77
|
+
@server.add_record coll, { :id => 1, :text => 'hello world' }
|
78
|
+
@server.add_record coll, { :id => 2, :text => 'hello world' }
|
79
|
+
@server.switch coll
|
80
|
+
assert_equal 2, @server.size( coll )
|
81
|
+
@server.delete_record coll, { :id => 2, :text => 'hello world' }
|
82
|
+
assert_equal 2, @server.size( coll )
|
83
|
+
@server.switch coll
|
84
|
+
assert_equal 1, @server.size( coll )
|
85
|
+
end
|
86
|
+
|
87
|
+
def test_data_require_id_field
|
88
|
+
@server.register 'default'
|
89
|
+
assert_raise ArgumentError do
|
90
|
+
@server.add_record 'default', { :text => 'hello world' }
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'simple_collection_tests'
|
3
|
+
|
4
|
+
class StaticCollectionTest < StellrTest
|
5
|
+
include SimpleCollectionTests
|
6
|
+
fixtures :movies
|
7
|
+
|
8
|
+
def setup
|
9
|
+
super
|
10
|
+
@collection = Stellr::Collections::Static.new( 'default', default_collection_options )
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_create
|
14
|
+
c = Stellr::Collections::Base.create( 'test', default_collection_options(:collection_class => 'Stellr::Collections::Static') )
|
15
|
+
assert_equal Stellr::Collections::Static, c.class
|
16
|
+
c.close
|
17
|
+
c = Stellr::Collections::Base.create( 'test', default_collection_options(:collection => :static) )
|
18
|
+
assert_equal Stellr::Collections::Static, c.class
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_switch
|
22
|
+
#assert_equal 0, @collection.size
|
23
|
+
index_something
|
24
|
+
assert_equal 0, @collection.size
|
25
|
+
@collection.switch
|
26
|
+
assert_equal 2, @collection.size
|
27
|
+
|
28
|
+
# switchen ohne vorheriges indizieren leert index
|
29
|
+
@collection.switch
|
30
|
+
assert_equal 0, @collection.size
|
31
|
+
@collection.switch
|
32
|
+
assert_equal 0, @collection.size
|
33
|
+
end
|
34
|
+
|
35
|
+
protected
|
36
|
+
|
37
|
+
def default_collection_options( options = {} )
|
38
|
+
{ :path => INDEX_TMP_TEST_DIR, :logger => Logger.new('/tmp/stellr/test.log') }.update( options )
|
39
|
+
end
|
40
|
+
end
|
data/test/test_stellr.rb
ADDED
metadata
ADDED
@@ -0,0 +1,110 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: stellr
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Benjamin Krause
|
8
|
+
- "Jens Kr\xC3\xA4mer"
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
|
13
|
+
date: 2008-08-04 00:00:00 +02:00
|
14
|
+
default_executable:
|
15
|
+
dependencies:
|
16
|
+
- !ruby/object:Gem::Dependency
|
17
|
+
name: hoe
|
18
|
+
type: :development
|
19
|
+
version_requirement:
|
20
|
+
version_requirements: !ruby/object:Gem::Requirement
|
21
|
+
requirements:
|
22
|
+
- - ">="
|
23
|
+
- !ruby/object:Gem::Version
|
24
|
+
version: 1.7.0
|
25
|
+
version:
|
26
|
+
description: "== FEATURES: * DRb frontend * easy to use client library (see below) * multi index search * Index rotation Stellr always keeps two versions of your index around - one is used in a multi threaded, read only way to handle incoming search requests, while the other one is written to when you index something. Using the switch function you may decide when to switch over searching from the old index to the new one. Then, changes will be synced, and searches will see the new or updated data from before the switch call. * Index synchronization Two kinds of synchronization methods are supported for now: rsync, using rsync two copy over the changes from one index to the other, and static, which will completely replace the old index with the new one. While the latter is suitable for indexes which you rebuild completely from time to time, the former is good for large indexes that are updated frequently or that are too large for frequent rebuilds. == SYNOPSIS: * start the server:"
|
27
|
+
email:
|
28
|
+
- bk@benjaminkrause.com
|
29
|
+
- jk@jkraemer.net
|
30
|
+
executables:
|
31
|
+
- stellr
|
32
|
+
- stellr-search
|
33
|
+
extensions: []
|
34
|
+
|
35
|
+
extra_rdoc_files:
|
36
|
+
- History.txt
|
37
|
+
- Manifest.txt
|
38
|
+
- README.txt
|
39
|
+
files:
|
40
|
+
- History.txt
|
41
|
+
- Manifest.txt
|
42
|
+
- README.txt
|
43
|
+
- Rakefile
|
44
|
+
- bin/stellr
|
45
|
+
- bin/stellr-search
|
46
|
+
- config/stellr.yml
|
47
|
+
- lib/stellr.rb
|
48
|
+
- lib/stellr/client.rb
|
49
|
+
- lib/stellr/collections.rb
|
50
|
+
- lib/stellr/collections/base.rb
|
51
|
+
- lib/stellr/collections/searchable_collection.rb
|
52
|
+
- lib/stellr/collections/writeable_collection.rb
|
53
|
+
- lib/stellr/collections/rsync.rb
|
54
|
+
- lib/stellr/collections/static.rb
|
55
|
+
- lib/stellr/collections/multi_collection.rb
|
56
|
+
- lib/stellr/config.rb
|
57
|
+
- lib/stellr/search.rb
|
58
|
+
- lib/stellr/search/search_result.rb
|
59
|
+
- lib/stellr/search/search_results.rb
|
60
|
+
- lib/stellr/server.rb
|
61
|
+
- lib/stellr/strategies.rb
|
62
|
+
- lib/stellr/strategies/base.rb
|
63
|
+
- lib/stellr/strategies/blocking.rb
|
64
|
+
- lib/stellr/strategies/queueing.rb
|
65
|
+
- lib/stellr/utils.rb
|
66
|
+
- lib/stellr/utils/shutdown.rb
|
67
|
+
- lib/stellr/utils/observable.rb
|
68
|
+
- test/fixtures/movies.yml
|
69
|
+
- test/stellr_test.rb
|
70
|
+
- test/test_collections_base.rb
|
71
|
+
- test/test_helper.rb
|
72
|
+
- test/test_rsync_collection.rb
|
73
|
+
- test/test_server.rb
|
74
|
+
- test/test_static_collection.rb
|
75
|
+
- test/test_stellr.rb
|
76
|
+
has_rdoc: true
|
77
|
+
homepage: " by Jens Kraemer and Benjamin Krause"
|
78
|
+
post_install_message:
|
79
|
+
rdoc_options:
|
80
|
+
- --main
|
81
|
+
- README.txt
|
82
|
+
require_paths:
|
83
|
+
- lib
|
84
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - ">="
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: "0"
|
89
|
+
version:
|
90
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
91
|
+
requirements:
|
92
|
+
- - ">="
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
version: "0"
|
95
|
+
version:
|
96
|
+
requirements: []
|
97
|
+
|
98
|
+
rubyforge_project: stellr
|
99
|
+
rubygems_version: 1.2.0
|
100
|
+
signing_key:
|
101
|
+
specification_version: 2
|
102
|
+
summary: Stellr is a Ferret based standalone search server.
|
103
|
+
test_files:
|
104
|
+
- test/test_client.rb
|
105
|
+
- test/test_collections_base.rb
|
106
|
+
- test/test_helper.rb
|
107
|
+
- test/test_rsync_collection.rb
|
108
|
+
- test/test_server.rb
|
109
|
+
- test/test_static_collection.rb
|
110
|
+
- test/test_stellr.rb
|