garb-reporter 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +6 -0
- data/.rspec +2 -0
- data/.rvmrc +1 -0
- data/Gemfile +4 -0
- data/README +1 -0
- data/Rakefile +1 -0
- data/config/vcr_setup.rb +7 -0
- data/garb-reporter.gemspec +33 -0
- data/lib/garb-reporter/version.rb +3 -0
- data/lib/report.rb +64 -0
- data/spec/garb_reporter_spec.rb +88 -0
- data/spec/spec_helper.rb +7 -0
- data/vcr_cassettes/garb_authentication.yml +38 -0
- data/vcr_cassettes/get_profile.yml +40 -0
- metadata +153 -0
data/.gitignore
ADDED
data/.rspec
ADDED
data/.rvmrc
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
rvm --create ruby-1.8.7-p352@garb-reporter
|
data/Gemfile
ADDED
data/README
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
Welcome to garb-reporter!
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/config/vcr_setup.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "garb-reporter/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "garb-reporter"
|
7
|
+
s.version = GarbReporter::VERSION
|
8
|
+
s.authors = ["Stu Liston"]
|
9
|
+
s.email = ["stuart.liston@gmail.com"]
|
10
|
+
s.homepage = ""
|
11
|
+
s.summary = %q{Provides a reporter class for Garb}
|
12
|
+
s.description = %q{GarbReporter removes the need in Garb to create a new class for every report by offering a querying api that dynamically creates the Garb classes for you in the background.}
|
13
|
+
|
14
|
+
# Only running dependency is Garb (and its dependencies)
|
15
|
+
s.add_dependency "garb", "0.9.1"
|
16
|
+
|
17
|
+
# Development dependencies:
|
18
|
+
s.add_development_dependency "rspec", ">= 2.7.0"
|
19
|
+
s.add_development_dependency "i18n"
|
20
|
+
s.add_development_dependency "vcr"
|
21
|
+
s.add_development_dependency "fakeweb"
|
22
|
+
|
23
|
+
s.rubyforge_project = "garb-reporter"
|
24
|
+
|
25
|
+
s.files = `git ls-files`.split("\n")
|
26
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
27
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
28
|
+
s.require_paths = ["lib"]
|
29
|
+
|
30
|
+
# specify any dependencies here; for example:
|
31
|
+
# s.add_development_dependency "rspec"
|
32
|
+
# s.add_runtime_dependency "rest-client"
|
33
|
+
end
|
data/lib/report.rb
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
require "garb-reporter/version"
|
2
|
+
|
3
|
+
module GarbReporter
|
4
|
+
|
5
|
+
class Report
|
6
|
+
|
7
|
+
def initialize(profile)
|
8
|
+
@profile = profile
|
9
|
+
end
|
10
|
+
|
11
|
+
def method_missing(method, *args)
|
12
|
+
|
13
|
+
method_name = method.to_s
|
14
|
+
super unless valid_method_name?(method_name)
|
15
|
+
|
16
|
+
class_name = build_class_name(method_name)
|
17
|
+
klass = existing_report_class(class_name) || build_new_report_class(method_name, class_name)
|
18
|
+
klass.results(@profile)
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def valid_method_name?(method_name)
|
24
|
+
(!method_name.empty? && method_name.scan('_by_').count <= 1 && !method_name.start_with?('_', 'by_', 'and_') && !method_name.end_with?('_', '_by', '_and'))
|
25
|
+
end
|
26
|
+
|
27
|
+
def existing_report_class(class_name)
|
28
|
+
class_exists?(class_name) ? GarbReporter.const_get(class_name) : nil
|
29
|
+
end
|
30
|
+
|
31
|
+
def class_exists?(class_name)
|
32
|
+
GarbReporter.const_defined?(class_name)
|
33
|
+
rescue NameError
|
34
|
+
return false
|
35
|
+
end
|
36
|
+
|
37
|
+
def build_new_report_class(method_name, class_name)
|
38
|
+
klass = GarbReporter.const_set(class_name, Class.new())
|
39
|
+
klass.extend Garb::Model
|
40
|
+
klass.metrics << extract_metrics(method_name)
|
41
|
+
klass.dimensions << extract_dimensions(method_name)
|
42
|
+
return klass
|
43
|
+
end
|
44
|
+
|
45
|
+
def build_class_name(method_name)
|
46
|
+
method_name.split('_').collect { |w| w.capitalize! }.join
|
47
|
+
end
|
48
|
+
|
49
|
+
def extract_metrics(method_name)
|
50
|
+
build_metrics_and_dimensions(method_name).first || []
|
51
|
+
end
|
52
|
+
|
53
|
+
def extract_dimensions(method_name)
|
54
|
+
return [] unless method_name.include?('_by_')
|
55
|
+
|
56
|
+
build_metrics_and_dimensions(method_name).last || []
|
57
|
+
end
|
58
|
+
|
59
|
+
def build_metrics_and_dimensions(method_name)
|
60
|
+
(metrics, dimensions) = method_name.split('_by_').collect { |part| part.split('_and_').collect(&:to_sym) }
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe GarbReporter do
|
4
|
+
|
5
|
+
before do
|
6
|
+
|
7
|
+
# get VCR to serve us up a previously-canned http response for successful login
|
8
|
+
VCR.use_cassette('garb authentication') do
|
9
|
+
Garb::Session.login('stuart@example.com', 'password')
|
10
|
+
end
|
11
|
+
|
12
|
+
# get VCR to serve us up a previously-canned http response for fetching profiles
|
13
|
+
VCR.use_cassette('get profile') do
|
14
|
+
@profile = Garb::Management::Profile.all.detect { |p| p.web_property_id == 'UA-12345678-1' }
|
15
|
+
end
|
16
|
+
|
17
|
+
@report = GarbReporter::Report.new(@profile)
|
18
|
+
end
|
19
|
+
|
20
|
+
describe 'build_class_name' do
|
21
|
+
|
22
|
+
it 'should capitalise the string' do
|
23
|
+
@report.send(:build_class_name, 'visits').should == 'Visits'
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'should strip out underscores' do
|
27
|
+
@report.send(:build_class_name, 'visits_by_office').should_not match(/_/)
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'should handle really long strings' do
|
31
|
+
@report.send(:build_class_name, 'visits_and_pageviews_and_clicks_by_date_and_office').should == 'VisitsAndPageviewsAndClicksByDateAndOffice'
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe 'valid_method_name' do
|
36
|
+
|
37
|
+
it 'should not allow a blank string' do
|
38
|
+
@report.send(:valid_method_name?, '').should be_false
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'should not allow more than one instance of "_by_"' do
|
42
|
+
@report.send(:valid_method_name?, 'visits_by_office_by_date').should be_false
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'should not start or end with "_by_" or "_and_' do
|
46
|
+
@report.send(:valid_method_name?, '_by_date').should be_false
|
47
|
+
@report.send(:valid_method_name?, 'visits_by_').should be_false
|
48
|
+
@report.send(:valid_method_name?, '_and_date').should be_false
|
49
|
+
@report.send(:valid_method_name?, 'visits_and_').should be_false
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
describe 'extract_metrics' do
|
54
|
+
|
55
|
+
it 'should extract a single metric' do
|
56
|
+
@report.send(:extract_metrics, 'visits').should == [:visits]
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'should extract multiple metrics' do
|
60
|
+
@report.send(:extract_metrics, 'visits_and_clicks_and_foo_and_bar').should == [:visits, :clicks, :foo, :bar]
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'should ignore a dimension' do
|
64
|
+
@report.send(:extract_metrics, 'visits_and_clicks_by_date').should == [:visits, :clicks]
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'should ignore multiple dimensions' do
|
68
|
+
@report.send(:extract_metrics, 'visits_and_clicks_by_date_and_office').should == [:visits, :clicks]
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
describe 'dimensions' do
|
73
|
+
|
74
|
+
it 'should know when there are no dimensions' do
|
75
|
+
@report.send(:extract_dimensions, 'visits').should == []
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'should extract a single dimension' do
|
79
|
+
@report.send(:extract_dimensions, 'visits_by_date').should == [:date]
|
80
|
+
end
|
81
|
+
|
82
|
+
it 'should extract multiple dimensions' do
|
83
|
+
@report.send(:extract_dimensions, 'visits_by_date_and_office').should == [:date, :office]
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
---
|
2
|
+
- !ruby/struct:VCR::HTTPInteraction
|
3
|
+
request: !ruby/struct:VCR::Request
|
4
|
+
method: :post
|
5
|
+
uri: https://www.google.com:443/accounts/ClientLogin
|
6
|
+
body: Passwd=password&service=analytics&accountType=HOSTED_OR_GOOGLE&Email=stuart%40example.com&source=vigetLabs-garb-001
|
7
|
+
headers:
|
8
|
+
content-type:
|
9
|
+
- application/x-www-form-urlencoded
|
10
|
+
response: !ruby/struct:VCR::Response
|
11
|
+
status: !ruby/struct:VCR::ResponseStatus
|
12
|
+
code: 200
|
13
|
+
message: OK
|
14
|
+
headers:
|
15
|
+
x-content-type-options:
|
16
|
+
- nosniff
|
17
|
+
expires:
|
18
|
+
- Mon, 01-Jan-1990 00:00:00 GMT
|
19
|
+
content-type:
|
20
|
+
- text/plain
|
21
|
+
server:
|
22
|
+
- GSE
|
23
|
+
x-xss-protection:
|
24
|
+
- 1; mode=block
|
25
|
+
date:
|
26
|
+
- Wed, 30 Nov 2011 02:58:23 GMT
|
27
|
+
content-length:
|
28
|
+
- "881"
|
29
|
+
cache-control:
|
30
|
+
- no-cache, no-store
|
31
|
+
pragma:
|
32
|
+
- no-cache
|
33
|
+
body: |
|
34
|
+
SID=DQAAAMEAAABEPGo2a2ewm6JsKoF18TB1W4NzG21XMZZoX4uS44RP0BhBnP7daTnL3ZPtLO8fgEr-Ym-nF5OByN29mkdOSdFBA90zSMSlGedGyzryY_NC93C3zMFRe0Sa8ia-snWqbp7U_ja4u7v6mC7t4We_eyGRujR3tRJeCJURzYmcFLJtAuNPmkNWA8sZ1g-libvB6Y9Q_iSVyKWVU1te0vVBOamN1HPQizwDcNpEX4nKACiuHyt-Uj1fn1kTnS4HDbNirlQy2yjlt2klvnzy5GMPVkZN
|
35
|
+
LSID=DQAAAMIAAAB-o-KXnRZiasADsE59dnSkvovrXlY0htPFyvyeGZKIKumNGpatwtMVTogWlnGlRa0uZP4C3yyyOIXizXaogiYKlwKo3kEj7eo0QtnWjwXurwktNrfqUtdcoxjl8UkV_cIJl77aFcg5J5aQUJgv7d96jriTI5JB1W6krd3RjhqyjYICp5abqrMKASsUqjOaCBxI__5WvH4DYGQD4n64BrdCUUMFaGXOFDnpiPATWM9YvjQf7qDt0q_UkQify2tvlIJl70gvA-5dV-Ps1agz7Fpp
|
36
|
+
Auth=DQAAAMIAAAB-o-KXnRZiasADsE59dnSkvovrXlY0htPFyvyeGZKIKumNGpatwtMVTogWlnGlRa29p1ILfFYTDmueKnj9Ds2SOVu0tx670XjlgrRvtbia1MJ7p0M4uB5aJr4UMqqrkMF4dsnA28Les-plij6icPJq9OxCwQ2og_1q3bCjs-wkC6CN9lEBvQPeLj5ipIiCNM8fap03Qxfj7-0vTVFt4nP-kpYuIpILZja05N0am1_EAItaUOXWzcjPe_tDqSkXGN_5Mm46v7Qs2dR35ssEitsT
|
37
|
+
|
38
|
+
http_version: "1.1"
|
@@ -0,0 +1,40 @@
|
|
1
|
+
---
|
2
|
+
- !ruby/struct:VCR::HTTPInteraction
|
3
|
+
request: !ruby/struct:VCR::Request
|
4
|
+
method: :get
|
5
|
+
uri: https://www.google.com:443/analytics/feeds/datasources/ga/accounts/~all/webproperties/~all/profiles
|
6
|
+
body:
|
7
|
+
headers:
|
8
|
+
authorization:
|
9
|
+
- GoogleLogin auth=DQAAAMIAAAB-o-KXnRZiasADsE59dnSkvovrXlY0htPFyvyeGZKIKumNGpatwtMVTogWlnGlRa29p1ILfFYTDmueKnj9Ds2SOVu0tx670XjlgrRvtbia1MJ7p0M4uB5aJr4UMqqrkMF4dsnA28Les-plij6icPJq9OxCwQ2og_1q3bCjs-wkC6CN9lEBvQPeLj5ipIiCNM8fap03Qxfj7-0vTVFt4nP-kpYuIpILZja05N0am1_EAItaUOXWzcjPe_tDqSkXGN_5Mm46v7Qs2dR35ssEitsT
|
10
|
+
gdata-version:
|
11
|
+
- "2"
|
12
|
+
response: !ruby/struct:VCR::Response
|
13
|
+
status: !ruby/struct:VCR::ResponseStatus
|
14
|
+
code: 200
|
15
|
+
message: OK
|
16
|
+
headers:
|
17
|
+
x-frame-options:
|
18
|
+
- SAMEORIGIN
|
19
|
+
x-content-type-options:
|
20
|
+
- nosniff
|
21
|
+
last-modified:
|
22
|
+
- Wed, 30 Nov 2011 02:58:25 GMT
|
23
|
+
expires:
|
24
|
+
- Wed, 30 Nov 2011 02:58:25 GMT
|
25
|
+
content-type:
|
26
|
+
- application/atom+xml; charset=UTF-8; type=feed
|
27
|
+
server:
|
28
|
+
- GSE
|
29
|
+
x-xss-protection:
|
30
|
+
- 1; mode=block
|
31
|
+
date:
|
32
|
+
- Wed, 30 Nov 2011 02:58:25 GMT
|
33
|
+
gdata-version:
|
34
|
+
- "2.1"
|
35
|
+
vary:
|
36
|
+
- Accept, X-GData-Authorization, GData-Version
|
37
|
+
cache-control:
|
38
|
+
- private, max-age=0, must-revalidate, no-transform
|
39
|
+
body: <?xml version="1.0" encoding="UTF-8"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:dxp="http://schemas.google.com/analytics/2009" xmlns:openSearch="http://a9.com/-/spec/opensearch/1.1/" xmlns:gd="http://schemas.google.com/g/2005" gd:kind="analytics#profiles"> <id>https://www.google.com/analytics/feeds/datasources/ga/accounts/~all/webproperties/~all/profiles</id> <updated>2011-11-30T02:58:25.381Z</updated> <title>Google Analytics Profiles for stuart@example.com</title> <link rel="self" type="application/atom+xml" href="https://www.google.com/analytics/feeds/datasources/ga/accounts/~all/webproperties/~all/profiles"/> <author> <name>Google Analytics</name> </author> <generator version="1.0">Google Analytics</generator> <openSearch:totalResults>53</openSearch:totalResults> <openSearch:startIndex>1</openSearch:startIndex> <openSearch:itemsPerPage>1000</openSearch:itemsPerPage> <entry gd:etag="W/"C0cHSXw7fSp7I2A9WhRREUw."" gd:kind="analytics#profile"> <id>https://www.google.com/analytics/feeds/datasources/ga/accounts/12345678/webproperties/UA-12345678-1/profiles/67976579</id> <updated>2011-11-23T21:03:58.205-08:00</updated> <title>Google Analytics Profile TEST</title> <link rel="self" type="application/atom+xml" href="https://www.google.com/analytics/feeds/datasources/ga/accounts/12345678/webproperties/UA-12345678-1/profiles/67976579"/> <link rel="http://schemas.google.com/ga/2009#parent" type="application/atom+xml" href="https://www.google.com/analytics/feeds/datasources/ga/accounts/12345678/webproperties/UA-12345678-1" gd:targetKind="analytics#webproperty"/> <link rel="http://schemas.google.com/ga/2009#child" type="application/atom+xml" href="https://www.google.com/analytics/feeds/datasources/ga/accounts/12345678/webproperties/UA-12345678-1/profiles/67976579/goals" gd:targetKind="analytics#goals"/> <dxp:property name="ga:accountId" value="12345678"/> <dxp:property name="ga:webPropertyId" value="UA-12345678-1"/> <dxp:property name="ga:profileName" value="CAV UAT"/> <dxp:property name="ga:profileId" value="67976579"/> <dxp:property name="dxp:tableId" value="ga:67976579"/> <dxp:property name="ga:currency" value="USD"/> <dxp:property name="ga:timezone" value="America/Los_Angeles"/> </entry></feed>
|
40
|
+
http_version: "1.1"
|
metadata
ADDED
@@ -0,0 +1,153 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: garb-reporter
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 29
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 1
|
10
|
+
version: 0.0.1
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Stu Liston
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2011-11-30 00:00:00 Z
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: garb
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
25
|
+
requirements:
|
26
|
+
- - "="
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
hash: 57
|
29
|
+
segments:
|
30
|
+
- 0
|
31
|
+
- 9
|
32
|
+
- 1
|
33
|
+
version: 0.9.1
|
34
|
+
type: :runtime
|
35
|
+
version_requirements: *id001
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: rspec
|
38
|
+
prerelease: false
|
39
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
40
|
+
none: false
|
41
|
+
requirements:
|
42
|
+
- - ">="
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
hash: 19
|
45
|
+
segments:
|
46
|
+
- 2
|
47
|
+
- 7
|
48
|
+
- 0
|
49
|
+
version: 2.7.0
|
50
|
+
type: :development
|
51
|
+
version_requirements: *id002
|
52
|
+
- !ruby/object:Gem::Dependency
|
53
|
+
name: i18n
|
54
|
+
prerelease: false
|
55
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
56
|
+
none: false
|
57
|
+
requirements:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
hash: 3
|
61
|
+
segments:
|
62
|
+
- 0
|
63
|
+
version: "0"
|
64
|
+
type: :development
|
65
|
+
version_requirements: *id003
|
66
|
+
- !ruby/object:Gem::Dependency
|
67
|
+
name: vcr
|
68
|
+
prerelease: false
|
69
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
70
|
+
none: false
|
71
|
+
requirements:
|
72
|
+
- - ">="
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
hash: 3
|
75
|
+
segments:
|
76
|
+
- 0
|
77
|
+
version: "0"
|
78
|
+
type: :development
|
79
|
+
version_requirements: *id004
|
80
|
+
- !ruby/object:Gem::Dependency
|
81
|
+
name: fakeweb
|
82
|
+
prerelease: false
|
83
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
84
|
+
none: false
|
85
|
+
requirements:
|
86
|
+
- - ">="
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
hash: 3
|
89
|
+
segments:
|
90
|
+
- 0
|
91
|
+
version: "0"
|
92
|
+
type: :development
|
93
|
+
version_requirements: *id005
|
94
|
+
description: GarbReporter removes the need in Garb to create a new class for every report by offering a querying api that dynamically creates the Garb classes for you in the background.
|
95
|
+
email:
|
96
|
+
- stuart.liston@gmail.com
|
97
|
+
executables: []
|
98
|
+
|
99
|
+
extensions: []
|
100
|
+
|
101
|
+
extra_rdoc_files: []
|
102
|
+
|
103
|
+
files:
|
104
|
+
- .gitignore
|
105
|
+
- .rspec
|
106
|
+
- .rvmrc
|
107
|
+
- Gemfile
|
108
|
+
- README
|
109
|
+
- Rakefile
|
110
|
+
- config/vcr_setup.rb
|
111
|
+
- garb-reporter.gemspec
|
112
|
+
- lib/garb-reporter/version.rb
|
113
|
+
- lib/report.rb
|
114
|
+
- spec/garb_reporter_spec.rb
|
115
|
+
- spec/spec_helper.rb
|
116
|
+
- vcr_cassettes/garb_authentication.yml
|
117
|
+
- vcr_cassettes/get_profile.yml
|
118
|
+
homepage: ""
|
119
|
+
licenses: []
|
120
|
+
|
121
|
+
post_install_message:
|
122
|
+
rdoc_options: []
|
123
|
+
|
124
|
+
require_paths:
|
125
|
+
- lib
|
126
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
127
|
+
none: false
|
128
|
+
requirements:
|
129
|
+
- - ">="
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
hash: 3
|
132
|
+
segments:
|
133
|
+
- 0
|
134
|
+
version: "0"
|
135
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
136
|
+
none: false
|
137
|
+
requirements:
|
138
|
+
- - ">="
|
139
|
+
- !ruby/object:Gem::Version
|
140
|
+
hash: 3
|
141
|
+
segments:
|
142
|
+
- 0
|
143
|
+
version: "0"
|
144
|
+
requirements: []
|
145
|
+
|
146
|
+
rubyforge_project: garb-reporter
|
147
|
+
rubygems_version: 1.8.10
|
148
|
+
signing_key:
|
149
|
+
specification_version: 3
|
150
|
+
summary: Provides a reporter class for Garb
|
151
|
+
test_files:
|
152
|
+
- spec/garb_reporter_spec.rb
|
153
|
+
- spec/spec_helper.rb
|