railsquest 0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +4 -0
- data/Gemfile +4 -0
- data/Rakefile +26 -0
- data/Readme.md +134 -0
- data/bin/railsquest +61 -0
- data/development.log +1 -0
- data/lib/railsquest.rb +119 -0
- data/lib/railsquest/badge.rb +84 -0
- data/lib/railsquest/bonjour.rb +9 -0
- data/lib/railsquest/bonjour/advertiser.rb +66 -0
- data/lib/railsquest/bonjour/browser.rb +58 -0
- data/lib/railsquest/bonjour/person.rb +25 -0
- data/lib/railsquest/bonjour/quest.rb +39 -0
- data/lib/railsquest/bonjour/quest_browser.rb +36 -0
- data/lib/railsquest/bonjour/railsquest_browser.rb +32 -0
- data/lib/railsquest/commands.rb +53 -0
- data/lib/railsquest/grit_extensions.rb +11 -0
- data/lib/railsquest/helpers.rb +100 -0
- data/lib/railsquest/quest.rb +86 -0
- data/lib/railsquest/version.rb +3 -0
- data/railsquest.gemspec +21 -0
- data/sinatra/app.rb +162 -0
- data/sinatra/config.ru +2 -0
- data/sinatra/lib/diff_helpers.rb +92 -0
- data/sinatra/lib/mock_browsers.rb +55 -0
- data/sinatra/public/jquery-1.3.2.min.js +19 -0
- data/sinatra/public/loader.gif +0 -0
- data/sinatra/public/logo.png +0 -0
- data/sinatra/public/pbjt.swf +0 -0
- data/sinatra/public/peanut.png +0 -0
- data/sinatra/public/rm.gif +0 -0
- data/sinatra/public/theme.mp3 +0 -0
- data/sinatra/views/home.haml +32 -0
- data/sinatra/views/layout.haml +346 -0
- data/sinatra/views/readme.haml +52 -0
- data/sinatra/views/run.haml +1 -0
- data/sinatra/views/user.haml +21 -0
- metadata +103 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
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'
|