fanfeedrb 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
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