fanfeedrb 0.0.1 → 0.0.2

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/fanfeedrb.gemspec CHANGED
@@ -12,7 +12,7 @@ Gem::Specification.new do |s|
12
12
  s.summary = %q{Ruby implementation of the FanFeedr api}
13
13
  s.description = %q{Ruby implementation of the FanFeedr api}
14
14
  s.add_dependency('rake')
15
- s.add_dependency('httparty')
15
+ s.add_dependency('crack')
16
16
  s.rubyforge_project = "fanfeedrb"
17
17
 
18
18
  s.files = `git ls-files`.split("\n")
@@ -0,0 +1,15 @@
1
+ class Fanfeedrb
2
+ class Fanfeedr
3
+ class Conference < Abstract
4
+ attr_reader :fanfeedr
5
+ reader :level, :name, :id
6
+
7
+ def initialize(fanfeedr, attributes = {})
8
+ @fanfeedr = fanfeedr
9
+ super(attributes)
10
+ end
11
+
12
+ end
13
+ end
14
+ end
15
+
@@ -0,0 +1,15 @@
1
+ class Fanfeedrb
2
+ class Fanfeedr
3
+ class Conference < Abstract
4
+ attr_reader :fanfeedr
5
+ reader :level, :name, :id
6
+
7
+ def initialize(fanfeedr, attributes = {})
8
+ @fanfeedr = fanfeedr
9
+ super(attributes)
10
+ end
11
+
12
+ end
13
+ end
14
+ end
15
+
@@ -0,0 +1,29 @@
1
+ class Fanfeedrb
2
+ class Fanfeedr
3
+ class Event < Abstract
4
+ attr_reader :name
5
+ #reader :description, :author, :position, :complete
6
+ date_reader :date
7
+
8
+ def when
9
+ date && Date.new(date.year, date.mon, date.day)
10
+ end
11
+
12
+ def initialize(event, attributes = {})
13
+ @event = event
14
+ super(attributes)
15
+ end
16
+
17
+ def to_xml
18
+ Fanfeedrb.hash_to_xml(:task, @attributes)
19
+ end
20
+
21
+ def inspect
22
+ "#<#{self.class.inspect}:#{id.inspect}, event_id: #{event.id.inspect}, date: #{when.inspect} >"
23
+ end
24
+
25
+ end
26
+ end
27
+
28
+
29
+ end
@@ -0,0 +1,15 @@
1
+ class Fanfeedrb
2
+ class Fanfeedr
3
+ class Geo < Abstract
4
+ attr_reader :fanfeedr
5
+ #reader :level, :name, :id
6
+
7
+ def initialize(fanfeedr, attributes = {})
8
+ @fanfeedr = fanfeedr
9
+ super(attributes)
10
+ end
11
+
12
+ end
13
+ end
14
+ end
15
+
@@ -0,0 +1,31 @@
1
+ class Fanfeedrb
2
+ class Fanfeedr
3
+ class League < Abstract
4
+ attr_reader :fanfeedr
5
+ reader :gender, :levels, :sport, :name
6
+
7
+ def initialize(fanfeedr, attributes = {})
8
+ p "league attr: #{attributes}"
9
+ @fanfeedr = fanfeedr
10
+ super(attributes)
11
+ end
12
+
13
+ def conference(conference_id)
14
+ raise Error, "No conference id given" if conference_id.to_s.empty?
15
+ Conference.new(self,fanfeedr.get_json("/conferences/#{conference_id}"))
16
+ end
17
+ def conferences(*args)
18
+ path = "/leagues/#{id}/conferences"
19
+ #path << "?api_key=#{CGI.escape(Fanfeedrb.config['api_token'])}"
20
+ #if filter
21
+ response = fanfeedr.get_json(path)
22
+ [response].flatten.compact.map {|s| Conference.new(self,s)}
23
+ end
24
+
25
+
26
+
27
+
28
+
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,15 @@
1
+ class Fanfeedrb
2
+ class Fanfeedr
3
+ class Team < Abstract
4
+ attr_reader :fanfeedr
5
+ #reader :level, :name, :id
6
+
7
+ def initialize(fanfeedr, attributes = {})
8
+ @fanfeedr = fanfeedr
9
+ super(attributes)
10
+ end
11
+
12
+ end
13
+ end
14
+ end
15
+
@@ -0,0 +1,130 @@
1
+ require 'cgi'
2
+ require 'crack/json'
3
+
4
+ class Fanfeedrb
5
+ class Fanfeedr
6
+ #/silver/api/leagues/20f0857f-3c43-5f50-acfc-879f838ee853/events/4dd1704b-a712-511c-b947-8c8f03ea3200?api_key=vbxctn5sn8x7jz644evkrhtc
7
+ ADDRESS = "ffapi.fanfeedr.com"
8
+ BASE_PATH = "/#{Fanfeedrb.config['api_plan']}/api/"
9
+ #SEARCH_KEYS = %w(label type state requester owner mywork id includedone)
10
+ class Error < Fanfeedrb::Error; end
11
+
12
+ attr_reader :token,:plan
13
+
14
+ def initialize(token,plan, ssl = false)
15
+ @token = token
16
+ @plan = plan
17
+ @ssl = ssl
18
+ end
19
+
20
+ def ssl?
21
+ @ssl
22
+ end
23
+ def http
24
+ unless @http
25
+ if ssl?
26
+ require 'net/https'
27
+ @http = Net::HTTP.new(ADDRESS, Net::HTTP.https_default_port)
28
+ @http.verify_mode = OpenSSL::SSL::VERIFY_NONE
29
+ @http.use_ssl = true
30
+ else
31
+ require 'net/http'
32
+ @http = Net::HTTP.new(ADDRESS)
33
+ end
34
+ end
35
+ @http
36
+ end
37
+
38
+ def request(method, path, *args)
39
+ headers = {
40
+ #"api_key" => @token,
41
+ "Accept" => "application/json",
42
+ "Content-type" => "application/json"
43
+ }
44
+ http # trigger require of 'net/http'
45
+ klass = Net::HTTP.const_get(method.to_s.capitalize)
46
+ p klass
47
+ path << "?api_key=#{CGI.escape(@token)}"
48
+ p path
49
+ http.request(klass.new("#{BASE_PATH}#{path}", headers), *args)
50
+ end
51
+ def request_json(method, path, *args)
52
+ response = request(method,path,*args)
53
+ raise response.inspect if response["Content-type"].split(/; */).first != "application/json"
54
+ hash = Crack::JSON.parse(response.body)
55
+ if hash.class == Hash && hash["message"] && (response.code.to_i >= 400 || hash["success"] == "false")
56
+ raise Error, hash["message"], caller
57
+ end
58
+ hash
59
+ end
60
+
61
+ def get_json(path)
62
+ p path
63
+ request_json(:get, path)
64
+ end
65
+
66
+ def league(id)
67
+ League.new(self,get_json("/leagues/#{id}"))
68
+ end
69
+ #def conference(id)
70
+ #Conference.new(self,get_json("/conferences/#{id}"))
71
+ #end
72
+
73
+ class Abstract
74
+ def initialize(attributes = {})
75
+ @attributes = {}
76
+ (attributes || {}).each do |k,v|
77
+ if respond_to?("#{k}=")
78
+ send("#{k}=", v)
79
+ else
80
+ @attributes[k.to_s] = v
81
+ end
82
+ end
83
+ yield self if block_given?
84
+ end
85
+
86
+ def self.reader(*methods)
87
+ methods.each do |method|
88
+ define_method(method) { @attributes[method.to_s] }
89
+ end
90
+ end
91
+
92
+ def self.date_reader(*methods)
93
+ methods.each do |method|
94
+ define_method(method) do
95
+ value = @attributes[method.to_s]
96
+ value.kind_of?(String) ? Date.parse(value) : value
97
+ end
98
+ end
99
+ end
100
+
101
+ def self.accessor(*methods)
102
+ reader(*methods)
103
+ methods.each do |method|
104
+ define_method("#{method}=") { |v| @attributes[method.to_s] = v }
105
+ end
106
+ end
107
+
108
+ def id
109
+ id = @attributes['id'] and Integer(id)
110
+ end
111
+
112
+ def to_xml
113
+ Fanfeedrb.hash_to_xml(self.class.name.split('::').last.downcase, @attributes)
114
+ end
115
+
116
+ end
117
+ end
118
+ end
119
+
120
+
121
+ #require 'fanfeedrb/fanfeedr/league'
122
+ #require 'fanfeedrb/fanfeedr/events'
123
+
124
+
125
+
126
+
127
+
128
+
129
+
130
+
@@ -0,0 +1,527 @@
1
+ require 'optparse'
2
+
3
+ class Fanfeedrb
4
+ class Runner
5
+
6
+ class Base
7
+ attr_reader :argv
8
+
9
+ def initialize(argv)
10
+ @argv = argv
11
+ @tty = $stdout.tty?
12
+ @opts = OptionParser.new
13
+ @opts.version = "0.0"
14
+ @opts.banner = "Usage: fanfeedrb #{self.class.command_name} #{self.class.banner_arguments}"
15
+ @opts.base.long["help"] = OptionParser::Switch::NoArgument.new do
16
+ help = @opts.help.chomp.chomp + "\n"
17
+ help += "\n#{self.class.description}" if self.class.description
18
+ puts help
19
+ @exit = 0
20
+ end
21
+ @opts.separator("")
22
+ end
23
+
24
+ def self.options
25
+ @options ||= []
26
+ end
27
+
28
+ def self.on(*args, &block)
29
+ options << args
30
+ define_method("option_#{args.object_id}", &block)
31
+ end
32
+
33
+ def self.banner_arguments(value = nil)
34
+ if value
35
+ @banner_arguments = value
36
+ else
37
+ @banner_arguments || (arity.zero? ? "" : "...")
38
+ end
39
+ end
40
+
41
+ def self.summary(value = nil)
42
+ if value
43
+ @summary = value
44
+ else
45
+ @summary
46
+ end
47
+ end
48
+
49
+ def self.description(value = nil)
50
+ if value
51
+ @description = value
52
+ else
53
+ @description || "#@summary."
54
+ end
55
+ end
56
+
57
+ def self.command_name
58
+ name.split('::').last.gsub(/(.)([A-Z])/) {"#$1-#$2"}.downcase
59
+ end
60
+
61
+ def self.method_name
62
+ command_name.gsub('-','_')
63
+ end
64
+
65
+ def self.process(&block)
66
+ define_method(:process, &block)
67
+ end
68
+
69
+ def self.arity
70
+ instance_method(:process).arity
71
+ end
72
+
73
+ def arity
74
+ self.class.arity
75
+ end
76
+
77
+ def fanfeedrb
78
+ @fanfeedrb ||= Fanfeedrb.new(Dir.getwd)
79
+ end
80
+
81
+ def abort(message)
82
+ raise Error, message
83
+ end
84
+
85
+ def too_many
86
+ abort "too many arguments"
87
+ end
88
+
89
+ def run
90
+ self.class.options.each do |arguments|
91
+ @opts.on(*arguments, &method("option_#{arguments.object_id}"))
92
+ end
93
+ begin
94
+ @opts.parse!(@argv)
95
+ rescue OptionParser::InvalidOption
96
+ abort $!.message
97
+ end
98
+ return @exit if @exit
99
+ minimum = arity < 0 ? -1 - arity : arity
100
+ if arity >= 0 && arity < @argv.size
101
+ too_many
102
+ elsif minimum > @argv.size
103
+ abort "not enough arguments"
104
+ end
105
+ process(*@argv)
106
+ end
107
+
108
+ def process(*argv)
109
+ fanfeedrb.send(self.class.method_name,*argv)
110
+ end
111
+
112
+ def color?
113
+ case fanfeedrb.config["color"]
114
+ when "always" then true
115
+ when "never" then false
116
+ else
117
+ @tty && RUBY_PLATFORM !~ /mswin|mingw/
118
+ end
119
+ end
120
+
121
+ def colorize(code, string)
122
+ if color?
123
+ "\e[#{code}m#{string}\e[00m"
124
+ else
125
+ string.to_s
126
+ end
127
+ end
128
+
129
+ def puts_summary(story)
130
+ summary = "%6d " % story.id
131
+ type = story.estimate || TYPE_SYMBOLS[story.story_type]
132
+ state = STATE_SYMBOLS[story.current_state]
133
+ summary << colorize("3#{STATE_COLORS[story.current_state]}", state) << ' '
134
+ summary << colorize("01;3#{TYPE_COLORS[story.story_type]}", type) << ' '
135
+ summary << story.name
136
+ puts summary
137
+ end
138
+
139
+ def puts_full(story)
140
+ puts colorize("01;3#{TYPE_COLORS[story.story_type]}", story.name)
141
+ puts "Type: #{story.story_type}".rstrip
142
+ if story.story_type == "release"
143
+ puts "Deadline: #{story.deadline}".rstrip
144
+ else
145
+ puts "Estimate: #{story.estimate}".rstrip
146
+ end
147
+ puts "State: #{story.current_state}".rstrip
148
+ puts "Labels: #{story.labels.join(', ')}".rstrip
149
+ puts "Requester: #{story.requested_by}".rstrip
150
+ puts "Owner: #{story.owned_by}".rstrip
151
+ puts "URL: #{story.url}".rstrip
152
+ puts unless story.description =~ /^\s*$/
153
+ story.description_lines.each do |line|
154
+ puts " #{line}".rstrip
155
+ end
156
+ story.notes.each do |note|
157
+ puts
158
+ puts " #{colorize('01', note.author)} (#{note.date})"
159
+ puts(*note.lines(72).map {|l| " #{l}".rstrip})
160
+ end
161
+ story.tasks.each do |task|
162
+ # puts
163
+ # puts " #{colorize('01', note.author)} (#{note.date})"
164
+ # puts(*note.lines(72).map {|l| " #{l}".rstrip})
165
+ end
166
+
167
+ end
168
+
169
+ def paginated_output
170
+ stdout = $stdout
171
+ if @tty && pager = fanfeedrb.config["pager"]
172
+ # Modeled after git
173
+ ENV["LESS"] ||= "FRSX"
174
+ IO.popen(pager,"w") do |io|
175
+ $stdout = io
176
+ yield
177
+ end
178
+ else
179
+ yield
180
+ end
181
+ rescue Errno::EPIPE
182
+ ensure
183
+ $stdout = stdout
184
+ end
185
+
186
+ end
187
+
188
+ def self.[](command)
189
+ klass_name = command.to_s.capitalize.gsub(/[-_](.)/) { $1.upcase }
190
+ if klass_name =~ /^[A-Z]\w*$/ && const_defined?(klass_name)
191
+ klass = const_get(klass_name)
192
+ if Class === klass && klass < Base
193
+ return klass
194
+ end
195
+ end
196
+ end
197
+
198
+ def self.commands
199
+ constants.map {|c| Runner.const_get(c)}.select {|c| Class === c && c < Runner::Base}.sort_by {|r| r.command_name}.uniq
200
+ end
201
+
202
+ def self.command(name, &block)
203
+ const_set(name.to_s.capitalize.gsub(/[-_](.)/) { $1.upcase },Class.new(Base,&block))
204
+ end
205
+
206
+ command :show do
207
+ banner_arguments "<story>"
208
+ summary "Show details for a story"
209
+
210
+ on "--full", "default format" do |full|
211
+ @format = :full
212
+ end
213
+
214
+ on "--raw", "same as the .feature" do |raw|
215
+ @format = :raw
216
+ end
217
+
218
+ process do |*args|
219
+ case args.size
220
+ when 0
221
+ puts "#{fanfeedrb.project_id} #{fanfeedrb.project.name}"
222
+ when 1
223
+ feature = fanfeedrb.feature(args.first)
224
+ story = feature.story
225
+ case @format
226
+ when :raw
227
+ puts feature.story.to_s(fanfeedrb.format) if feature.story
228
+ else
229
+ paginated_output do
230
+ puts_full feature.story
231
+ end
232
+ end
233
+ else
234
+ too_many
235
+ end
236
+ end
237
+ end
238
+
239
+ command :search do
240
+ banner_arguments "[query]"
241
+ summary "List all stories matching a query"
242
+
243
+ def modifications
244
+ @modifications ||= {}
245
+ end
246
+ [:label, :type, :state].each do |o|
247
+ on "--#{o} #{o.to_s.upcase}" do |value|
248
+ modifications[o] = value
249
+ end
250
+ end
251
+ [:requester, :owner, :mywork].each do |o|
252
+ on "--#{o}[=USERNAME]" do |value|
253
+ modifications[o] = value || fanfeedrb.real_name
254
+ end
255
+ end
256
+ on "--[no-]includedone", "include accepted stories" do |value|
257
+ modifications[:includedone] = value
258
+ @iterations ||= []
259
+ @iterations << :done?
260
+ end
261
+
262
+ on "-b", "--backlog", "filter results to future iterations" do |c|
263
+ @iterations ||= []
264
+ @iterations << :backlog?
265
+ end
266
+
267
+ on "-c", "--current", "filter results to current iteration" do |b|
268
+ @iterations ||= []
269
+ @iterations << :current?
270
+ end
271
+
272
+ on "--[no-]full", "show full story, not a summary line" do |b|
273
+ @full = b
274
+ end
275
+
276
+ process do |*argv|
277
+ argv << modifications unless modifications.empty?
278
+ if argv == [{:includedone => true}]
279
+ # Bypass the 200 search results limitation
280
+ stories = fanfeedrb.project.stories
281
+ else
282
+ stories = fanfeedrb.project.stories(*argv)
283
+ end
284
+ if @iterations && @iterations != [:done?]
285
+ stories.reject! {|s| !@iterations.any? {|i| s.send(i)}}
286
+ end
287
+ paginated_output do
288
+ first = true
289
+ stories.each do |story|
290
+ if @full
291
+ puts unless first
292
+ puts_full story
293
+ else
294
+ puts_summary story
295
+ end
296
+ first = false
297
+ end
298
+ end
299
+ end
300
+ end
301
+
302
+ command :push do
303
+ banner_arguments "[story] ..."
304
+ summary "Upload stories"
305
+ description <<-EOF
306
+ Upload the given story or all features with a tracker url in a comment on the
307
+ first line. Features with a blank comment in the first line will created as
308
+ new stories.
309
+ EOF
310
+
311
+ process do |*args|
312
+ args.replace(fanfeedrb.local_features) if args.empty?
313
+ args.each do |arg|
314
+ fanfeedrb.feature(arg).push
315
+ end
316
+ end
317
+ end
318
+
319
+ command :pull do
320
+ banner_arguments "[story] ..."
321
+ summary "Download stories"
322
+ description <<-EOF
323
+ Download the given story story to the features/ directory. With no arguments,
324
+ downloads all stories that look like Cucumber features (with "Scenario:" and
325
+ valid Cucumber syntax). The version without arguments is mainly useful for
326
+ syncing existing features.
327
+ EOF
328
+
329
+ process do |*args|
330
+ args.replace(fanfeedrb.scenario_features) if args.empty?
331
+ args.each do |arg|
332
+ fanfeedrb.feature(arg).pull
333
+ end
334
+ end
335
+ end
336
+
337
+ command :start do
338
+ banner_arguments "<story> [basename]"
339
+ summary "Pull a story and mark it started"
340
+ description <<-EOF
341
+ Pull a given story and change its state to started. If basename is given
342
+ and no local file exists, features/basename.feature will be created. Give a
343
+ basename of "-" to use a downcased, underscored version of the story name as
344
+ the basename.
345
+ EOF
346
+
347
+ process do |story, *args|
348
+ fanfeedrb.feature(story).start(args.first)
349
+ end
350
+ end
351
+
352
+ command :finish do
353
+ banner_arguments "<story>"
354
+ summary "Push a story and mark it finished"
355
+
356
+ process do |story|
357
+ fanfeedrb.feature(story).finish
358
+ end
359
+ end
360
+
361
+ command :deliver do
362
+ banner_arguments "[story] ..."
363
+ summary "Mark stories delivered"
364
+ on "--all-finished", "deliver all finished stories" do
365
+ @all = true
366
+ end
367
+ process do |*args|
368
+ if @all
369
+ fanfeedrb.deliver_all_finished_stories
370
+ end
371
+ args.each do |arg|
372
+ fanfeedrb.story(arg).transition!('delivered')
373
+ end
374
+ end
375
+ end
376
+
377
+ command :unstart do
378
+ banner_arguments "[story] ..."
379
+ summary "Mark stories unstarted"
380
+ on "--all-started", "unstart all started stories" do
381
+ @all = true
382
+ end
383
+ process do |*args|
384
+ if @all
385
+ fanfeedrb.project.stories(:state => "started").each do |story|
386
+ story.transition!('unstarted')
387
+ end
388
+ end
389
+ args.each do |arg|
390
+ fanfeedrb.story(arg).transition!('unstarted')
391
+ end
392
+ end
393
+ end
394
+
395
+ command :unschedule do
396
+ banner_arguments "[story] ..."
397
+ summary "Move stories to icebox"
398
+ process do |*args|
399
+ args.each do |arg|
400
+ fanfeedrb.story(arg).transition!('unscheduled')
401
+ end
402
+ end
403
+ end
404
+
405
+ command :browse do
406
+ banner_arguments "[story]"
407
+ summary "Open a story in the web browser"
408
+ description <<-EOF
409
+ Open project or a story in the web browser.
410
+
411
+ Requires launchy (gem install launchy).
412
+ EOF
413
+
414
+ on "--dashboard" do
415
+ @special = "dashboard"
416
+ end
417
+ on "--faq" do
418
+ @special = "help"
419
+ end
420
+ on "--profile", "get your API Token here" do
421
+ @special = "profile"
422
+ end
423
+ on "--time", "not publicly available" do
424
+ @special = "time_shifts?project=#{fanfeedrb.project_id}"
425
+ end
426
+
427
+ process do |*args|
428
+ too_many if args.size > 1 || @special && args.first
429
+ if args.first
430
+ url = fanfeedrb.story(args.first).url
431
+ elsif @special
432
+ url = "http://www.pivotaltracker.com/#@special"
433
+ else
434
+ url = "http://www.pivotaltracker.com/projects/#{fanfeedrb.project_id}/stories"
435
+ end
436
+ require 'launchy'
437
+ Launchy.open(url)
438
+ end
439
+ end
440
+
441
+ command :comment do
442
+ banner_arguments "<story> <paragraph> ..."
443
+ summary "Post a comment to a story"
444
+
445
+ process do |story, *paragraphs|
446
+ fanfeedrb.story(story).comment!(paragraphs.join("\n\n"))
447
+ end
448
+ end
449
+
450
+ def initialize(argv)
451
+ @argv = argv
452
+ end
453
+
454
+ COLORS = {
455
+ :black => 0,
456
+ :red => 1,
457
+ :green => 2,
458
+ :yellow => 3,
459
+ :blue => 4,
460
+ :magenta => 5,
461
+ :cyan => 6,
462
+ :white => 7
463
+ }
464
+
465
+ STATE_COLORS = {
466
+ nil => COLORS[:black],
467
+ "rejected" => COLORS[:red],
468
+ "accepted" => COLORS[:green],
469
+ "delivered" => COLORS[:yellow],
470
+ "unscheduled" => COLORS[:white],
471
+ "started" => COLORS[:magenta],
472
+ "finished" => COLORS[:cyan],
473
+ "unstarted" => COLORS[:blue]
474
+ }
475
+
476
+ STATE_SYMBOLS = {
477
+ "unscheduled" => " ",
478
+ "unstarted" => ":|",
479
+ "started" => ":/",
480
+ "finished" => ":)",
481
+ "delivered" => ";)",
482
+ "rejected" => ":(",
483
+ "accepted" => ":D"
484
+ }
485
+
486
+ TYPE_COLORS = {
487
+ 'chore' => COLORS[:blue],
488
+ 'feature' => COLORS[:magenta],
489
+ 'bug' => COLORS[:red],
490
+ 'release' => COLORS[:cyan]
491
+ }
492
+
493
+ TYPE_SYMBOLS = {
494
+ "feature" => "*",
495
+ "chore" => "%",
496
+ "release" => "!",
497
+ "bug" => "/"
498
+ }
499
+
500
+ def run
501
+ command = @argv.shift
502
+ if klass = self.class[command]
503
+ result = klass.new(@argv).run
504
+ exit result.respond_to?(:to_int) ? result.to_int : 0
505
+ elsif ['help', '--help', '-h', '', nil].include?(command)
506
+ puts "usage: fanfeedrb <command> [options] [arguments]"
507
+ puts
508
+ puts "Commands:"
509
+ self.class.commands.each do |command|
510
+ puts " %-19s %s" % [command.command_name, command.summary]
511
+ end
512
+ puts
513
+ puts "Run fanfeedrb <command> --help for help with a given command"
514
+ else
515
+ raise Error, "Unknown fanfeedrb command #{command}"
516
+ end
517
+ rescue fanfeedrb::Error
518
+ $stderr.puts "#$!"
519
+ exit 1
520
+ rescue Interrupt
521
+ $stderr.puts "Interrupted!"
522
+ exit 130
523
+ end
524
+
525
+ end
526
+ end
527
+
@@ -1,3 +1,3 @@
1
1
  module Fanfeedrb
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
data/lib/fanfeedrb.rb CHANGED
@@ -1,85 +1,239 @@
1
- require 'httparty'
2
- module Fanfeedrb
3
-
1
+ class Fanfeedrb
2
+ class Error < RuntimeError
3
+ end
4
4
 
