Tracker 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/MIT-LICENSE +9 -0
- data/README +18 -0
- data/bin/fixture.rb +37 -0
- data/bin/package +12 -0
- data/bin/trac +65 -0
- data/db/migrate +70 -0
- data/lib/action.rb +79 -0
- data/lib/bug.rb +63 -0
- data/lib/bug_description.rb +15 -0
- data/lib/database.rb +44 -0
- data/lib/helper.rb +16 -0
- data/lib/interface.rb +161 -0
- data/lib/prompter.rb +66 -0
- data/tests/fixtures.yml +3 -0
- data/tests/tc_action.rb +135 -0
- data/tests/tc_bug.rb +65 -0
- data/tests/tc_bug_description.rb +32 -0
- data/tests/tc_integration.rb +0 -0
- data/tests/tc_interface.rb +9 -0
- data/tests/ts_tracker.rb +13 -0
- metadata +82 -0
data/MIT-LICENSE
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
The MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2006 Hugh Bien
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
6
|
+
|
7
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
8
|
+
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
== Using tracker (or whatever it's named)
|
2
|
+
tracker help
|
3
|
+
Can be used with alternative file, use the -f option.
|
4
|
+
tracker -f /path/to/sqlite3_file
|
5
|
+
|
6
|
+
== Dependencies
|
7
|
+
Ruby 1.8
|
8
|
+
SWIG
|
9
|
+
SQLite3
|
10
|
+
SQLite3-Ruby interface
|
11
|
+
|
12
|
+
== To add a new action
|
13
|
+
* Implement it in Bug class (for whatever bug needs that doesn't have)
|
14
|
+
* Add the command to the factory in lib/factory.rb
|
15
|
+
* Make sure the options are valid. You can add new options in the hash in tracker.rb.
|
16
|
+
|
17
|
+
== LICENSE
|
18
|
+
See MIT-LICENSE.
|
data/bin/fixture.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "yaml"
|
4
|
+
require "rubygems"
|
5
|
+
require_gem "activerecord"
|
6
|
+
|
7
|
+
# Load the fixtures file and database
|
8
|
+
fixtures_file = ARGV[0] || "tests/fixtures.yml"
|
9
|
+
database = ARGV[1] || ".tracker_db"
|
10
|
+
|
11
|
+
# Load the YAML information
|
12
|
+
yaml = YAML.load_file(fixtures_file)
|
13
|
+
|
14
|
+
# Connect to the database file
|
15
|
+
ActiveRecord::Base.establish_connection(
|
16
|
+
:adapter => "sqlite3",
|
17
|
+
:database => database
|
18
|
+
)
|
19
|
+
|
20
|
+
class Bug < ActiveRecord::Base
|
21
|
+
end
|
22
|
+
|
23
|
+
class BugDescription < ActiveRecord::Base
|
24
|
+
end
|
25
|
+
|
26
|
+
class String
|
27
|
+
def to_constant
|
28
|
+
return Bug if self == "Bug"
|
29
|
+
return BugDescription if self == "BugDescription"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
yaml.each_pair do |key, value|
|
34
|
+
key.to_constant.create( value )
|
35
|
+
end
|
36
|
+
|
37
|
+
|
data/bin/package
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
#!/usr/bin/env bash
|
2
|
+
|
3
|
+
cd lib
|
4
|
+
cat *.rb >> final_package
|
5
|
+
mv final_package ..
|
6
|
+
cd ..
|
7
|
+
cat tracker >> final_package
|
8
|
+
sed -e 's/^require.*$//g' final_package > temp
|
9
|
+
rm final_package
|
10
|
+
cat lib/requires temp > final_package
|
11
|
+
rm temp
|
12
|
+
chmod a+x final_package
|
data/bin/trac
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# Tracker is under the MIT-LICENSE
|
3
|
+
# Please see http://ieng6.ucsd.edu/~hbien/tracker for more information about Tracker.
|
4
|
+
|
5
|
+
# Handles connection to the database
|
6
|
+
|
7
|
+
require "rubygems"
|
8
|
+
require_gem "Tracker"
|
9
|
+
|
10
|
+
# The default database file_name
|
11
|
+
DEFAULT_DATABASE_FILE_NAME = ".tracker_db"
|
12
|
+
|
13
|
+
# -----------------------------------------------------------------------------
|
14
|
+
# Begin executing the script here.
|
15
|
+
# -----------------------------------------------------------------------------
|
16
|
+
|
17
|
+
# What file should we use for the database?
|
18
|
+
database = DEFAULT_DATABASE_FILE_NAME
|
19
|
+
|
20
|
+
# What to do if no database can be found
|
21
|
+
def no_database_error(database_file = "the current")
|
22
|
+
puts "Could not find '.tracker_db' in #{database_file} or any parent directories."
|
23
|
+
puts "Try '#{File.basename(__FILE__)} create' to create a new database in the current directory or '#{File.basename(__FILE__)} help' to see available commands."
|
24
|
+
exit
|
25
|
+
end
|
26
|
+
|
27
|
+
# Is the user creating a new database?
|
28
|
+
if ARGV.include?("create")
|
29
|
+
if File.exists?(database)
|
30
|
+
puts "A '.tracker_db' database already exists in the current directory. Try removing it to create a new one."
|
31
|
+
exit
|
32
|
+
else
|
33
|
+
Database::create_database(database)
|
34
|
+
exit
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# Find the database in parent directories
|
39
|
+
iteration = 0
|
40
|
+
while !File.exists?(database) && iteration < 10
|
41
|
+
iteration += 1
|
42
|
+
database = File.join("..", database)
|
43
|
+
end
|
44
|
+
|
45
|
+
# Could we find the database?
|
46
|
+
if iteration == 10 && !ARGV.include?("help") && !ARGV.include?("--file") && !ARGV.include?("-f")
|
47
|
+
no_database_error
|
48
|
+
else
|
49
|
+
if ARGV.include?("-f") || ARGV.include?("--file")
|
50
|
+
index = ARGV.index("-f") + 1 if ARGV.include?("-f")
|
51
|
+
index = ARGV.index("--file") + 1 if ARGV.include?("--file")
|
52
|
+
database = ARGV[index]
|
53
|
+
database = File.join(database, ".tracker_db") if File.directory?(database)
|
54
|
+
no_database_error(database) if !File.exist?(database)
|
55
|
+
end
|
56
|
+
# if we did, execute command
|
57
|
+
Database::connect_to_database(database)
|
58
|
+
parser = Interface.new
|
59
|
+
begin
|
60
|
+
parser.parse_command(ARGV)
|
61
|
+
rescue Exception => error
|
62
|
+
puts "Error executing database instructions: " + error
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
data/db/migrate
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "rubygems"
|
4
|
+
require_gem "activerecord"
|
5
|
+
|
6
|
+
puts "Removing the database..."
|
7
|
+
`rm .tracker_db` if File.exists?(".tracker_db")
|
8
|
+
|
9
|
+
# Connect to the database
|
10
|
+
ActiveRecord::Base.establish_connection(
|
11
|
+
:adapter => "sqlite3",
|
12
|
+
:database => ".tracker_db"
|
13
|
+
)
|
14
|
+
|
15
|
+
# Create the bugs table
|
16
|
+
ActiveRecord::Schema.define do
|
17
|
+
create_table "bugs" do |t|
|
18
|
+
t.column "name", :string
|
19
|
+
t.column "priority", :int
|
20
|
+
t.column "area", :string
|
21
|
+
t.column "status", :boolean, :default => false
|
22
|
+
t.column "date_time", :date_time
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# Creates the bug_descriptions
|
27
|
+
ActiveRecord::Schema.define do
|
28
|
+
create_table "bug_descriptions" do |t|
|
29
|
+
t.column "bug_id", :int
|
30
|
+
t.column "date_time", :date_time
|
31
|
+
t.column "description", :text
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
class Bug < ActiveRecord::Base
|
36
|
+
has_many :bug_descriptions
|
37
|
+
end
|
38
|
+
|
39
|
+
class BugDescription < ActiveRecord::Base
|
40
|
+
belongs_to :bugs
|
41
|
+
end
|
42
|
+
|
43
|
+
puts "Populating database with test data..."
|
44
|
+
yesterday = 1.day.ago.to_s(:db)
|
45
|
+
today = Time.now.to_s(:db)
|
46
|
+
tomorrow = 1.day.from_now.to_s(:db)
|
47
|
+
|
48
|
+
# Add test data to the database
|
49
|
+
bug = Bug.create(:name => "First", :priority => 1, :area => "One", :date_time => yesterday, :status => true)
|
50
|
+
bug.bug_descriptions.create(:date_time => yesterday, :description => "First description.\n")
|
51
|
+
bug.bug_descriptions.create(:date_time => today, :description => "Second description.\n")
|
52
|
+
bug.bug_descriptions.create(:date_time => today, :description => "Third description.\n")
|
53
|
+
|
54
|
+
bug = Bug.create(:name => "Second", :priority => 2, :area => "Two", :date_time => today, :status => true)
|
55
|
+
bug.bug_descriptions.create(:date_time => yesterday, :description => "First description.\n")
|
56
|
+
bug.bug_descriptions.create(:date_time => today, :description => "Second description.\n")
|
57
|
+
bug.bug_descriptions.create(:date_time => today, :description => "Third description.\n")
|
58
|
+
|
59
|
+
bug = Bug.create(:name => "Third", :priority => 3, :area => "Three", :date_time => tomorrow)
|
60
|
+
bug.bug_descriptions.create(:date_time => yesterday, :description => "First description.\n")
|
61
|
+
bug.bug_descriptions.create(:date_time => today, :description => "Second description.\n")
|
62
|
+
bug.bug_descriptions.create(:date_time => today, :description => "Third description.\n")
|
63
|
+
|
64
|
+
bug = Bug.create(:name => "Fourth", :priority => 4, :area => "Four", :date_time => tomorrow)
|
65
|
+
bug.bug_descriptions.create(:date_time => yesterday, :description => "First description.\n")
|
66
|
+
bug.bug_descriptions.create(:date_time => today, :description => "Second description.\n")
|
67
|
+
bug.bug_descriptions.create(:date_time => today, :description => "Third description.\n")
|
68
|
+
|
69
|
+
|
70
|
+
|
data/lib/action.rb
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/bug.rb"
|
2
|
+
require File.dirname(__FILE__) + "/helper.rb"
|
3
|
+
|
4
|
+
# The Action module is just a simple interface for the Bug class. Use this instead of using the Bug class directly, it should satisfy all actions needed to CRUD from the bugs database.
|
5
|
+
module Action
|
6
|
+
# Adds a new description to an existing bug. Used when a user wants to add new information found about a bug. Takes a hash as parameter where one item is either :name or :id and the other is :description
|
7
|
+
def self.append_description(hash)
|
8
|
+
return nil if hash[:description] =~ /^\s*$/ # Returns nil if no description given
|
9
|
+
bug = Bug.search(hash)
|
10
|
+
bug.bug_descriptions.create(:description => hash[:description])
|
11
|
+
end
|
12
|
+
|
13
|
+
# Closes the bug from being listed.
|
14
|
+
def self.close_bug(hash)
|
15
|
+
bug = Bug.search(hash)
|
16
|
+
bug.update_attributes(:status => true)
|
17
|
+
end
|
18
|
+
|
19
|
+
# Delets the bug with the given id/name and associated bug descriptions
|
20
|
+
def self.delete_bug(hash)
|
21
|
+
bug = Bug.search(hash)
|
22
|
+
bug.destroy
|
23
|
+
end
|
24
|
+
|
25
|
+
# Lists all of the open bugs by whatever conditions are meant
|
26
|
+
# The intersection of the conditions are returned
|
27
|
+
# By default, status is false
|
28
|
+
def self.list_bugs(hash = Hash.new)
|
29
|
+
hash[:status] = false unless hash.has_key?(:status)
|
30
|
+
bugs = Bug.find(:all, :conditions => Helper::hash_to_sql(hash), :order => "priority DESC, date_time")
|
31
|
+
return bugs
|
32
|
+
end
|
33
|
+
|
34
|
+
# Takes a hash as a parameter which is the data for the bug.
|
35
|
+
# * name - Descriptive name used for searching
|
36
|
+
# * priority - an integer, 1 being the highest priority, as large a number as you want.
|
37
|
+
# * area - Area, can be the filename, classname, or programmer name, etc.
|
38
|
+
# * description - Quick description, what was expected, what actually happened, and how to reproduce the bug.
|
39
|
+
def self.new_bug(hash)
|
40
|
+
description = hash[:description]
|
41
|
+
hash.delete(:description)
|
42
|
+
bug = Bug.create(hash)
|
43
|
+
bug.bug_descriptions.create(:description => description)
|
44
|
+
return bug
|
45
|
+
end
|
46
|
+
|
47
|
+
# Opens a bug to be listed.
|
48
|
+
def self.open_bug(hash)
|
49
|
+
bug = Bug.search(hash)
|
50
|
+
bug.update_attributes(:status => false)
|
51
|
+
end
|
52
|
+
|
53
|
+
# Updates the given bug with new information
|
54
|
+
def self.update_bug(hash)
|
55
|
+
bug = Bug.search(hash)
|
56
|
+
bug.update_attributes(hash)
|
57
|
+
end
|
58
|
+
|
59
|
+
# Returns all of the areas for the open bugs
|
60
|
+
def self.list_areas(*args)
|
61
|
+
# Find all open bugs
|
62
|
+
bugs = list_bugs(*args)
|
63
|
+
|
64
|
+
# Get all of the unique areas from the bugs
|
65
|
+
areas = bugs.collect { |bug| bug.area }
|
66
|
+
return areas.uniq
|
67
|
+
end
|
68
|
+
|
69
|
+
# Returns all of the names of the open bugs
|
70
|
+
def self.list_names(*args)
|
71
|
+
# Find all open bugs
|
72
|
+
bugs = list_bugs(*args)
|
73
|
+
|
74
|
+
# Get all of the uniqe names from the bugs
|
75
|
+
names = bugs.collect { |bug| bug.name }
|
76
|
+
return names.uniq
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
data/lib/bug.rb
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require_gem 'activerecord'
|
3
|
+
|
4
|
+
# Fields are name (string), priority (int), area (string), status (boolean).
|
5
|
+
class Bug < ActiveRecord::Base
|
6
|
+
has_many :bug_descriptions, :exclusively_dependent => true
|
7
|
+
before_create :save_date_time
|
8
|
+
before_update :save_date_time
|
9
|
+
|
10
|
+
# Is the bug open?
|
11
|
+
def open?
|
12
|
+
!status
|
13
|
+
end
|
14
|
+
|
15
|
+
# Is the bug closed?
|
16
|
+
def closed?
|
17
|
+
status
|
18
|
+
end
|
19
|
+
|
20
|
+
# Searches for a bug with given name or id
|
21
|
+
def self.search(term=nil)
|
22
|
+
# Get the needed search term if the argument is a hash
|
23
|
+
term = self.get_term(term) if term.kind_of?(Hash)
|
24
|
+
|
25
|
+
# Find the right bug
|
26
|
+
bug = self.get_bug_from_id_or_name(term)
|
27
|
+
|
28
|
+
# What to do if the bug isn't found
|
29
|
+
if bug == nil
|
30
|
+
raise Exception, "Bug not found with name '#{term}'." if term.kind_of?(String)
|
31
|
+
raise Exception, "Bug not found with id \##{term}." if term.kind_of?(Fixnum)
|
32
|
+
end
|
33
|
+
|
34
|
+
# Return the bug
|
35
|
+
return bug
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
# Makes sure the time is saved whenever a bug is saved or modified
|
40
|
+
def save_date_time
|
41
|
+
self.date_time = Time.now.to_s(:db)
|
42
|
+
end
|
43
|
+
|
44
|
+
# Gets the term (name or id) out of a hash
|
45
|
+
def self.get_term(hash)
|
46
|
+
term = hash[:name] if hash.has_key?(:name)
|
47
|
+
term = hash[:id] if hash.has_key?(:id)
|
48
|
+
return term
|
49
|
+
end
|
50
|
+
|
51
|
+
# Get the bug when given an id or name
|
52
|
+
def self.get_bug_from_id_or_name(search_key)
|
53
|
+
if search_key.kind_of?(String)
|
54
|
+
bug = find_by_name(search_key)
|
55
|
+
elsif search_key.kind_of?(Fixnum)
|
56
|
+
bug = find_by_id(search_key)
|
57
|
+
end
|
58
|
+
return bug
|
59
|
+
end
|
60
|
+
|
61
|
+
alias_method :opened?, :open?
|
62
|
+
|
63
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require_gem 'activerecord'
|
3
|
+
|
4
|
+
# Fields are bug_id (int), date (date), description (text).
|
5
|
+
class BugDescription < ActiveRecord::Base
|
6
|
+
belongs_to :bug
|
7
|
+
before_save :save_date_time
|
8
|
+
before_update :save_date_time
|
9
|
+
|
10
|
+
private
|
11
|
+
# Saves date_time as current time
|
12
|
+
def save_date_time
|
13
|
+
self.date_time = Time.now.to_s(:db)
|
14
|
+
end
|
15
|
+
end
|
data/lib/database.rb
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
require "rubygems"
|
2
|
+
require_gem "activerecord"
|
3
|
+
|
4
|
+
# This module handles the connection to a database. There are only two methods, one to connect, one to create (connect_to_database and create_database, repsectively).
|
5
|
+
module Database
|
6
|
+
|
7
|
+
# This method should be called when starting the script to connect the program
|
8
|
+
# to the database.
|
9
|
+
def self.connect_to_database(database)
|
10
|
+
# Start the script by connecting to the sqlite3 datbase
|
11
|
+
ActiveRecord::Base.establish_connection(
|
12
|
+
:adapter => "sqlite3",
|
13
|
+
:database => database
|
14
|
+
)
|
15
|
+
end
|
16
|
+
|
17
|
+
# If no database currently exists, this method will create one using
|
18
|
+
# ActiveRecord's migrations
|
19
|
+
def self.create_database(database)
|
20
|
+
puts "Creating database '#{database}' in current directory..."
|
21
|
+
connect_to_database(database)
|
22
|
+
|
23
|
+
# Creates the bugs table
|
24
|
+
ActiveRecord::Schema.define do
|
25
|
+
create_table "bugs" do |t|
|
26
|
+
t.column "name", :string
|
27
|
+
t.column "priority", :int
|
28
|
+
t.column "area", :string
|
29
|
+
t.column "status", :boolean, :default => false
|
30
|
+
t.column "date_time", :datetime
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# Creates the bug_descriptions
|
35
|
+
ActiveRecord::Schema.define do
|
36
|
+
create_table "bug_descriptions" do |t|
|
37
|
+
t.column "bug_id", :int
|
38
|
+
t.column "date_time", :datetime
|
39
|
+
t.column "description", :text
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
data/lib/helper.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
# This module includes static methods that do not fall into cateogries. These are just helper methods for various algorithms throughout the source code.
|
2
|
+
module Helper
|
3
|
+
|
4
|
+
# Takes a hash and returns the conditions sql for an ActiveRecord find method
|
5
|
+
def self.hash_to_sql(hash)
|
6
|
+
conditions = Array.new
|
7
|
+
hash.each do |attribute, term|
|
8
|
+
conditions << "#{attribute} = ?"
|
9
|
+
end
|
10
|
+
sql_string = conditions.join(" and ")
|
11
|
+
sql = [sql_string]
|
12
|
+
hash.each { |attribute, term| sql << term }
|
13
|
+
return sql
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
data/lib/interface.rb
ADDED
@@ -0,0 +1,161 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/prompter.rb"
|
2
|
+
require File.dirname(__FILE__) + "/database.rb"
|
3
|
+
require File.dirname(__FILE__) + "/action.rb"
|
4
|
+
require File.dirname(__FILE__) + "/bug.rb"
|
5
|
+
require File.dirname(__FILE__) + "/bug_description.rb"
|
6
|
+
|
7
|
+
# The Interface's job is to act as the interface between the user and the Action module. The Interface will make sure that messages are outputted to the user and pass along actions to the Action module depending on whatever the command is.
|
8
|
+
class Interface
|
9
|
+
|
10
|
+
# Let the tracker interface have access to the bugger - controller
|
11
|
+
def initialize()
|
12
|
+
@filename = File.basename(__FILE__)
|
13
|
+
@all_options = ["name", "priority", "area", "description"]
|
14
|
+
end
|
15
|
+
|
16
|
+
# Parses ARGV and executes the correct bugger operations
|
17
|
+
# Will print out to the user whatever the user requested
|
18
|
+
def parse_command(command_line_arguments)
|
19
|
+
# Parse the command
|
20
|
+
hash = args_to_hash(command_line_arguments)
|
21
|
+
command = command_line_arguments[0]
|
22
|
+
if command_line_arguments.size > 1 && command_line_arguments[1][0,1] != "-"
|
23
|
+
get_name_or_id(command_line_arguments[1], hash)
|
24
|
+
end
|
25
|
+
|
26
|
+
# Perform the correct action for whatever command it is
|
27
|
+
if command.include?("append") # Appends a description to existing bug
|
28
|
+
hash = include_name_or_id(hash)
|
29
|
+
hash[:description] = Prompter.description? if !hash.has_key?(:description)
|
30
|
+
Action::append_description(hash)
|
31
|
+
elsif command.include?("areas")
|
32
|
+
puts "Searched ticket areas are:"
|
33
|
+
Action::list_areas(hash).each { |area| puts " * " + area }
|
34
|
+
elsif command.include?("names")
|
35
|
+
puts "Searched ticket names are:"
|
36
|
+
Action::list_names(hash).each { |name| puts " * " + name }
|
37
|
+
elsif command.include?("close") # Closes an existing bug
|
38
|
+
hash = include_name_or_id(hash)
|
39
|
+
Action::close_bug(hash)
|
40
|
+
elsif command.include?("delete") # Deletes the bug from the database
|
41
|
+
hash = include_name_or_id(hash)
|
42
|
+
Action::delete_bug(hash)
|
43
|
+
elsif command.include?("list") # Lists either all open bugs or bugs by conditions
|
44
|
+
bugs = Action::list_bugs(hash)
|
45
|
+
puts bugs.collect { |bug| get_print_info(bug) }.join("\n") if bugs.size > 0
|
46
|
+
elsif command.include?("new") # Creates a new bug
|
47
|
+
set_options(@all_options, hash)
|
48
|
+
Action::new_bug(hash)
|
49
|
+
elsif command.include?("open") # Opens a closed bug
|
50
|
+
hash = include_name_or_id(hash)
|
51
|
+
Action::open_bug(hash)
|
52
|
+
elsif command.include?("update")
|
53
|
+
hash = include_name_or_id(hash)
|
54
|
+
Action::update_bug(hash)
|
55
|
+
elsif command.include?("help") || command.include?("--help") # Prints usage
|
56
|
+
print_usage
|
57
|
+
else
|
58
|
+
puts "Try '#{@filename} help'."
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
|
63
|
+
private
|
64
|
+
# Changes ARGV into a hash, ie. trac --name NAME --priority 2 becomes {:name => "NAME", :priority => 2}
|
65
|
+
def args_to_hash(args)
|
66
|
+
hash = Hash.new
|
67
|
+
hash[:id] = args[args.index("--id") + 1].to_i if args.include?("--id")
|
68
|
+
hash[:id] = args[args.index("-i") + 1].to_i if args.include?("-i")
|
69
|
+
hash[:name] = args[args.index("--name") + 1] if args.include?("--name")
|
70
|
+
hash[:name] = args[args.index("-n") + 1] if args.include?("-n")
|
71
|
+
hash[:priority] = args[args.index("--priority") + 1].to_i if args.include?("--priority")
|
72
|
+
hash[:priority] = args[args.index("-p") + 1].to_i if args.include?("-p")
|
73
|
+
hash[:area] = args[args.index("--area") + 1] if args.include?("--area")
|
74
|
+
hash[:area] = args[args.index("-a") + 1] if args.include?("-a")
|
75
|
+
hash[:description] = args[args.index("--description") + 1] if args.include?("--description")
|
76
|
+
hash[:description] = args[args.index("-d") + 1] if args.include?("-d")
|
77
|
+
hash[:status] = false if args.include?("--open") or args.include?("-o")
|
78
|
+
hash[:status] = true if args.include?("--closed") or args.include?("-c")
|
79
|
+
return hash
|
80
|
+
end
|
81
|
+
|
82
|
+
# Inspects the hash 'given' to see if it has the 'needed' keys. If it doesn't,
|
83
|
+
# will prompt the user for items.
|
84
|
+
def set_options(needed, given)
|
85
|
+
needed.each do |item|
|
86
|
+
if !given.has_key?(item.to_sym)
|
87
|
+
given[item.to_sym] = Prompter.send(item+"?")
|
88
|
+
end
|
89
|
+
end
|
90
|
+
return given
|
91
|
+
end
|
92
|
+
|
93
|
+
# If the given hash doesn't include an :id or :name key, prompts the user for
|
94
|
+
# an :id key and inserts it into the hash
|
95
|
+
def include_name_or_id(hash)
|
96
|
+
return hash if hash.has_key?(:name) or hash.has_key?(:id)
|
97
|
+
hash[:id] = Prompter.id?
|
98
|
+
return hash
|
99
|
+
end
|
100
|
+
|
101
|
+
# Determines if a given argument is an id or name and includes it in a hash as
|
102
|
+
# long as no other id or name was already included in the hash
|
103
|
+
def get_name_or_id(name_or_id, hash = Hash.new)
|
104
|
+
hash[:id] = name_or_id.to_i if name_or_id.to_i.kind_of?(Fixnum) && !hash.has_key?(:name) && name_or_id.to_i != 0
|
105
|
+
hash[:name] = name_or_id if name_or_id.kind_of?(String) && !hash.has_key?(:id)
|
106
|
+
return hash
|
107
|
+
end
|
108
|
+
|
109
|
+
# Returns the info for a bug to be printed out to the user
|
110
|
+
def get_print_info(bug)
|
111
|
+
working_string = String.new
|
112
|
+
working_string << "Ticket(#{bug.id}): #{bug.name}\n" if bug.id < 10
|
113
|
+
working_string << "Ticket(#{bug.id}): #{bug.name}\n" if bug.id >= 10 && bug.id < 100
|
114
|
+
working_string << "Ticket(#{bug.id}): #{bug.name}\n" if bug.id >= 100
|
115
|
+
working_string << "Priority: #{bug.priority}\n"
|
116
|
+
working_string << "Area: #{bug.area}\n"
|
117
|
+
working_string << "Description - #{bug.date_time.to_s(:short)}:\n"
|
118
|
+
descriptions = bug.bug_descriptions.collect { |desc| " * " + desc.description }.join
|
119
|
+
working_string << descriptions
|
120
|
+
return working_string
|
121
|
+
end
|
122
|
+
|
123
|
+
# Prints a description out
|
124
|
+
def print_description(description)
|
125
|
+
puts description.description # The description method
|
126
|
+
puts "-"
|
127
|
+
end
|
128
|
+
|
129
|
+
# Tracker's usage
|
130
|
+
def print_usage
|
131
|
+
puts "usage: #{@filename} <command> [options] [-f DIRECTORY | DATABASE]"
|
132
|
+
puts ""
|
133
|
+
puts "Available commands:"
|
134
|
+
puts " create"
|
135
|
+
puts " append [-i ID | -n NAME | -d DESCRIPTION]"
|
136
|
+
puts " close [-i ID | -n NAME]"
|
137
|
+
puts " delete [-i ID | -n NAME]"
|
138
|
+
puts " list [-i ID | -n NAME | -p PRIORITY | -a AREA | -c | -o]"
|
139
|
+
puts " help"
|
140
|
+
puts " new [-n NAME | -p PRIORITY | -a AREA | -d DESCRIPTION]"
|
141
|
+
puts " open [-i ID | -n NAME]"
|
142
|
+
puts " update [-i ID | -n NAME | -p PRIORITY | -a AREA]"
|
143
|
+
puts " areas [-n NAME | -p PRIORITY | -c | -o]"
|
144
|
+
puts " names [-a AREA | -p PRIORITY | -c | -o]"
|
145
|
+
puts ""
|
146
|
+
puts "Available options:"
|
147
|
+
puts " -i ID | --id ID"
|
148
|
+
puts " -n NAME | --name NAME"
|
149
|
+
puts " -p PRIORITY | --priority PRIORITY"
|
150
|
+
puts " -a AREA | --area AREA"
|
151
|
+
puts " -d DESCRIPTION | --description DESCRIPTION"
|
152
|
+
puts " -c | --closed"
|
153
|
+
puts " -o | --open"
|
154
|
+
puts " -f FILE | --file FILE"
|
155
|
+
puts ""
|
156
|
+
puts "Tracker is a command-line client for a bugs/features database. The database is kept in a flat file '.tracker_db' in the current directory."
|
157
|
+
puts ""
|
158
|
+
puts "To create a '.tracker_db' file, try using '#{@filename} create'."
|
159
|
+
end
|
160
|
+
|
161
|
+
end
|
data/lib/prompter.rb
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
# Prompts the user for information. All prompts are contained into this class. All methods are class methods.
|
2
|
+
class Prompter
|
3
|
+
|
4
|
+
# Asks for id
|
5
|
+
def self.id?
|
6
|
+
self.int("id")
|
7
|
+
end
|
8
|
+
|
9
|
+
# Asks for area
|
10
|
+
def self.area?
|
11
|
+
self.string("area")
|
12
|
+
end
|
13
|
+
|
14
|
+
# Ask for priority - int
|
15
|
+
def self.priority?
|
16
|
+
self.int("priority")
|
17
|
+
end
|
18
|
+
|
19
|
+
# Asks for a name
|
20
|
+
def self.name?
|
21
|
+
self.string("name")
|
22
|
+
end
|
23
|
+
|
24
|
+
# Asks for description. Terminate with ^d on empty line.
|
25
|
+
def self.description?
|
26
|
+
self.text("description")
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
# Prompt for a string
|
31
|
+
def self.string(term)
|
32
|
+
print term.capitalize + ": "
|
33
|
+
STDIN.gets.gsub("\n", "")
|
34
|
+
end
|
35
|
+
|
36
|
+
# Prompt for an int
|
37
|
+
def self.int(term)
|
38
|
+
print term.capitalize + ": "
|
39
|
+
STDIN.gets.to_i
|
40
|
+
end
|
41
|
+
|
42
|
+
# Prompt for text. Terminate with ^d on empty line.
|
43
|
+
def self.text(term)
|
44
|
+
# Create the method
|
45
|
+
puts term.capitalize + ":"
|
46
|
+
text = String.new
|
47
|
+
string = STDIN.gets
|
48
|
+
while string
|
49
|
+
text += string
|
50
|
+
string = STDIN.gets
|
51
|
+
end
|
52
|
+
return text
|
53
|
+
end
|
54
|
+
|
55
|
+
# Prompt for a yes or no.
|
56
|
+
def self.boolean(term)
|
57
|
+
print term.capitalize + ": "
|
58
|
+
string = STDIN.gets
|
59
|
+
if string.include?("Y") || string.include?("y")
|
60
|
+
return true
|
61
|
+
else
|
62
|
+
return false
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
data/tests/fixtures.yml
ADDED
data/tests/tc_action.rb
ADDED
@@ -0,0 +1,135 @@
|
|
1
|
+
require "test/unit"
|
2
|
+
|
3
|
+
require File.dirname(__FILE__) + "/../lib/action.rb"
|
4
|
+
require File.dirname(__FILE__) + "/../lib/database.rb"
|
5
|
+
require File.dirname(__FILE__) + "/../lib/bug_description.rb"
|
6
|
+
|
7
|
+
class TestBugger < Test::Unit::TestCase
|
8
|
+
def setup
|
9
|
+
Database::connect_to_database(".tracker_db")
|
10
|
+
@bug_names = ["First", "Second", "Third", "Fourth"]
|
11
|
+
@bug_ids = [1, 2, 3, 4]
|
12
|
+
@bug_priorities = [1, 2, 3, 4]
|
13
|
+
@bug_areas = ["One", "Two", "Three", "Four"]
|
14
|
+
@bug_status = [true, true, false, false]
|
15
|
+
@descriptions = ["First description", "Second description", "Third description"]
|
16
|
+
end
|
17
|
+
|
18
|
+
def teardown
|
19
|
+
# Delete all bugs that aren't in the migration
|
20
|
+
bugs = Bug.find(:all)
|
21
|
+
bugs.delete_if { |bug| @bug_ids.include?(bug.id) }
|
22
|
+
bugs.each { |bug| bug.destroy }
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_append_description
|
26
|
+
bug = Bug.create(:name => "test_append_description", :priority => 1, :area => "none")
|
27
|
+
Action::append_description(:id => bug.id, :description => "Hello")
|
28
|
+
Action::append_description(:name => "test_append_description", :description => "World")
|
29
|
+
assert_equal(bug.bug_descriptions[0].description, "Hello")
|
30
|
+
assert_equal(bug.bug_descriptions[1].description, "World")
|
31
|
+
end
|
32
|
+
|
33
|
+
# An empty description should NOT be appened.
|
34
|
+
def test_append_description_that_is_empty
|
35
|
+
bug = Bug.create(:name => "test_append_empty_description", :priority => 1, :area => "none")
|
36
|
+
descriptions_to_test = ["", " ", " ", "\n", "\n\n", " \n ", "\n ", " \n"]
|
37
|
+
descriptions_to_test.each do |string|
|
38
|
+
Action::append_description(:name => "test_append_empty_description", :description => string)
|
39
|
+
end
|
40
|
+
bug = Bug.find(bug.id)
|
41
|
+
assert_equal(0, bug.bug_descriptions.size)
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_close_bug
|
45
|
+
bug = Bug.find_by_status(false)
|
46
|
+
Action::close_bug(:id => bug.id)
|
47
|
+
bug = Bug.find_by_id(bug.id)
|
48
|
+
assert(bug.closed?)
|
49
|
+
bug.status = false
|
50
|
+
bug.save
|
51
|
+
end
|
52
|
+
|
53
|
+
def test_delete_bug
|
54
|
+
bug = Bug.create(:name => "test_delete_bug", :priority => 2, :area => "none")
|
55
|
+
Action::delete_bug(:name => "test_delete_bug")
|
56
|
+
assert(!Bug.find_by_name("test_delete_bug"))
|
57
|
+
end
|
58
|
+
|
59
|
+
def test_list_bugs
|
60
|
+
bugs = Action::list_bugs
|
61
|
+
bugs.each { |bug| assert(@bug_names.include?(bug.name)) }
|
62
|
+
bugs = Action::list_bugs(:name => "First")
|
63
|
+
assert_equal(0, bugs.size)
|
64
|
+
bugs = Action::list_bugs(:status => false)
|
65
|
+
assert_equal(2, bugs.size)
|
66
|
+
end
|
67
|
+
|
68
|
+
# Make sure the listed bugs with a condition are ALL opened
|
69
|
+
def test_open_list_bugs
|
70
|
+
first = Bug.create(:name => "Another open bug", :area => "Four")
|
71
|
+
second = Bug.create(:name => "Another closed bug", :area => "Four", :status => true)
|
72
|
+
bugs = Action::list_bugs(:area => "Four")
|
73
|
+
bugs.each { |bug| assert(bug.open?) }
|
74
|
+
end
|
75
|
+
|
76
|
+
def test_new_bug
|
77
|
+
bug = Action::new_bug(:name => "test_new_bug", :priority => 1, :area => "none", :description => "This is a test description")
|
78
|
+
assert(bug)
|
79
|
+
assert(Bug.find_by_name("test_new_bug"))
|
80
|
+
assert_equal(Bug.find_by_name("test_new_bug").name, "test_new_bug")
|
81
|
+
assert_equal(bug.bug_descriptions[0].description, "This is a test description")
|
82
|
+
end
|
83
|
+
|
84
|
+
def test_open_bug
|
85
|
+
bug = Bug.find_by_status(true)
|
86
|
+
Action::open_bug(:id => bug.id)
|
87
|
+
bug = Bug.find_by_id(bug.id)
|
88
|
+
assert(bug.open?)
|
89
|
+
bug.status = true
|
90
|
+
bug.save
|
91
|
+
end
|
92
|
+
|
93
|
+
def test_update_bug
|
94
|
+
bug = Bug.create(:name => "test_update_bug", :priority => 1, :area => "none")
|
95
|
+
assert(bug)
|
96
|
+
Action::update_bug(:id => bug.id, :name => "new_name", :priority => 2, :area => "Nothing")
|
97
|
+
bug = Bug.find_by_id(bug.id)
|
98
|
+
assert_equal("new_name", bug.name)
|
99
|
+
assert_equal("Nothing", bug.area)
|
100
|
+
assert_equal(2, bug.priority)
|
101
|
+
end
|
102
|
+
|
103
|
+
# Should list all _open_ areas
|
104
|
+
def test_list_areas
|
105
|
+
areas = Action::list_areas
|
106
|
+
known_areas = ["Four", "Three"]
|
107
|
+
areas.each { |area| assert(known_areas.include?(area)) }
|
108
|
+
end
|
109
|
+
|
110
|
+
# Should list all bug areas with the condition given
|
111
|
+
def test_list_areas_with_conditions
|
112
|
+
areas = Action::list_areas(:name => "Fourth")
|
113
|
+
assert_equal("Four", areas[0])
|
114
|
+
areas = Action::list_areas(:priority => 3)
|
115
|
+
assert_equal("Three", areas[0])
|
116
|
+
end
|
117
|
+
|
118
|
+
# Should list all _open_ bug names
|
119
|
+
def test_list_names
|
120
|
+
names = Action::list_names
|
121
|
+
known_names = ["Fourth", "Third"]
|
122
|
+
names.each { |name| assert(known_names.include?(name)) }
|
123
|
+
end
|
124
|
+
|
125
|
+
# Should list all _open_ bug names with the conditions given
|
126
|
+
def test_list_names_with_conditions
|
127
|
+
names = Action::list_names(:name => "Third")
|
128
|
+
assert_equal("Third", names[0])
|
129
|
+
names = Action::list_names(:priority => 2)
|
130
|
+
assert_equal(nil, names[0])
|
131
|
+
names = Action::list_names(:priority => 2, :status => true)
|
132
|
+
assert_equal("Second", names[0])
|
133
|
+
end
|
134
|
+
|
135
|
+
end
|
data/tests/tc_bug.rb
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
require "test/unit"
|
2
|
+
|
3
|
+
require File.dirname(__FILE__) + "/../lib/bug.rb"
|
4
|
+
|
5
|
+
class TestBug < Test::Unit::TestCase
|
6
|
+
def setup
|
7
|
+
Database::connect_to_database(".tracker_db")
|
8
|
+
@bug_names = ["First", "Second", "Third", "Fourth"]
|
9
|
+
@bug_ids = [1, 2, 3, 4]
|
10
|
+
@bug_priorities = [1, 2, 3, 4]
|
11
|
+
@bug_areas = ["One", "Two", "Three", "Four"]
|
12
|
+
@bug_status = [true, true, false, false]
|
13
|
+
@descriptions = ["First description", "Second description", "Third description"]
|
14
|
+
end
|
15
|
+
|
16
|
+
def teardown
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_open?
|
20
|
+
bug = Bug.find_by_name("Third")
|
21
|
+
assert(bug.open?)
|
22
|
+
assert(!bug.closed?)
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_closed?
|
26
|
+
bug = Bug.find_by_name("First")
|
27
|
+
assert(bug.closed?)
|
28
|
+
assert(!bug.open?)
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_search_by_name
|
32
|
+
@bug_names.each do |name|
|
33
|
+
bug = Bug.search(name)
|
34
|
+
assert_equal(name, bug.name)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_search_by_id
|
39
|
+
4.times do |i|
|
40
|
+
bug = Bug.search(@bug_ids[i])
|
41
|
+
assert_equal(@bug_names[i], bug.name)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_create_with_date_time
|
46
|
+
bug = Bug.create(:name => "New Bug!", :priority => 1, :area => "None")
|
47
|
+
assert(bug.date_time)
|
48
|
+
bug.destroy
|
49
|
+
end
|
50
|
+
|
51
|
+
def test_save_with_date_time
|
52
|
+
bug = Bug.new(:name => "Another new bug!", :priority => 2, :area => "None")
|
53
|
+
bug.save
|
54
|
+
assert(bug.date_time)
|
55
|
+
bug.destroy
|
56
|
+
end
|
57
|
+
|
58
|
+
def test_destroy
|
59
|
+
bug = Bug.create(:name => "test_destroy", :priority => 1, :area => "None")
|
60
|
+
id = bug.id
|
61
|
+
bug.destroy
|
62
|
+
assert_raise(ActiveRecord::RecordNotFound) { Bug.find(id) }
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require "test/unit"
|
2
|
+
|
3
|
+
require File.dirname(__FILE__) + "/../lib/bug_description.rb"
|
4
|
+
|
5
|
+
class TestBugDescription < Test::Unit::TestCase
|
6
|
+
def setup
|
7
|
+
Database::connect_to_database(".tracker_db")
|
8
|
+
@bug_names = ["First", "Second", "Third", "Fourth"]
|
9
|
+
@bug_ids = [1, 2, 3, 4]
|
10
|
+
@bug_priorities = [1, 2, 3, 4]
|
11
|
+
@bug_areas = ["One", "Two", "Three", "Four"]
|
12
|
+
@bug_status = [true, true, false, false]
|
13
|
+
@descriptions = ["First description", "Second description", "Third description"]
|
14
|
+
end
|
15
|
+
|
16
|
+
def teardown
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_create_with_date_time
|
21
|
+
description = BugDescription.create(:description => "A new description for the first bug.", :bug_id => 1)
|
22
|
+
assert(description.date_time)
|
23
|
+
description.destroy
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_save_with_date_time
|
27
|
+
description = BugDescription.new(:description => "A new description for the second bug.", :bug_id => 2)
|
28
|
+
description.save
|
29
|
+
assert(description.date_time)
|
30
|
+
description.destroy
|
31
|
+
end
|
32
|
+
end
|
File without changes
|
data/tests/ts_tracker.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# Adds current directory - test - and the lib directory to current PATH
|
4
|
+
$:.unshift File.join(File.dirname(__FILE__))
|
5
|
+
|
6
|
+
# require needed library files
|
7
|
+
require 'test/unit'
|
8
|
+
require File.dirname(__FILE__) + "/../lib/database.rb"
|
9
|
+
|
10
|
+
# Run all tests
|
11
|
+
test_cases = `ls #{File.dirname(__FILE__)}`.split("\n").select{ |f| f =~ /tc.*\.rb/ }.each do |file|
|
12
|
+
require File.join(File.dirname(__FILE__), file)
|
13
|
+
end
|
metadata
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
rubygems_version: 0.8.11
|
3
|
+
specification_version: 1
|
4
|
+
name: Tracker
|
5
|
+
version: !ruby/object:Gem::Version
|
6
|
+
version: 0.1.0
|
7
|
+
date: 2006-08-23 00:00:00 -07:00
|
8
|
+
summary: Command-line bug tracking database application.
|
9
|
+
require_paths:
|
10
|
+
- lib
|
11
|
+
email: bienhd@gmail.com
|
12
|
+
homepage: http://ieng6.ucsd.edu/~hbien/tracker
|
13
|
+
rubyforge_project:
|
14
|
+
description:
|
15
|
+
autorequire:
|
16
|
+
default_executable: trac
|
17
|
+
bindir: bin
|
18
|
+
has_rdoc: true
|
19
|
+
required_ruby_version: !ruby/object:Gem::Version::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">"
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 0.0.0
|
24
|
+
version:
|
25
|
+
platform: ruby
|
26
|
+
signing_key:
|
27
|
+
cert_chain:
|
28
|
+
authors:
|
29
|
+
- Hugh Bien
|
30
|
+
files:
|
31
|
+
- bin/fixture.rb
|
32
|
+
- bin/package
|
33
|
+
- bin/trac
|
34
|
+
- tests/fixtures.yml
|
35
|
+
- tests/tc_action.rb
|
36
|
+
- tests/tc_bug.rb
|
37
|
+
- tests/tc_bug_description.rb
|
38
|
+
- tests/tc_integration.rb
|
39
|
+
- tests/tc_interface.rb
|
40
|
+
- tests/ts_tracker.rb
|
41
|
+
- lib/action.rb
|
42
|
+
- lib/bug.rb
|
43
|
+
- lib/bug_description.rb
|
44
|
+
- lib/database.rb
|
45
|
+
- lib/helper.rb
|
46
|
+
- lib/interface.rb
|
47
|
+
- lib/prompter.rb
|
48
|
+
- db/migrate
|
49
|
+
- README
|
50
|
+
- MIT-LICENSE
|
51
|
+
test_files: []
|
52
|
+
|
53
|
+
rdoc_options: []
|
54
|
+
|
55
|
+
extra_rdoc_files:
|
56
|
+
- README
|
57
|
+
- MIT-LICENSE
|
58
|
+
executables:
|
59
|
+
- trac
|
60
|
+
extensions: []
|
61
|
+
|
62
|
+
requirements: []
|
63
|
+
|
64
|
+
dependencies:
|
65
|
+
- !ruby/object:Gem::Dependency
|
66
|
+
name: sqlite3-ruby
|
67
|
+
version_requirement:
|
68
|
+
version_requirements: !ruby/object:Gem::Version::Requirement
|
69
|
+
requirements:
|
70
|
+
- - ">"
|
71
|
+
- !ruby/object:Gem::Version
|
72
|
+
version: 0.0.0
|
73
|
+
version:
|
74
|
+
- !ruby/object:Gem::Dependency
|
75
|
+
name: activerecord
|
76
|
+
version_requirement:
|
77
|
+
version_requirements: !ruby/object:Gem::Version::Requirement
|
78
|
+
requirements:
|
79
|
+
- - ">"
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: 0.0.0
|
82
|
+
version:
|