statsmix 0.0.6 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/README.rdoc CHANGED
@@ -8,6 +8,38 @@ StatsMix makes it easy to track, chart, and share application and business metri
8
8
 
9
9
  To get started, you'll need a API key for StatsMix. You can get a free developer account here: http://www.statsmix.com/try?plan=developer
10
10
 
11
+ Detail documentation is at http://www.statsmix.com/developers/ruby_gem
12
+
13
+ == Quick Start ==
14
+
15
+ require "statsmix"
16
+ StatsMix.api_key = "YOUR API KEY"
17
+
18
+ #push a stat with the value 1 (default) to a metric called "My First Metric"
19
+ StatsMix.track("My First Metric")
20
+
21
+ #push the value 20
22
+ StatsMix.track("My First Metric",20)
23
+
24
+ #add metadata - you can use this to add granularity to your chart via Chart Settings in StatsMix
25
+ #this example tracks file uploads by file type
26
+ StatsMix.track("File Uploads", 1, {:meta => {"file type" => "PDF"}})
27
+
28
+ #if you need the ability to update a stat after the fact, you can pass in a unique identifier ref_id (scoped to that metric)
29
+ StatsMix.track("File Uploads", 1, {:ref_id => "abc123", :meta => {"file type" => "PDF"}})
30
+
31
+ #if you need to timestamp the stat for something other than now, pass in a UTC datetime called generated_at
32
+ StatsMix.track("File Uploads", 1, {:generated_at => 1.days.ago })
33
+
34
+ #to turn off tracking in your development environment
35
+ StatsMix.ignore = true
36
+
37
+ #to redirect all stats in dev environment to a test metric
38
+ StatsMix.test_metric_name = "My Test Metric"
39
+
40
+ == More Documentation ==
41
+
42
+ The StatsMix gem supports all the methods documented at http://www.statsmix.com/developers/documentation
11
43
 
12
44
  == Contributing to statsmix
13
45
 
data/Rakefile CHANGED
@@ -17,8 +17,8 @@ Jeweler::Tasks.new do |gem|
17
17
  gem.license = "MIT"
18
18
  gem.summary = %Q{A Ruby gem for the StatsMix API.}