5
- class Client
6
- include HTTParty
7
-
8
- def initialize(key,type)
9
- @auth = {:key => key, :type => type}
10
- @proxy = FanfeedrbProxy.new
11
- end
12
-
13
- def method_missing(method, *args, &block)
14
- @proxy.append(method, args[0])
15
- @opts = {:query => @proxy.options}
16
- if args.size > 0 && !method.to_s.eql?("post")
17
- execute("get")
18
- elsif method.to_s.match /\bget\b|\bpost\b/
19
- execute(method)
5
+ autoload :Runner, 'fanfeedrb/runner'
6
+
7
+ def self.config
8
+ @config ||= {'api_token' => ENV["FANFEEDR_API_TOKEN"]}.merge(
9
+ if File.exist?(path = File.expand_path('~/.fanfeedr.yml'))
10
+ YAML.load_file(path)
11
+ end || {}
12
+ )
13
+ end
14
+
15
+ def self.hash_to_xml(root, attributes)
16
+ require 'cgi'
17
+ xml = "<#{root}>"
18
+ attributes.each do |k,v|
19
+ if v.kind_of?(Hash)
20
+ xml << hash_to_xml(k, v)
20
21
  else
21
- execute("get")
22
+ xml << "<#{k}>#{CGI.escapeHTML(v.to_s)}</#{k}>"
22
23
  end
