railsquest 0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in railsquest.gemspec
4
+ gem 'rest-client'
data/Rakefile ADDED
@@ -0,0 +1,26 @@
1
+ lib = File.expand_path('../lib/', __FILE__)
2
+ $:.unshift lib unless $:.include?(lib)
3
+
4
+ desc "Boot up railsquest"
5
+ task :default do
6
+ exec "bundle exec bin/railsquest"
7
+ end
8
+
9
+ desc "Boot up just the web interface"
10
+ task :web do
11
+ exec "bundle exec thin start -c #{File.dirname(__FILE__)}/sinatra -p 4567"
12
+ end
13
+
14
+ require "railsquest/version"
15
+ version = Railsquest::VERSION
16
+ gem_name = "railsquest-#{version}.gem"
17
+
18
+ desc "Build #{gem_name}"
19
+ task :build do
20
+ system "gem build railsquest.gemspec"
21
+ end
22
+
23
+ desc "Tag and push #{gem_name}"
24
+ task :push => :build do
25
+ system "git tag #{version} && git push && git push --tags && gem push #{gem_name}"
26
+ end
data/Readme.md ADDED
@@ -0,0 +1,134 @@
1
+ Railsquest - Propose and pursue quests for Ruby programming glory!
2
+ ==================================================================
3
+
4
+ Pursue fame and glory by discovering and completing Quests, or offer a Quest to challenge your friends!
5
+ As you complete Quests, your trophy page will slowly fill up with your achievement badges.
6
+
7
+ Railsquest brings the magic of Bonjour auto-discovery to find Quests and other Challengers in your network.
8
+
9
+ Installation and usage
10
+ ----------------------
11
+
12
+ Install it (from [gemcutter](http://gemcutter.org/)) via gems:
13
+
14
+ gem install railsquest
15
+
16
+ (you might need to do a `gem sources -a http://gemcutter.org` beforehand!)
17
+
18
+ Start it up:
19
+
20
+ railsquest
21
+
22
+ Then fire up [http://localhost:9876/](http://localhost:9876/) to begin your quest for the holy (g)rails!
23
+
24
+ Your name, as it will be seen by other people, is taken from your git global config setting.
25
+
26
+ You will see a list of all current adventurers, and a list of all available quests.
27
+ You can inspect the badges that have been won by other adventurers, and visit the quests to try them yourself.
28
+
29
+ How to create a quest
30
+ ---------------------
31
+
32
+ Your quest is a web application running on your machine.
33
+ You can run it on any port you choose.
34
+ To make the quest available to other adventurers, add it to your railsquests:
35
+
36
+ railsquest add <port> "Name of your quest"
37
+
38
+ Specify the correct port number, and the name of your quest.
39
+
40
+ An adventurer will commence your quest by browsing from their Railsquest page.
41
+ Your quest application will receive a GET request with a "host" parameter.
42
+ The host is the computer hostname of the challenger (again from the git global config).
43
+ (TODO: We also need to make the adventurer's personal name available)
44
+
45
+ When you decide that an adventurer has completed your quest, you must notify
46
+ your local Railsquest server of their success by posting two parameters to
47
+
48
+ http://localhost:9876/submit
49
+
50
+ The post must contain these two parameters:
51
+
52
+ * The adventurer's "hostname"
53
+ * The "quest_name"
54
+
55
+ For example:
56
+
57
+ require 'rest-client'
58
+
59
+ RestClient.post 'http://localhost:9876/submit', :hostname => hostname, :quest_name => 'My Funky Quest'
60
+
61
+ Or using JQuery:
62
+
63
+ $.post('http://localhost:9876/submit', {hostname: hostname, quest_name: 'My Funky Quest'});
64
+
65
+ Railsquest will then sign these facts into an achievement badge that will be displayed on the adventurer's trophy page!
66
+
67
+ Linux support
68
+ -------------
69
+
70
+ To install the dnssd gem on Linux you'll need [avahi](http://avahi.org/). For Ubunutu peeps this means:
71
+
72
+ sudo aptitude update
73
+
74
+ sudo aptitude install g++ ruby-dev \
75
+ libavahi-compat-libdnssd-dev avahi-discover avahi-utils
76
+
77
+ and you'll need to set the domain-name:
78
+
79
+ sudo sed -i \
80
+ -e 's/#domain-name=local/domain-name=local/' \
81
+ /etc/avahi/avahi-daemon.conf
82
+
83
+ sudo service avahi-daemon restart
84
+
85
+ You can debug whether or not Avahi can see Railsquest and git-daemon Bonjour statuses using the command 'avahi-browse'. This command can be found in the package 'avahi-utils'.
86
+
87
+ The following command will show you all of the Bonjour services running on your local network:
88
+
89
+ avahi-browse --all
90
+
91
+ If you kill railsquest with kill -9 it doesn't get a chance to unregister the Bonjour services, and when it is restarted it will die with DNSSD::AlreadyRegisteredError. Although not ideal, you can work around this my restarting avahi-daemon first.
92
+
93
+ Note: You might have to restart the avahi-daemon sometimes if you are having problems seeing other railsquests.
94
+
95
+ Developing
96
+ ----------
97
+
98
+ bundle install
99
+ rake -T
100
+
101
+ Contributors
102
+ ------------
103
+
104
+ * [Sam Dalton](http://github.com/samdalton/)
105
+ * [Michael Dowse](http://michaeldowse.name/)
106
+ * [Clifford Heath](http://dataconstellation.com/)
107
+ * [Eaden McKee](@eadz)
108
+ * [Cameron Walsh]
109
+
110
+ With blithe cribbage from bananajour by...
111
+ * [Tim Lucas](http://www.toolmantim.com)
112
+ * [Carla Hackett](http://carlahackettdesign.com/) (logo)
113
+ * [Nathan de Vries](http://github.com/atnan)
114
+ * [Lachlan Hardy](http://github.com/lachlanhardy)
115
+ * [Josh Bassett](http://github.com/nullobject)
116
+ * [Myles Byrne](http://github.com/quackingduck)
117
+ * [Ben Hoskings](http://github.com/benhoskings)
118
+ * [Brett Goulder](http://github.com/brettgo1)
119
+ * [Tony Issakov](https://github.com/tissak)
120
+ * [Mark Bennett](http://github.com/MarkBennett)
121
+ * [Travis Swicegood](http://github.com/tswicegood)
122
+ * [Nate Haas](http://github.com/natehaas)
123
+ * [James Sadler](http://github.com/freshtonic)
124
+ * [Jason King](http://github.com/JasonKing)
125
+ * [Michael Pope](http://github.com/map7)
126
+
127
+ License
128
+ -------
129
+
130
+ All directories and files are MIT Licensed.
131
+
132
+ Warning to all those who still believe secrecy will save their revenue stream
133
+ -----------------------------------------------------------------------------
134
+ Bananas were meant to be shared. There are no secret bananas.
data/bin/railsquest ADDED
@@ -0,0 +1,61 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ lib = File.expand_path('../../lib/', __FILE__)
4
+ $:.unshift lib unless $:.include?(lib)
5
+
6
+ require 'railsquest'
7
+
8
+ Thread.abort_on_exception = true
9
+
10
+ Railsquest.setup! if !Railsquest.setup?
11
+
12
+ parent_process_pid = Process.pid
13
+ pids = []
14
+ at_exit do
15
+ if Process.pid == parent_process_pid
16
+ pids.each do |pid|
17
+ Process.kill("TERM", pid) rescue nil
18
+ sleep 0.2
19
+ end
20
+ end
21
+ end
22
+
23
+ case ARGV.first
24
+
25
+ when nil
26
+ pids << Railsquest.serve_web!
27
+ pids << Railsquest.advertise!
28
+ Process.waitall
29
+
30
+ when "add", "init"
31
+ quest = Railsquest.add!(ARGV[1], ARGV[2])
32
+
33
+ when "remove", "rm"
34
+ name = ARGV[1]
35
+
36
+ if !name || name.empty?
37
+ abort "You need to specify the name of the quest you'd like to remove:\n#{File.basename($0)} remove <path>"
38
+ elsif !(quest = Railsquest::Quest.for_name(name))
39
+ abort "#{name.inspect} is not a valid quest name"
40
+ end
41
+
42
+ quest.remove!
43
+
44
+ when "help", "--help", "-h"
45
+ puts <<-HELP
46
+ Usage: #{File.basename($0)} [<command>]
47
+
48
+ Commands:
49
+ none - Start the web, quest, and bonjour serving
50
+ add <port> <name> - Add a quest running on server port with given name
51
+ remove <name> - Remove a quest
52
+ help
53
+ version
54
+ HELP
55
+
56
+ when "version", "--version", "-v"
57
+ puts "railsquest version #{Railsquest::VERSION}"
58
+
59
+ else
60
+ abort "Say what? Try: #{File.basename($0)} help"
61
+ end
data/development.log ADDED
@@ -0,0 +1 @@
1
+ # Logfile created on 2011-03-19 00:36:04 +1300 by logger.rb/25413
data/lib/railsquest.rb ADDED
@@ -0,0 +1,119 @@
1
+ libdir = File.dirname(__FILE__)
2
+ $LOAD_PATH.unshift(libdir) unless $LOAD_PATH.include?(libdir)
3
+
4
+ require 'railsquest/quest'
5
+ require 'railsquest/badge'
6
+ require 'railsquest/grit_extensions'
7
+ require 'railsquest/version'
8
+ require 'railsquest/bonjour'
9
+ require 'railsquest/helpers'
10
+ require 'railsquest/commands'
11
+
12
+ require 'ostruct'
13
+ require 'socket'
14
+ require 'pathname'
15
+ require 'rest-client'
16
+
17
+ module Railsquest
18
+
19
+ class << self
20
+
21
+ include DateHelpers
22
+ include GravatarHelpers
23
+ include Commands
24
+
25
+ def setup?
26
+ quests_path.exist?
27
+ badges_path.exist?
28
+ end
29
+
30
+ def setup!
31
+ quests_path.mkpath
32
+ badges_path.mkpath
33
+ end
34
+
35
+ def path
36
+ Pathname("~/.railsquest").expand_path
37
+ end
38
+
39
+ def quests_path
40
+ path + "quests"
41
+ end
42
+
43
+ def badges_path
44
+ path + "badges"
45
+ end
46
+
47
+ def get_git_global_config(key)
48
+ `git config --global #{key}`.strip
49
+ end
50
+
51
+ def config
52
+ @config ||= begin
53
+ OpenStruct.new({
54
+ :name => get_git_global_config("user.name"),
55
+ :email => get_git_global_config("user.email")
56
+ })
57
+ end
58
+ end
59
+
60
+ def web_port
61
+ 9876
62
+ end
63
+
64
+ def web_uri
65
+ "http://#{host_name}:#{web_port}/"
66
+ end
67
+
68
+ def host_name
69
+ hn = get_git_global_config("railsquest.hostname")
70
+ unless hn.nil? or hn.empty?
71
+ return hn
72
+ end
73
+
74
+ hn = Socket.gethostname
75
+
76
+ # if there is more than one period in the hostname then assume it's a FQDN
77
+ # and the user knows what they're doing
78
+ return hn if hn.count('.') > 1
79
+
80
+ if hn =~ /\.local$/
81
+ hn
82
+ else
83
+ hn + ".local"
84
+ end
85
+ end
86
+
87
+ def quest_uri
88
+ "http://#{host_name}/"
89
+ end
90
+
91
+ def quests
92
+ quests_path.children.reject{ |b| b.to_s =~ /DS_Store/}.map {|q| Quest.new(q) }
93
+ end
94
+
95
+ def quest(name)
96
+ quests.find {|r| r.name == name}
97
+ end
98
+
99
+ def badges
100
+ badges_path.children.reject{ |b| b.to_s =~ /DS_Store/}.map {|b| Badge.new(b).to_hash }
101
+ end
102
+
103
+ def to_hash
104
+ {
105
+ "name" => config.name,
106
+ "email" => config.email,
107
+ "uri" => web_uri,
108
+ "git-uri" => quest_uri,
109
+ "gravatar" => Railsquest.gravatar,
110
+ "version" => Railsquest::VERSION,
111
+ "quests" => quests.collect do |r|
112
+ {"name" => r.name, "uri" => r.uri}
113
+ end
114
+ }
115
+ end
116
+
117
+ end
118
+
119
+ end
@@ -0,0 +1,84 @@
1
+ require 'pathname'
2
+
3
+ module Railsquest
4
+ class Badge
5
+
6
+ attr_accessor :name
7
+
8
+ def self.for_name(name)
9
+ n = name.gsub(' ', '_')
10
+ q = new(Railsquest.badges_path.join(n + ".badge"))
11
+ q.name = n
12
+ q
13
+ end
14
+
15
+ def self.html_id(name)
16
+ name.gsub(/[^A-Za-z-]+/, '').downcase
17
+ end
18
+
19
+ def initialize(path)
20
+ @path = Pathname(path)
21
+ end
22
+
23
+ def ==(other)
24
+ other.respond_to?(:path) && self.path == other.path
25
+ end
26
+
27
+ attr_reader :path
28
+
29
+ def exist?
30
+ path.exist?
31
+ end
32
+
33
+ def init!(signature, original_host)
34
+ contents = '{\"signature\" : \"' + signature + '\", \"original_host\" : \"' + original_host + '\"}'
35
+ Dir.chdir(Railsquest.badges_path) { `echo #{contents} >> #{path}` }
36
+ end
37
+
38
+ def uri
39
+ Railsquest.quest_uri.gsub(/\/$/, '') + ':' + File.open(path) { |f| f.gets }
40
+ end
41
+
42
+ def name
43
+ dirname.sub(".badge",'')
44
+ end
45
+
46
+ def html_id
47
+ self.class.html_id(name)
48
+ end
49
+
50
+ def dirname
51
+ path.split.last.to_s
52
+ end
53
+
54
+ def to_s
55
+ name
56
+ end
57
+
58
+ def web_uri
59
+ Railsquest.web_uri + "#" + html_id
60
+ end
61
+
62
+ def remove!
63
+ path.rmtree
64
+ end
65
+
66
+ def verified?
67
+ contents = JSON.parse(File.read(path))
68
+ begin
69
+ RestClient.post 'http://' + contents['original_host'] + ':' + Railsquest.web_port.to_s + '/verify', { :signature => contents['signature'], :quest_name => name, :hostname => Railsquest.host_name }
70
+ rescue
71
+ false
72
+ end
73
+ end
74
+
75
+ def to_hash
76
+ contents = JSON.parse(File.read(path))
77
+ {
78
+ "original_host" => contents['original_host'],
79
+ "signature" => contents['signature'],
80
+ "name" => name
81
+ }
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,9 @@
1
+ module Railsquest::Bonjour
2
+ end
3
+
4
+ require 'railsquest/bonjour/person'
5
+ require 'railsquest/bonjour/quest'
6
+ require 'railsquest/bonjour/browser'
7
+ require 'railsquest/bonjour/quest_browser'
8
+ require 'railsquest/bonjour/railsquest_browser'
9
+ require 'railsquest/bonjour/advertiser'