19
19
  gem.description = %Q{A Ruby gem for the StatsMix API - http://www.statsmix.com/developers}
20
- gem.email = "tmarkiewicz@gmail.com"
21
- gem.authors = ["Tom Markiewicz"]
20
+ gem.email = ["tmarkiewicz@gmail.com","me@derekscruggs.com"]
21
+ gem.authors = ["Tom Markiewicz","Derek Scruggs"]
22
22
  # Include your dependencies below. Runtime dependencies are required when using your gem,
23
23
  # and development dependencies are only needed for development (ie running rake tasks, tests, etc)
24
24
  # gem.add_runtime_dependency 'jabber4r', '> 0.1'
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.6
1
+ 0.1.3
data/lib/statsmix.rb CHANGED
@@ -1,128 +1,102 @@
1
1
  require 'net/http'
2
+ require 'rubygems'
3
+ require 'json'
2
4
 
3
5
  class StatsMix
4
6
 
5
- BASE_URI = 'http://www.statsmix.com/api/v2/'
6
- # BASE_URI = 'http://localhost:3000/api/v2/'
7
+ BASE_URI = 'https://statsmix.com/api/v2/'
7
8
 
8
9
  GEM_VERSION = File.exist?('../VERSION') ? File.read('../VERSION') : ""
9
-
10
- def initialize(api_key, format = 'xml')
11
- @api_key = api_key
12
- @format = format
13
- @user_agent = "StatsMix Ruby Gem " + GEM_VERSION
14
- end
15
-
16
- def connect(resource)
17
- # Resources available: stats, metrics, TODO: profiles
18
- @url = URI.parse(BASE_URI + resource)
19
- @connection = Net::HTTP.new(@url.host, @url.port)
20
-
10
+
11
+ # Track an event
12
+ #
13
+ # Required: name of metric
14
+ # Optional: value, options {:generated_at}
15
+ # Returns: Net::HTTP object
16
+ def self.track(name, value = nil, options = {})
17
+ self.connect('track')
18
+ @request_uri = @url.path + '.' + @format
19
+ @request = Net::HTTP::Get.new(@request_uri)
20
+ @params[:name] = name
21
+ if @test_metric_name
22
+ @params[:name] = @test_metric_name
23
+ end
24
+ @params[:value] = value if value != nil
25
+ @params.merge!(options)
26
+ @params[:meta] =
27
+ if @params[:meta] && !@params[:meta].is_a?(String)
28
+ if @params[:meta].respond_to?('to_json')
29
+ @params[:meta] = @params[:meta].to_json
30
+ end
31
+ end
32
+ return do_request
21
33
  end
22
-
23
34
  # Stats
24
35
 
25
36
  # List stats (index)
26
37
  #
27
38
  # Required: metric_id
28
39
  # Optional: limit
29
- def list_stats(metric_id, limit = nil)
30
- connect('stats')
31
- request = Net::HTTP::Get.new(@url.path + '.' + @format)
32
- request["User-Agent"] = @user_agent
33
-
34
- form_hash = Hash.new
35
- form_hash["api_key"] = @api_key
36
- form_hash["metric_id"] = metric_id
37
- if limit != nil
38
- form_hash["limit"] = limit
39
- end
40
-
41
- request.set_form_data(form_hash)
42
-
43
- response = @connection.request(request)
44
- return response.body
40
+ # Returns: Net::HTTP object
41
+ def self.list_stats(metric_id, limit = nil)
42
+ self.connect('stats')
43
+ @request_uri = @url.path + '.' + @format
44
+ @request = Net::HTTP::Get.new(@request_uri)
45
+ @params[:metric_id] = metric_id
46
+ @params[:limit] = limit if limit != nil
47
+ return do_request
45
48
  end
46
49
 
47
50
  # Get stat
48
51
  #
49
52
  # Required: stat_id
50
53
  # Optional: none
51
- def get_stat(stat_id)
54
+ # Returns: Net::HTTP object
55
+ def self.get_stat(stat_id)
52
56
  connect('stats')
53
- request = Net::HTTP::Get.new(@url.path + '/' + stat_id.to_s + '.' + @format)
54
- request["User-Agent"] = @user_agent
55
-
56
- request.set_form_data({"api_key" => @api_key})
57
-
58
- response = @connection.request(request)
59
- return response.body
57
+ @request_uri = @url.path + '/' + stat_id.to_s + '.' + @format
58
+ @request = Net::HTTP::Get.new(@request_uri)
59
+ return do_request
60
60
  end
61
61
 
62
62
  # Create stat
63
63
  #
64
64
  # Required: metric_id
65
- # Optional: value, generated_at, meta
66
- def create_stat(metric_id, value = 1, generated_at = Time.now, meta = nil)
65
+ # Optional: value, params[:generated_at, :meta]
66
+ # Returns: Net::HTTP object
67
+ def self.create_stat(metric_id, value = nil, params = {})
67
68
  connect('stats')
68
- request = Net::HTTP::Post.new(@url.path + '.' + @format)
69
- request["User-Agent"] = @user_agent
70
-
71
- form_hash = Hash.new
72
- form_hash["api_key"] = @api_key
73
- form_hash["value"] = value
74
- form_hash["metric_id"] = metric_id
75
- form_hash["generated_at"] = generated_at
76
- if meta != nil
77
- form_hash["meta"] = meta
78
- end
79
-
80
- request.set_form_data(form_hash)
81
-
82
- response = @connection.request(request)
83
- return response.body
69
+ @request_uri = @url.path + '.' + @format
70
+ @request = Net::HTTP::Post.new(@request_uri)
71
+ @params.merge!(params)
72
+ @params[:value] = value if value
73
+ return do_request
84
74
  end
85
75
 
86
76
  # Update stat
87
77
  #
88
78
  # Required: stat_id
89
79
  # Optional: value, generated_at, meta
90
- def update_stat(stat_id, value = nil, generated_at = nil, meta = nil)
80
+ # Returns: Net::HTTP object
81
+ def self.update_stat(stat_id, value = nil, params = {})
91
82
  connect('stats')
92
- request = Net::HTTP::Put.new(@url.path + '/' + stat_id.to_s + '.' + @format)
93
- request["User-Agent"] = @user_agent
94
-
95
- form_hash = Hash.new
96
- form_hash["api_key"] = @api_key
97
- if value != nil
98
- form_hash["value"] = value
99
- end
100
- if generated_at != nil
101
- form_hash["generated_at"] = generated_at
102
- end
103
- if meta != nil
104
- form_hash["meta"] = meta
105
- end
106
-
107
- request.set_form_data(form_hash)
108
-
109
- response = @connection.request(request)
110
- return response.body
83
+ @request_uri = @url.path + '/' + stat_id.to_s + '.' + @format
84
+ @request = Net::HTTP::Put.new(@request_uri)
85
+ @params.merge!(params)
86
+ @params[:value] = value if value != nil
87
+ return do_request
111
88
  end
112
89
 
113
90
  # Delete stat
114
91
  #
115
92
  # Required: stat_id
116
93
  # Optional: none
117
- def delete_stat(stat_id)
94
+ # Returns: Net::HTTP object
95
+ def self.delete_stat(stat_id)
118
96
  connect('stats')
119
- request = Net::HTTP::Delete.new(@url.path + '/' + stat_id.to_s + '.' + @format)
120
- request["User-Agent"] = @user_agent
121
-
122
- request.set_form_data({"api_key" => @api_key})
123
-
124
- response = @connection.request(request)
125
- return response.body
97
+ @request_uri = @url.path + '/' + stat_id.to_s + '.' + @format
98
+ @request = Net::HTTP::Delete.new(@request_uri)
99
+ return do_request
126
100
  end
127
101
 
128
102
  # Metrics
@@ -131,102 +105,194 @@ class StatsMix
131
105
  #
132
106
  # Required: none
133
107
  # Optional: profile_id, limit
134
- def list_metrics(profile_id = nil, limit = nil)
108
+ # Returns: Net::HTTP object
109
+ def self.list_metrics(profile_id = nil, limit = nil)
135
110
  connect('metrics')
136
- request = Net::HTTP::Get.new(@url.path + '.' + @format)
137
- request["User-Agent"] = @user_agent
138
-
139
- form_hash = Hash.new
140
- form_hash["api_key"] = @api_key
141
- if profile_id != nil
142
- form_hash["profile_id"] = profile_id
143
- end
144
- if limit != nil
145
- form_hash["limit"] = limit
146
- end
147
-
148
- request.set_form_data(form_hash)
111
+ @request_uri = @url.path + '.' + @format
112
+ @request = Net::HTTP::Get.new(@request_uri)
113
+
114
+ @params[:profile_id] = profile_id if profile_id != nil
115
+ @params[:limit] = limit if limit != nil
149
116
 
150
- response = @connection.request(request)
151
- return response.body
117
+ return do_request
152
118
  end
153
119
 
154
120
  # Get metric
155
121
  #
156
122
  # Required: metric_id
157
123
  # Optional: none
158
- def get_metric(metric_id)
124
+ # Returns: Net::HTTP object
125
+ def self.get_metric(metric_id)
159
126
  connect('metrics')
160
- request = Net::HTTP::Get.new(@url.path + '/' + metric_id.to_s + '.' + @format)
161
- request["User-Agent"] = @user_agent
162
-
163
- request.set_form_data({"api_key" => @api_key})
164
-
165
- response = @connection.request(request)
166
- return response.body
127
+ @request_uri = @url.path + '/' + metric_id.to_s + '.' + @format
128
+ @request = Net::HTTP::Get.new(@request_uri)
129
+ return do_request
167
130
  end
168
131
 
169
132
  # Create metric
170
133
  #
171
134
  # Required: name
172
- # Optional: profile_id, generated_at
173
- def create_metric(name, profile_id = nil)
135
+ # Optional: params[:profile_id, :sharing, :include_in_email]
136
+ # Returns: Net::HTTP object
137
+ def self.create_metric(name,params={})
174
138
  connect('metrics')
175
- request = Net::HTTP::Post.new(@url.path + '.' + @format)
176
- request["User-Agent"] = @user_agent
177
-
178
- form_hash = Hash.new
179
- form_hash["api_key"] = @api_key
180
- form_hash["name"] = name
181
- if profile_id != nil
182
- form_hash["profile_id"] = profile_id
183
- end
184
-
185
- request.set_form_data(form_hash)
186
-
187
- response = @connection.request(request)
188
- return response.body
139
+ @params.merge!(params)
140
+ @params[:name] = name
141
+ @request_uri = @url.path + '.' + @format
142
+ @request = Net::HTTP::Post.new(@request_uri)
143
+ return do_request
189
144
  end
190
145
 
191
146
  # Update metric
192
147
  #
193
148
  # Required: metric_id
194
- # Optional: name, sharing, include_in_email
195
- def update_metric(metric_id, name = nil, sharing = nil, include_in_email = nil)
149
+ # Optional: params[:profile_id, :sharing, :include_in_email]
150
+ # Returns: Net::HTTP object
151
+ def self.update_metric(metric_id, params = {})
196
152
  connect('metrics')
197
- request = Net::HTTP::Put.new(@url.path + '/' + metric_id.to_s + '.' + @format)
198
- request["User-Agent"] = @user_agent
199
-
200
- form_hash = Hash.new
201
- form_hash["api_key"] = @api_key
202
- if name != nil
203
- form_hash["name"] = name
204
- end
205
- if sharing != nil
206
- form_hash["sharing"] = sharing
207
- end
208
- if include_in_email != nil
209
- form_hash["include_in_email"] = include_in_email
210
- end
211
-
212
- request.set_form_data(form_hash)
153
+ @params = [] if @params.nil?
154
+ @params.merge!(params)
155
+ @request_uri = @url.path + '/' + metric_id.to_s + '.' + @format
156
+ @request = Net::HTTP::Put.new(@request_uri)
213
157
 
214
- response = @connection.request(request)
215
- return response.body
158
+ return do_request
216
159
  end
217
160
 
218
161
  # Delete metric
219
162
  #
220
163
  # Required: metric_id
221
164
  # Optional: none
222
- def delete_metric(metric_id)
165
+ # Returns: Net::HTTP object
166
+ def self.delete_metric(metric_id)
223
167
  connect('metrics')
224
- request = Net::HTTP::Delete.new(@url.path + '/' + metric_id.to_s + '.' + @format)
225
- request["User-Agent"] = @user_agent
168
+ @request_uri = @url.path + '/' + metric_id.to_s + '.' + @format
169
+ @request = Net::HTTP::Delete.new(@request_uri)
170
+ return do_request
171
+ end
172
+
173
+ def initialize(api_key = nil)
174
+ self.setup(api_key)
175
+ end
176
+
177
+ # Returns: Net::HTTP object
178
+ def self.response
179
+ @response
180
+ end
181
+
182
+ # Returns: string or boolean false
183
+ def self.error
184
+ @error
185
+ end
186
+
187
+ # Returns: hash
188
+ def self.params
189
+ @params
190
+ end
191
+
192
+ def self.api_key=(string)
193
+ @api_key = string
194
+ end
195
+
196
+ # Returns: string
197
+ def self.api_key
198
+ @api_key
199
+ end
200
+
201
+ def self.ignore=(boolean)
202
+ @ignore = boolean ? true : false
203
+ end
204
+
205
+ #Returns: boolean
206
+ def self.ignore
207
+ @ignore
208
+ end
209
+
210
+ def self.test_metric_name=(name)
211
+ @test_metric_name = name
212
+ end
213
+
214
+ #Returns: string or nil
215
+ def self.test_metric_name
216
+ @test_metric_name
217
+ end
218
+
219
+ # Returns: string
220
+ def self.api_key
221
+ @api_key
222
+ end
223
+
224
+ def self.api_from_env
225
+ return nil if ENV['STATSMIX_URL'].nil?
226
+ url = ENV['STATSMIX_URL']
227
+ pieces = url.gsub('http://','').gsub('https://','').split('/')
228
+ @api_key = pieces[2]
229
+ end
230
+
231
+ def self.format=(string)
232
+ string.downcase!
233
+ if string != 'json' && string != 'xml'
234
+ raise "format MUST be either xml or json"
235
+ end
236
+ @format = string
237
+ end
238
+
239
+ # Returns: string
240
+ def self.format
241
+ @format
242
+ end
243
+
244
+ # Returns: string
245
+ def self.request_uri
246
+ @request_uri
247
+ end
248
+ private
249
+
250
+ def self.setup(api_key = nil)
251
+ return if @initiliazed
252
+ if !api_key.nil?
253
+ @api_key = api_key
254
+ end
255
+ @format = 'xml' if @format.nil?
256
+ @ignore = false if @ignore.nil?
257
+ @user_agent = "StatsMix Ruby Gem " + GEM_VERSION
258
+ @initiliazed = true
259
+ @error = false
260
+ end
261
+
262
+ def self.connect(resource)
263
+ self.setup
226
264
 
227
- request.set_form_data({"api_key" => @api_key})
265
+ if @api_key.nil?
266
+ raise "API key not set. You must set it frist with StatsMix.api_key = [your api key]"
267
+ end
268
+ # Resources available: stats, metrics, TODO: profiles
269
+ @url = URI.parse(BASE_URI + resource)
270
+ @connection = Net::HTTP.new(@url.host, @url.port)
228
271
 
229
- response = @connection.request(request)
230
- return response.body
272
+ @request = Hash.new
273
+ @request["User-Agent"] = @user_agent
274
+ @params = Hash.new
275
+ @params[:api_key] = @api_key
231
276
  end
232
- end
277
+
278
+ def self.do_request
279
+ @error = false
280
+ return if @ignore
281
+ @request.set_form_data(@params)
282
+ @response = @connection.request(@request)
283
+ if @response.is_a?(Net::HTTPClientError)
284
+ if 'xml' == @format
285
+ begin
286
+ @error = @response.body.match('<error>(.)+</error>')[0].gsub('<error>','').gsub('</error>','')
287
+ rescue
288
+ @error = 'Unable to parse error message. Check StatsMix.response for more information'
289
+ end
290
+ else
291
+ @error = JSON.parse(@response.body)['errors']['error']
292
+ end
293
+ end
294
+ @response.body
295
+ end
296
+ end
297
+
298
+ StatsMix.api_from_env
data/statsmix.gemspec CHANGED
@@ -5,13 +5,13 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{statsmix}
8
- s.version = "0.0.6"
8
+ s.version = "0.1.3"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
- s.authors = ["Tom Markiewicz"]
12
- s.date = %q{2011-06-22}
11
+ s.authors = ["Tom Markiewicz", "Derek Scruggs"]
12
+ s.date = %q{2011-07-05}
13
13
  s.description = %q{A Ruby gem for the StatsMix API - http://www.statsmix.com/developers}
14
- s.email = %q{tmarkiewicz@gmail.com}
14
+ s.email = ["tmarkiewicz@gmail.com", "me@derekscruggs.com"]
15
15
  s.extra_rdoc_files = [
16
16
  "LICENSE.txt",
17
17
  "README.rdoc"
@@ -3,7 +3,7 @@ require 'statsmix'
3
3
 
4
4
  class TestStatsmix < Test::Unit::TestCase
5
5
 
6
- # TODO use fakwweb gem for testing
6
+ # TODO use fakwweb gem for testing
7
7
  # http://technicalpickles.com/posts/stop-net-http-dead-in-its-tracks-with-fakeweb/
8
8
  # https://github.com/chrisk/fakeweb
9
9
  # http://fakeweb.rubyforge.org/
@@ -12,51 +12,16 @@ class TestStatsmix < Test::Unit::TestCase
12
12
  # http://www.rubyinside.com/vcr-a-recorder-for-all-your-tests-http-interactions-4169.html
13
13
  # https://github.com/myronmarston/vcr
14
14
 
15
- should "initialize StatsMix API" do
16
- statsmix = StatsMix.new('59f08613db2691f28afe', 'xml')
17
- response = statsmix.list_metrics
18
- puts response
15
+ should "Track a stat and view the results in xml" do
16
+ StatsMix.api_key = '59f08613db2691f28afe'
17
+ result = StatsMix.track('Ruby Gem Testing')
18
+ assert_response 200
19
+ if StatsMix.error
20
+ raise "Error in gem: #{StatsMix.error}"
21
+ end
22
+ assert !StatsMix.error
23
+ puts result
19
24
  end
20
25
 
21
- # # metrics
22
- # should "create metric" do
23
- #
24
- # end
25
- #
26
- # should "get metric" do
27
- #
28
- # end
29
- #
30
- # should "list metrics" do
31
- #
32
- # end
33
- #
34
- # should "update metric" do
35
- #
36
- # end
37
- #
38
- # should "delete metric" do
39
- #
40
- # end
41
26
 
42
- # # stats
43
- # should "create stat" do
44
- #
45
- # end
46
- #
47
- # should "get stat" do
48
- #
49
- # end
50
- #
51
- # should "list stats" do
52
- #
53
- # end
54
- #
55
- # should "update stat" do
56
- #
57
- # end
58
- #
59
- # should "delete stat" do
60
- #
61
- # end
62
27
  end
metadata CHANGED
@@ -1,28 +1,26 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: statsmix
3
3
  version: !ruby/object:Gem::Version
4
- hash: 19
4
+ hash: 29
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
- - 0
9
- - 6
10
- version: 0.0.6
8
+ - 1
9
+ - 3
10
+ version: 0.1.3
11
11
  platform: ruby
12
12
  authors:
13
13
  - Tom Markiewicz
14
+ - Derek Scruggs
14
15
  autorequire:
15
16
  bindir: bin
16
17
  cert_chain: []
17
18
 
18
- date: 2011-06-22 00:00:00 -06:00
19
+ date: 2011-07-05 00:00:00 -06:00
19
20
  default_executable:
20
21
  dependencies:
21
22
  - !ruby/object:Gem::Dependency
22
- type: :development
23
- prerelease: false
24
- name: shoulda
25
- version_requirements: &id001 !ruby/object:Gem::Requirement
23
+ requirement: &id001 !ruby/object:Gem::Requirement
26
24
  none: false
27
25
  requirements:
28
26
  - - ">="
@@ -31,12 +29,12 @@ dependencies:
31
29
  segments:
32
30
  - 0
33
31
  version: "0"
34
- requirement: *id001
35
- - !ruby/object:Gem::Dependency
36
32
  type: :development
33
+ name: shoulda
37
34
  prerelease: false
38
- name: bundler
39
- version_requirements: &id002 !ruby/object:Gem::Requirement
35
+ version_requirements: *id001
36
+ - !ruby/object:Gem::Dependency
37
+ requirement: &id002 !ruby/object:Gem::Requirement
40
38
  none: false
41
39
  requirements:
42
40
  - - ~>
@@ -47,12 +45,12 @@ dependencies:
47
45
  - 0
48
46
  - 0
49
47
  version: 1.0.0
50
- requirement: *id002
51
- - !ruby/object:Gem::Dependency
52
48
  type: :development
49
+ name: bundler
53
50
  prerelease: false
54
- name: jeweler
55
- version_requirements: &id003 !ruby/object:Gem::Requirement
51
+ version_requirements: *id002
52
+ - !ruby/object:Gem::Dependency
53
+ requirement: &id003 !ruby/object:Gem::Requirement
56
54
  none: false
57
55
  requirements:
58
56
  - - ~>
@@ -63,12 +61,12 @@ dependencies:
63
61
  - 5
64
62
  - 1
65
63
  version: 1.5.1
66
- requirement: *id003
67
- - !ruby/object:Gem::Dependency
68
64
  type: :development
65
+ name: jeweler
69
66
  prerelease: false
70
- name: rcov
71
- version_requirements: &id004 !ruby/object:Gem::Requirement
67
+ version_requirements: *id003
68
+ - !ruby/object:Gem::Dependency
69
+ requirement: &id004 !ruby/object:Gem::Requirement
72
70
  none: false
73
71
  requirements:
74
72
  - - ">="
@@ -77,9 +75,14 @@ dependencies:
77
75
  segments:
78
76
  - 0
79
77
  version: "0"
80
- requirement: *id004
78
+ type: :development
79
+ name: rcov
80
+ prerelease: false
81
+ version_requirements: *id004
81
82
  description: A Ruby gem for the StatsMix API - http://www.statsmix.com/developers
82
- email: tmarkiewicz@gmail.com
83
+ email:
84
+ - tmarkiewicz@gmail.com
85
+ - me@derekscruggs.com
83
86
  executables: []
84
87
 
85
88
  extensions: []