23
24
  end
24
-
25
- def execute(method)
26
- p @proxy.url
27
- res = FanfeedrbResponse.construct self.class.send(method,@proxy.url,@opts)
28
- @proxy = FanfeedrbProxy.new
29
- res
30
- end
25
+ xml << "</#{root}>"
31
26
  end
32
-
33
- class FanfeedrbProxy
34
- attr_reader :options
35
-
36
- def initialize
37
- @keys = []; @options = {}
38
- end
39
-
40
- def append(key,options)
41
- #@options.merge!(self.default_json_options)
42
- @keys << key; @options.merge!(options) if options
27
+
28
+ def self.run(argv)
29
+ Runner.new(argv).run
30
+ end
31
+
32
+ attr_reader :directory
33
+
34
+ def initialize(path = '.')
35
+ @lang = 'en'
36
+ @directory = File.expand_path(path)
37
+ #until File.directory?(File.join(@directory,'features'))
38
+ #if @directory == File.dirname(@directory)
39
+ #raise Error, 'not found. Make sure you have a features/ directory.', caller
40
+ #end
41
+ #@directory = File.dirname(@directory)
42
+ #end
43
+ end
44
+
45
+ def feedr_path(*subdirs)
46
+ File.join(@directory,'fanfeedr',*subdirs)
47
+ end
48
+
49
+ def config_file
50
+ feedr_path('fanfeedr.yml')
51
+ end
52
+
53
+ def config
54
+ @config ||= File.exist?(config_file) && YAML.load_file(config_file) || {}
55
+ self.class.config.merge(@config)
56
+ end
57
+
58
+ def new_story(attributes = {}, &block)
59
+ attributes = attributes.inject('requested_by' => real_name) do |h,(k,v)|
60
+ h.update(k.to_s => v)
43
61
  end
62
+ .new_story(attributes, &block)
63
+ end
64
+
65
+ def stories(*args)
66
+ project.stories(*args)
67
+ end
68
+
69
+ def name
70
+ league.name
71
+ end
72
+
73
+ def iteration_length
74
+ project.iteration_length
75
+ end
76
+
77
+ def point_scale
78
+ project.point_scale
79
+ end
80
+
81
+ def week_start_day
82
+ project.week_start_day
83
+ end
84
+
85
+ def deliver_all_finished_stories
86
+ project.deliver_all_finished_stories
87
+ end
88
+
89
+ #def parse(story)
90
+ #require 'cucumber'
91
+ #Cucumber::FeatureFile.new(story.url, story.to_s).parse(
92
+ #Cucumber::Cli::Options.new,
93
+ #{}
94
+ #)
95
+ #end
96
+
97
+ def league_id
98
+ config["league_id"] || (self.class.config["leagues"]||{})[File.basename(@directory)]
99
+ end
100
+
101
+ def leagues
44
102
 
45
- def url
46
- @url = "http://ffapi.fanfeedr.com/#{ENV['FANFEEDR_TYPE']}/api/" + @keys.join("/")
103
+ end
104
+
105
+ def league
106
+ @league ||= Dir.chdir(@directory) do
107
+ unless token = config['api_token']
108
+ raise Error, 'echo api_token: ... > ~/.fanfeedr.yml'
109
+ end
110
+ unless plan = config['api_plan']
111
+ raise Error, 'echo api_plan: ... > ~/.fanfeedr.yml'
112
+ end
113
+ unless id = league_id
114
+ raise Error, 'echo league_id: ... > fanfeedr/fanfeedr.yml'
115
+ end
116
+ ssl = config['ssl']
117
+ Fanfeedr.new(token, plan, ssl).league(id)
47
118
  end
48
- protected
49
-
50
- def default_json_options
51
- {:api_key => @auth[:key]}
119
+ end
120
+
121
+ def scenario_word
122
+ #require 'cucumber'
123
+ #Gherkin::I18n::LANGUAGES[@lang]['scenario']
124
+ if @lang == 'en'
125
+ 'Scenario'
126
+ else
127
+ raise Error, 'Sorry, no internationalization support (yet)'
52
128
  end
53
129
 
54
130
  end
55
131
 
56
- class FanfeedrbResponse
57
- attr_reader :errors
58
- attr_reader :hresp
59
- def initialize(hash)
60
- hresp = []
61
- if hash.parsed_response.nil? || hash.parsed_response.blank?
62
- hresp << hash
63
- else
64
- hresp << hash.parsed_response
65
- end
66
- @hresp = hresp.flatten if !hresp.blank?
132
+ def format
133
+ (config['format'] || :tag).to_sym
134
+ end
67
135
 
68
- end
69
-
70
- def self.construct(res)
71
- return res.class == Array ? res.collect { |item| FanfeedrbResponse.new(item) } : FanfeedrbResponse.new(res)
136
+ def local_features
137
+ Dir[features_path('**','*.feature')].map {|f|feature(f)}.select {|f|f.pushable?}
138
+ end
139
+
140
+ def scenario_features (excluded_states = %w()) #(excluded_states = %w(unscheduled unstarted))
141
+ project.stories(scenario_word, :includedone => true).reject do |s|
142
+ Array(excluded_states).map {|state| state.to_s}.include?(s.current_state)
143
+ end.select do |s|
144
+ s.to_s =~ /^\s*#{Regexp.escape(scenario_word)}:/
72
145
  end
73
146
  end
147
+
148
+ def feature(string)
149
+ string.kind_of?(Feature) ? string : Feature.new(self,string)
150
+ end
151
+
152
+ def story(string)
153
+ feature(string).story
154
+ end
155
+
156
+ protected
157
+
158
+
74
159
  end
75
160
 
76
- #Examples
77
- #client = Fanfeedrb::Client.new(ENV['FANFEEDR_KEY'],ENV['FANFEEDR_TYPE'])
78
- #leagues = client.statuses.show(:id => "13400589015")
79
- #p status.errors ? status.errors : status.text
161
+ #class Client
162
+ #def initialize(key,type)
163
+ #@auth = {:key => key, :type => type}
164
+ #@proxy = FanfeedrbProxy.new
165
+ #end
166
+
167
+ #def method_missing(method, *args, &block)
168
+ #@proxy.append(method, args[0])
169
+ #@opts = {:query => @proxy.options}
170
+ #if args.size > 0 && !method.to_s.eql?("post")
171
+ #execute("get")
172
+ #elsif method.to_s.match /\bget\b|\bpost\b/
173
+ #execute(method)
174
+ #else
175
+ #execute("get")
176
+ #end
177
+ #end
178
+
179
+ #def execute(method)
180
+ #p @proxy.url
181
+ #res = FanfeedrbResponse.construct self.class.send(method,@proxy.url,@opts)
182
+ #@proxy = FanfeedrbProxy.new
183
+ #res
184
+ #end
185
+ #end
186
+
187
+ #class FanfeedrbProxy
188
+ #attr_reader :options
189
+
190
+ #def initialize
191
+ #@keys = []; @options = {}
192
+ #end
193
+
194
+ #def append(key,options)
195
+ ##@options.merge!(self.default_json_options)
196
+ #@keys << key; @options.merge!(options) if options
197
+ #end
198
+
199
+ #def url
200
+ #@url = "http://ffapi.fanfeedr.com/#{ENV['FANFEEDR_TYPE']}/api/" + @keys.join("/")
201
+ #end
202
+ #protected
203
+
204
+ #def default_json_options
205
+ #{:api_key => @auth[:key]}
206
+ #end
207
+
208
+ #end
209
+
210
+ #class FanfeedrbResponse
211
+ #attr_reader :errors
212
+ #attr_reader :hresp
213
+ #def initialize(hash)
214
+ #hresp = []
215
+ #if hash.parsed_response.nil? || hash.parsed_response.blank?
216
+ #hresp << hash
217
+ #else
218
+ #hresp << hash.parsed_response
219
+ #end
220
+ #@hresp = hresp.flatten if !hresp.blank?
221
+
222
+ #end
223
+
224
+ #def self.construct(res)
225
+ #return res.class == Array ? res.collect { |item| FanfeedrbResponse.new(item) } : FanfeedrbResponse.new(res)
226
+ #end
227
+ #end
228
+ #end
80
229
 
81
- # More Examples
82
- #user = client.users.lookup(:screen_name => "gregosuri")
83
- #client.statuses.update.post(:status=>"Ruby Metaprogramming Rocks")
84
- # Your code goes here...
230
+ ##Examples
231
+ ##client = Fanfeedrb::Client.new(ENV['FANFEEDR_KEY'],ENV['FANFEEDR_TYPE'])
232
+ ##leagues = client.statuses.show(:id => "13400589015")
233
+ ##p status.errors ? status.errors : status.text
234
+ #http://ffapi.fanfeedr.com/silver/api/leagues/20f0857f-3c43-5f50-acfc-879f838ee853/events/4dd1704b-a712-511c-b947-8c8f03ea3200?api_key=vbxctn5sn8x7jz644evkrhtc
235
+ ## More Examples
236
+ ##user = client.users.lookup(:screen_name => "gregosuri")
237
+ ##client.statuses.update.post(:status=>"Ruby Metaprogramming Rocks")
238
+ ## Your code goes here...
85
239
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fanfeedrb
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,12 +9,12 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-06-09 00:00:00.000000000 -04:00
12
+ date: 2011-06-12 00:00:00.000000000 -04:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: rake
17
- requirement: &2152451880 !ruby/object:Gem::Requirement
17
+ requirement: &2164394580 !ruby/object:Gem::Requirement
18
18
  none: false
19
19
  requirements:
20
20
  - - ! '>='
@@ -22,10 +22,10 @@ dependencies:
22
22
  version: '0'
23
23
  type: :runtime
24
24
  prerelease: false
25
- version_requirements: *2152451880
25
+ version_requirements: *2164394580
26
26
  - !ruby/object:Gem::Dependency
27
- name: httparty
28
- requirement: &2152451240 !ruby/object:Gem::Requirement
27
+ name: crack
28
+ requirement: &2164394160 !ruby/object:Gem::Requirement
29
29
  none: false
30
30
  requirements:
31
31
  - - ! '>='
@@ -33,7 +33,7 @@ dependencies:
33
33
  version: '0'
34
34
  type: :runtime
35
35
  prerelease: false
36
- version_requirements: *2152451240
36
+ version_requirements: *2164394160
37
37
  description: Ruby implementation of the FanFeedr api
38
38
  email:
39
39
  - mjording@opengotham.com
@@ -46,6 +46,14 @@ files:
46
46
  - Rakefile
47
47
  - fanfeedrb.gemspec
48
48
  - lib/fanfeedrb.rb
49
+ - lib/fanfeedrb/fanfeedr.rb
50
+ - lib/fanfeedrb/fanfeedr/conference.rb
51
+ - lib/fanfeedrb/fanfeedr/content.rb
52
+ - lib/fanfeedrb/fanfeedr/event.rb
53
+ - lib/fanfeedrb/fanfeedr/geo.rb
54
+ - lib/fanfeedrb/fanfeedr/league.rb
55
+ - lib/fanfeedrb/fanfeedr/team.rb
56
+ - lib/fanfeedrb/runner.rb
49
57
  - lib/fanfeedrb/version.rb
50
58
  has_rdoc: true
51
59
  homepage: http://iequalsi.com