railsquest 0.1
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/.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
| @@ -0,0 +1,66 @@ | |
| 1 | 
            +
            require 'dnssd'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            class Railsquest::Bonjour::Advertiser
         | 
| 4 | 
            +
              def initialize
         | 
| 5 | 
            +
                @services = []
         | 
| 6 | 
            +
              end
         | 
| 7 | 
            +
             | 
| 8 | 
            +
              def go!
         | 
| 9 | 
            +
                register_app
         | 
| 10 | 
            +
                register_quests
         | 
| 11 | 
            +
              end
         | 
| 12 | 
            +
             | 
| 13 | 
            +
              private
         | 
| 14 | 
            +
                def register_app
         | 
| 15 | 
            +
                  STDOUT.puts "Registering #{Railsquest.web_uri}"
         | 
| 16 | 
            +
                  tr = DNSSD::TextRecord.new
         | 
| 17 | 
            +
                  tr["name"] = Railsquest.config.name
         | 
| 18 | 
            +
                  tr["email"] = Railsquest.config.email
         | 
| 19 | 
            +
                  tr["uri"] = Railsquest.web_uri
         | 
| 20 | 
            +
                  tr["gravatar"] = Railsquest.gravatar
         | 
| 21 | 
            +
                  tr["version"] = Railsquest::VERSION
         | 
| 22 | 
            +
                  DNSSD.register("#{Railsquest.config.name}'s railsquest", "_http._tcp,_railsquest", nil, Railsquest.web_port, tr)
         | 
| 23 | 
            +
                end
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                def register_quests
         | 
| 26 | 
            +
                  loop do
         | 
| 27 | 
            +
                    stop_old_services
         | 
| 28 | 
            +
                    register_new_quests
         | 
| 29 | 
            +
                    sleep(1)
         | 
| 30 | 
            +
                  end
         | 
| 31 | 
            +
                end
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                def stop_old_services
         | 
| 34 | 
            +
                  old_services.each do |old_service|
         | 
| 35 | 
            +
                    STDOUT.puts "Unregistering #{old_service.quest.uri}"
         | 
| 36 | 
            +
                    old_service.stop
         | 
| 37 | 
            +
                    @services.delete(old_service)
         | 
| 38 | 
            +
                  end
         | 
| 39 | 
            +
                end
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                def old_services
         | 
| 42 | 
            +
                  @services.reject {|s| Railsquest.quests.include?(s.quest)}
         | 
| 43 | 
            +
                end
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                def register_new_quests
         | 
| 46 | 
            +
                  new_quests.each do |new_quest|
         | 
| 47 | 
            +
                    STDOUT.puts "Registering #{new_quest.uri}"
         | 
| 48 | 
            +
                    tr = DNSSD::TextRecord.new
         | 
| 49 | 
            +
                    tr["name"] = new_quest.name
         | 
| 50 | 
            +
                    tr["uri"] = new_quest.uri
         | 
| 51 | 
            +
                    tr["bjour-name"] = Railsquest.config.name
         | 
| 52 | 
            +
                    tr["bjour-email"] = Railsquest.config.email
         | 
| 53 | 
            +
                    tr["bjour-uri"] = Railsquest.web_uri
         | 
| 54 | 
            +
                    tr["bjour-gravatar"] = Railsquest.gravatar
         | 
| 55 | 
            +
                    tr["bjour-version"] = Railsquest::VERSION
         | 
| 56 | 
            +
                    service = DNSSD.register(new_quest.name, "_git._tcp,_railsquest", nil, 9877, tr)
         | 
| 57 | 
            +
                    service.class.instance_eval { attr_accessor(:quest) }
         | 
| 58 | 
            +
                    service.quest = new_quest
         | 
| 59 | 
            +
                    @services << service
         | 
| 60 | 
            +
                  end
         | 
| 61 | 
            +
                end
         | 
| 62 | 
            +
             | 
| 63 | 
            +
                def new_quests
         | 
| 64 | 
            +
                  Railsquest.quests.select {|quest| !@services.any? {|s| s.quest == quest } }
         | 
| 65 | 
            +
                end
         | 
| 66 | 
            +
            end
         | 
| @@ -0,0 +1,58 @@ | |
| 1 | 
            +
            require 'dnssd'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require 'thread'
         | 
| 4 | 
            +
            require 'timeout'
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            Thread.abort_on_exception = true
         | 
| 7 | 
            +
             | 
| 8 | 
            +
            # Generic bonjour browser
         | 
| 9 | 
            +
            #
         | 
| 10 | 
            +
            # Example use:
         | 
| 11 | 
            +
            #
         | 
| 12 | 
            +
            #   browser = BonjourBrowser.new("_git._tcp,_railsquest")
         | 
| 13 | 
            +
            #   loop do
         | 
| 14 | 
            +
            #     sleep(1)
         | 
| 15 | 
            +
            #     pp browser.replies.map {|r| r.name}
         | 
| 16 | 
            +
            #   end
         | 
| 17 | 
            +
            #
         | 
| 18 | 
            +
            # Probably gem-worthy
         | 
| 19 | 
            +
            class Railsquest::Bonjour::Browser
         | 
| 20 | 
            +
              def initialize(service)
         | 
| 21 | 
            +
                @service = service
         | 
| 22 | 
            +
                @mutex = Mutex.new
         | 
| 23 | 
            +
                @replies = {}
         | 
| 24 | 
            +
                watch!
         | 
| 25 | 
            +
              end
         | 
| 26 | 
            +
             | 
| 27 | 
            +
              def replies
         | 
| 28 | 
            +
                @mutex.synchronize { @replies.values }
         | 
| 29 | 
            +
              end
         | 
| 30 | 
            +
             | 
| 31 | 
            +
              private
         | 
| 32 | 
            +
                def watch!
         | 
| 33 | 
            +
                  Thread.new(@service, @mutex, @replies) do |service, mutex, replies|
         | 
| 34 | 
            +
                    begin
         | 
| 35 | 
            +
                      DNSSD.browse!(service) do |reply|
         | 
| 36 | 
            +
                        Thread.new(reply, replies, mutex) do |reply, replies, mutex|
         | 
| 37 | 
            +
                          begin
         | 
| 38 | 
            +
                            DNSSD.resolve!(reply.name, reply.type, reply.domain) do |resolve_reply|
         | 
| 39 | 
            +
                              mutex.synchronize do
         | 
| 40 | 
            +
                                if reply.flags.add?
         | 
| 41 | 
            +
                                  replies[reply.fullname] = resolve_reply
         | 
| 42 | 
            +
                                else
         | 
| 43 | 
            +
                                  replies.delete(reply.fullname)
         | 
| 44 | 
            +
                                end
         | 
| 45 | 
            +
                              end
         | 
| 46 | 
            +
                              resolve_reply.service.stop unless resolve_reply.service.stopped?
         | 
| 47 | 
            +
                            end
         | 
| 48 | 
            +
                          rescue DNSSD::BadParamError
         | 
| 49 | 
            +
                            # Ignore em
         | 
| 50 | 
            +
                          end
         | 
| 51 | 
            +
                        end
         | 
| 52 | 
            +
                      end
         | 
| 53 | 
            +
                    rescue DNSSD::BadParamError
         | 
| 54 | 
            +
                      # Ignore em
         | 
| 55 | 
            +
                    end
         | 
| 56 | 
            +
                  end
         | 
| 57 | 
            +
                end
         | 
| 58 | 
            +
            end
         | 
| @@ -0,0 +1,25 @@ | |
| 1 | 
            +
            class Railsquest::Bonjour::Person
         | 
| 2 | 
            +
             | 
| 3 | 
            +
              attr_accessor :name, :email, :uri , :gravatar
         | 
| 4 | 
            +
             | 
| 5 | 
            +
              def initialize(name, email, uri, gravatar)
         | 
| 6 | 
            +
                @name, @email, @uri, @gravatar = name, email, uri, gravatar
         | 
| 7 | 
            +
              end
         | 
| 8 | 
            +
              
         | 
| 9 | 
            +
              def ==(other)
         | 
| 10 | 
            +
                self.uri == other.uri
         | 
| 11 | 
            +
              end
         | 
| 12 | 
            +
              
         | 
| 13 | 
            +
              def hash
         | 
| 14 | 
            +
                to_hash.hash
         | 
| 15 | 
            +
              end
         | 
| 16 | 
            +
              
         | 
| 17 | 
            +
              def to_hash
         | 
| 18 | 
            +
                {"name" => name, "email" => email, "uri" => uri, "gravatar" => gravatar}
         | 
| 19 | 
            +
              end
         | 
| 20 | 
            +
              
         | 
| 21 | 
            +
              def host_name
         | 
| 22 | 
            +
                uri.gsub(/http:\/\//, '').gsub(/:9876/, '')
         | 
| 23 | 
            +
              end
         | 
| 24 | 
            +
              
         | 
| 25 | 
            +
            end
         | 
| @@ -0,0 +1,39 @@ | |
| 1 | 
            +
            class Railsquest::Bonjour::Quest
         | 
| 2 | 
            +
             | 
| 3 | 
            +
              attr_accessor :name, :uri, :person 
         | 
| 4 | 
            +
             | 
| 5 | 
            +
              def initialize(name, uri, person)
         | 
| 6 | 
            +
                @name, @uri, @person = name, uri, person
         | 
| 7 | 
            +
              end
         | 
| 8 | 
            +
              
         | 
| 9 | 
            +
              def html_id
         | 
| 10 | 
            +
                Railsquest::Quest.html_id(name)
         | 
| 11 | 
            +
              end
         | 
| 12 | 
            +
             | 
| 13 | 
            +
              def ==(other)
         | 
| 14 | 
            +
                self.uri == other.uri
         | 
| 15 | 
            +
              end
         | 
| 16 | 
            +
              
         | 
| 17 | 
            +
              def hash
         | 
| 18 | 
            +
                to_hash.hash
         | 
| 19 | 
            +
              end
         | 
| 20 | 
            +
              
         | 
| 21 | 
            +
              def json_uri
         | 
| 22 | 
            +
                "#{person.uri}#{name}.json"
         | 
| 23 | 
            +
              end
         | 
| 24 | 
            +
              
         | 
| 25 | 
            +
              def web_uri
         | 
| 26 | 
            +
                "#{person.uri}##{html_id}"
         | 
| 27 | 
            +
              end
         | 
| 28 | 
            +
              
         | 
| 29 | 
            +
              def to_hash
         | 
| 30 | 
            +
                {
         | 
| 31 | 
            +
                  "name" => name,
         | 
| 32 | 
            +
                  "uri" => uri,
         | 
| 33 | 
            +
                  "json_uri" => json_uri,
         | 
| 34 | 
            +
                  "web_uri" => web_uri,
         | 
| 35 | 
            +
                  "person" => person.to_hash
         | 
| 36 | 
            +
                }
         | 
| 37 | 
            +
              end
         | 
| 38 | 
            +
             | 
| 39 | 
            +
            end
         | 
| @@ -0,0 +1,36 @@ | |
| 1 | 
            +
            module Railsquest::Bonjour
         | 
| 2 | 
            +
            class QuestBrowser
         | 
| 3 | 
            +
             | 
| 4 | 
            +
              def initialize
         | 
| 5 | 
            +
                @browser = Browser.new('_git._tcp,_railsquest')
         | 
| 6 | 
            +
              end
         | 
| 7 | 
            +
             | 
| 8 | 
            +
              def quests
         | 
| 9 | 
            +
                @browser.replies.map do |reply|
         | 
| 10 | 
            +
                  Quest.new(
         | 
| 11 | 
            +
                    reply.text_record["name"],
         | 
| 12 | 
            +
                    reply.text_record["uri"],
         | 
| 13 | 
            +
                    Person.new(
         | 
| 14 | 
            +
                      reply.text_record["bjour-name"],
         | 
| 15 | 
            +
                      reply.text_record["bjour-email"],
         | 
| 16 | 
            +
                      reply.text_record["bjour-uri"],
         | 
| 17 | 
            +
                      reply.text_record["bjour-gravatar"]
         | 
| 18 | 
            +
                    )
         | 
| 19 | 
            +
                  )
         | 
| 20 | 
            +
                end
         | 
| 21 | 
            +
              end
         | 
| 22 | 
            +
              
         | 
| 23 | 
            +
              def other_quests
         | 
| 24 | 
            +
                quests.reject {|r| Railsquest.quests.any? {|my_quest| my_quest.name == r.name}}
         | 
| 25 | 
            +
              end
         | 
| 26 | 
            +
              
         | 
| 27 | 
            +
              def quests_similar_to(quest)
         | 
| 28 | 
            +
                quests.select {|r| r.name == quest.name}
         | 
| 29 | 
            +
              end
         | 
| 30 | 
            +
              
         | 
| 31 | 
            +
              def quests_for(person)
         | 
| 32 | 
            +
                quests.select {|r| r.person == person}
         | 
| 33 | 
            +
              end
         | 
| 34 | 
            +
              
         | 
| 35 | 
            +
            end
         | 
| 36 | 
            +
            end
         | 
| @@ -0,0 +1,32 @@ | |
| 1 | 
            +
            module Railsquest::Bonjour
         | 
| 2 | 
            +
            class RailsquestBrowser
         | 
| 3 | 
            +
             | 
| 4 | 
            +
              def initialize
         | 
| 5 | 
            +
                @browser = Browser.new('_http._tcp,_railsquest')
         | 
| 6 | 
            +
              end
         | 
| 7 | 
            +
             | 
| 8 | 
            +
              def railsquests
         | 
| 9 | 
            +
                @browser.replies.map do |reply|
         | 
| 10 | 
            +
                  Person.new(
         | 
| 11 | 
            +
                    reply.text_record["name"],
         | 
| 12 | 
            +
                    reply.text_record["email"],
         | 
| 13 | 
            +
                    reply.text_record["uri"],
         | 
| 14 | 
            +
                    reply.text_record["gravatar"]
         | 
| 15 | 
            +
                  )
         | 
| 16 | 
            +
                end
         | 
| 17 | 
            +
              end
         | 
| 18 | 
            +
              
         | 
| 19 | 
            +
              def other_railsquests
         | 
| 20 | 
            +
                railsquests.reject {|b| b.uri == Railsquest.web_uri}
         | 
| 21 | 
            +
              end
         | 
| 22 | 
            +
              
         | 
| 23 | 
            +
              def all_railsquests
         | 
| 24 | 
            +
                  railsquests
         | 
| 25 | 
            +
              end
         | 
| 26 | 
            +
              
         | 
| 27 | 
            +
              def find_person(hostname)
         | 
| 28 | 
            +
                railsquests.find { |u| u.uri =~ /hostname/}
         | 
| 29 | 
            +
              end
         | 
| 30 | 
            +
              
         | 
| 31 | 
            +
            end
         | 
| 32 | 
            +
            end
         | 
| @@ -0,0 +1,53 @@ | |
| 1 | 
            +
            require 'rainbow'
         | 
| 2 | 
            +
            require 'pathname'
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            module Railsquest::Commands
         | 
| 5 | 
            +
             | 
| 6 | 
            +
              # Start sinatra app.
         | 
| 7 | 
            +
              def serve_web!
         | 
| 8 | 
            +
                puts "* Starting " + web_uri.foreground(:yellow)
         | 
| 9 | 
            +
                fork do
         | 
| 10 | 
            +
                  ENV["RACK_ENV"] ||= "production"
         | 
| 11 | 
            +
                  require "railsquest/../../sinatra/app"
         | 
| 12 | 
            +
                  Sinatra::Application.set :port, web_port
         | 
| 13 | 
            +
                  Sinatra::Application.set :server, "thin"
         | 
| 14 | 
            +
                  Sinatra::Application.run!
         | 
| 15 | 
            +
                end
         | 
| 16 | 
            +
              end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
              def advertise!
         | 
| 19 | 
            +
                fork { Railsquest::Bonjour::Advertiser.new.go! }
         | 
| 20 | 
            +
              end
         | 
| 21 | 
            +
             | 
| 22 | 
            +
              def add!(port, name = nil)
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                if name.nil?
         | 
| 25 | 
            +
                  default_name = "My Awesome Quest"
         | 
| 26 | 
            +
                  print "Quest Name?".foreground(:yellow) + " [#{default_name}] "
         | 
| 27 | 
            +
                  name = (STDIN.gets || "").strip
         | 
| 28 | 
            +
                  name = default_name if name.empty?
         | 
| 29 | 
            +
                end
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                quest = Railsquest::Quest.for_name(name)
         | 
| 32 | 
            +
                quest.port = port
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                if quest.exist?
         | 
| 35 | 
            +
                  abort "You've already a quest #{quest}."
         | 
| 36 | 
            +
                end
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                quest.init!
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                puts init_success_message(quest.dirname)
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                quest
         | 
| 43 | 
            +
              end
         | 
| 44 | 
            +
             | 
| 45 | 
            +
              def init_success_message(quest_dirname)
         | 
| 46 | 
            +
                plain_init_success_message(quest_dirname)
         | 
| 47 | 
            +
              end
         | 
| 48 | 
            +
             | 
| 49 | 
            +
              def plain_init_success_message(quest_dirname)
         | 
| 50 | 
            +
                "Railsquest quest #{quest_dirname} created."
         | 
| 51 | 
            +
              end
         | 
| 52 | 
            +
             | 
| 53 | 
            +
            end
         | 
| @@ -0,0 +1,100 @@ | |
| 1 | 
            +
            require 'digest/md5'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Railsquest
         | 
| 4 | 
            +
              module GravatarHelpers
         | 
| 5 | 
            +
                def gravatar
         | 
| 6 | 
            +
                  gravatar_uri(self.config.email)
         | 
| 7 | 
            +
                end
         | 
| 8 | 
            +
                def gravatar_uri(email)
         | 
| 9 | 
            +
                  "http://gravatar.com/avatar/#{Digest::MD5.hexdigest(email)}.png"
         | 
| 10 | 
            +
                end
         | 
| 11 | 
            +
              end
         | 
| 12 | 
            +
             | 
| 13 | 
            +
              # Lifted from Rails
         | 
| 14 | 
            +
              module DateHelpers
         | 
| 15 | 
            +
                # Reports the approximate distance in time between two Time or Date objects or integers as seconds.
         | 
| 16 | 
            +
                # Set <tt>include_seconds</tt> to true if you want more detailed approximations when distance < 1 min, 29 secs
         | 
| 17 | 
            +
                # Distances are questrted based on the following table:
         | 
| 18 | 
            +
                #
         | 
| 19 | 
            +
                #   0 <-> 29 secs                                                             # => less than a minute
         | 
| 20 | 
            +
                #   30 secs <-> 1 min, 29 secs                                                # => 1 minute
         | 
| 21 | 
            +
                #   1 min, 30 secs <-> 44 mins, 29 secs                                       # => [2..44] minutes
         | 
| 22 | 
            +
                #   44 mins, 30 secs <-> 89 mins, 29 secs                                     # => about 1 hour
         | 
| 23 | 
            +
                #   89 mins, 29 secs <-> 23 hrs, 59 mins, 29 secs                             # => about [2..24] hours
         | 
| 24 | 
            +
                #   23 hrs, 59 mins, 29 secs <-> 47 hrs, 59 mins, 29 secs                     # => 1 day
         | 
| 25 | 
            +
                #   47 hrs, 59 mins, 29 secs <-> 29 days, 23 hrs, 59 mins, 29 secs            # => [2..29] days
         | 
| 26 | 
            +
                #   29 days, 23 hrs, 59 mins, 30 secs <-> 59 days, 23 hrs, 59 mins, 29 secs   # => about 1 month
         | 
| 27 | 
            +
                #   59 days, 23 hrs, 59 mins, 30 secs <-> 1 yr minus 1 sec                    # => [2..12] months
         | 
| 28 | 
            +
                #   1 yr <-> 2 yrs minus 1 secs                                               # => about 1 year
         | 
| 29 | 
            +
                #   2 yrs <-> max time or date                                                # => over [2..X] years
         | 
| 30 | 
            +
                #
         | 
| 31 | 
            +
                # With <tt>include_seconds</tt> = true and the difference < 1 minute 29 seconds:
         | 
| 32 | 
            +
                #   0-4   secs      # => less than 5 seconds
         | 
| 33 | 
            +
                #   5-9   secs      # => less than 10 seconds
         | 
| 34 | 
            +
                #   10-19 secs      # => less than 20 seconds
         | 
| 35 | 
            +
                #   20-39 secs      # => half a minute
         | 
| 36 | 
            +
                #   40-59 secs      # => less than a minute
         | 
| 37 | 
            +
                #   60-89 secs      # => 1 minute
         | 
| 38 | 
            +
                #
         | 
| 39 | 
            +
                # ==== Examples
         | 
| 40 | 
            +
                #   from_time = Time.now
         | 
| 41 | 
            +
                #   distance_of_time_in_words(from_time, from_time + 50.minutes)        # => about 1 hour
         | 
| 42 | 
            +
                #   distance_of_time_in_words(from_time, 50.minutes.from_now)           # => about 1 hour
         | 
| 43 | 
            +
                #   distance_of_time_in_words(from_time, from_time + 15.seconds)        # => less than a minute
         | 
| 44 | 
            +
                #   distance_of_time_in_words(from_time, from_time + 15.seconds, true)  # => less than 20 seconds
         | 
| 45 | 
            +
                #   distance_of_time_in_words(from_time, 3.years.from_now)              # => over 3 years
         | 
| 46 | 
            +
                #   distance_of_time_in_words(from_time, from_time + 60.hours)          # => about 3 days
         | 
| 47 | 
            +
                #   distance_of_time_in_words(from_time, from_time + 45.seconds, true)  # => less than a minute
         | 
| 48 | 
            +
                #   distance_of_time_in_words(from_time, from_time - 45.seconds, true)  # => less than a minute
         | 
| 49 | 
            +
                #   distance_of_time_in_words(from_time, 76.seconds.from_now)           # => 1 minute
         | 
| 50 | 
            +
                #   distance_of_time_in_words(from_time, from_time + 1.year + 3.days)   # => about 1 year
         | 
| 51 | 
            +
                #   distance_of_time_in_words(from_time, from_time + 4.years + 15.days + 30.minutes + 5.seconds) # => over 4 years
         | 
| 52 | 
            +
                #
         | 
| 53 | 
            +
                #   to_time = Time.now + 6.years + 19.days
         | 
| 54 | 
            +
                #   distance_of_time_in_words(from_time, to_time, true)     # => over 6 years
         | 
| 55 | 
            +
                #   distance_of_time_in_words(to_time, from_time, true)     # => over 6 years
         | 
| 56 | 
            +
                #   distance_of_time_in_words(Time.now, Time.now)           # => less than a minute
         | 
| 57 | 
            +
                #
         | 
| 58 | 
            +
                def distance_of_time_in_words(from_time, to_time = 0, include_seconds = false)
         | 
| 59 | 
            +
                  from_time = from_time.to_time if from_time.respond_to?(:to_time)
         | 
| 60 | 
            +
                  to_time = to_time.to_time if to_time.respond_to?(:to_time)
         | 
| 61 | 
            +
                  distance_in_minutes = (((to_time - from_time).abs)/60).round
         | 
| 62 | 
            +
                  distance_in_seconds = ((to_time - from_time).abs).round
         | 
| 63 | 
            +
             | 
| 64 | 
            +
                  case distance_in_minutes
         | 
| 65 | 
            +
                    when 0..1
         | 
| 66 | 
            +
                      return (distance_in_minutes == 0) ? 'less than a minute' : '1 minute' unless include_seconds
         | 
| 67 | 
            +
                      case distance_in_seconds
         | 
| 68 | 
            +
                        when 0..4   then 'less than 5 seconds'
         | 
| 69 | 
            +
                        when 5..9   then 'less than 10 seconds'
         | 
| 70 | 
            +
                        when 10..19 then 'less than 20 seconds'
         | 
| 71 | 
            +
                        when 20..39 then 'half a minute'
         | 
| 72 | 
            +
                        when 40..59 then 'less than a minute'
         | 
| 73 | 
            +
                        else             '1 minute'
         | 
| 74 | 
            +
                      end
         | 
| 75 | 
            +
             | 
| 76 | 
            +
                    when 2..44           then "#{distance_in_minutes} minutes"
         | 
| 77 | 
            +
                    when 45..89          then 'about 1 hour'
         | 
| 78 | 
            +
                    when 90..1439        then "about #{(distance_in_minutes.to_f / 60.0).round} hours"
         | 
| 79 | 
            +
                    when 1440..2879      then '1 day'
         | 
| 80 | 
            +
                    when 2880..43199     then "#{(distance_in_minutes / 1440).round} days"
         | 
| 81 | 
            +
                    when 43200..86399    then 'about 1 month'
         | 
| 82 | 
            +
                    when 86400..525599   then "#{(distance_in_minutes / 43200).round} months"
         | 
| 83 | 
            +
                    when 525600..1051199 then 'about 1 year'
         | 
| 84 | 
            +
                    else                      "over #{(distance_in_minutes / 525600).round} years"
         | 
| 85 | 
            +
                  end
         | 
| 86 | 
            +
                end
         | 
| 87 | 
            +
             | 
| 88 | 
            +
                # Like distance_of_time_in_words, but where <tt>to_time</tt> is fixed to <tt>Time.now</tt>.
         | 
| 89 | 
            +
                #
         | 
| 90 | 
            +
                # ==== Examples
         | 
| 91 | 
            +
                #   time_ago_in_words(3.minutes.from_now)       # => 3 minutes
         | 
| 92 | 
            +
                #   time_ago_in_words(Time.now - 15.hours)      # => 15 hours
         | 
| 93 | 
            +
                #   time_ago_in_words(Time.now)                 # => less than a minute
         | 
| 94 | 
            +
                #
         | 
| 95 | 
            +
                #   from_time = Time.now - 3.days - 14.minutes - 25.seconds     # => 3 days
         | 
| 96 | 
            +
                def time_ago_in_words(from_time, include_seconds = false)
         | 
| 97 | 
            +
                  distance_of_time_in_words(from_time, Time.now, include_seconds)
         | 
| 98 | 
            +
                end
         | 
| 99 | 
            +
              end
         | 
| 100 | 
            +
            end
         | 
| @@ -0,0 +1,86 @@ | |
| 1 | 
            +
            require 'grit'
         | 
| 2 | 
            +
            require 'pathname'
         | 
| 3 | 
            +
            require 'json'
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            module Railsquest
         | 
| 6 | 
            +
              class Quest
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                attr_accessor :name, :port
         | 
| 9 | 
            +
                attr_reader :secret
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                def self.for_name(name)
         | 
| 12 | 
            +
                    n = name.gsub(/[^A-Za-z-]+/, '_')
         | 
| 13 | 
            +
                    q = new(Railsquest.quests_path.join(n + ".quest"))
         | 
| 14 | 
            +
                    q.name = n
         | 
| 15 | 
            +
                    q
         | 
| 16 | 
            +
                end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                def self.html_id(name)
         | 
| 19 | 
            +
                  name.gsub(/[^A-Za-z-]+/, '').downcase
         | 
| 20 | 
            +
                end
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                def initialize(path)
         | 
| 23 | 
            +
                  @path = Pathname(path)
         | 
| 24 | 
            +
                end
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                def ==(other)
         | 
| 27 | 
            +
                  other.respond_to?(:path) && self.path == other.path
         | 
| 28 | 
            +
                end
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                attr_reader :path
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                def exist?
         | 
| 33 | 
            +
                  path.exist?
         | 
| 34 | 
            +
                end
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                def init!
         | 
| 37 | 
            +
                  secret = `uuidgen`.strip
         | 
| 38 | 
            +
                  contents =  '{\"secret\" : \"' + secret + '\", \"port\" : ' + port +  '}'
         | 
| 39 | 
            +
                  Dir.chdir(Railsquest.quests_path) { `echo "#{contents}" >> #{path}` }
         | 
| 40 | 
            +
                end
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                def uri
         | 
| 43 | 
            +
                  Railsquest.quest_uri.gsub(/\/$/, '') + ':' + File.open(path) { |f| JSON.parse(f.gets)["port"].to_s }
         | 
| 44 | 
            +
                end
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                def secret
         | 
| 47 | 
            +
                  File.open(path) { |f| JSON.parse(f.gets)["secret"] }
         | 
| 48 | 
            +
                end
         | 
| 49 | 
            +
             | 
| 50 | 
            +
                def name
         | 
| 51 | 
            +
                  dirname.sub(".quest",'')
         | 
| 52 | 
            +
                end
         | 
| 53 | 
            +
             | 
| 54 | 
            +
                def html_id
         | 
| 55 | 
            +
                  self.class.html_id(name)
         | 
| 56 | 
            +
                end
         | 
| 57 | 
            +
             | 
| 58 | 
            +
                def dirname
         | 
| 59 | 
            +
                  path.split.last.to_s
         | 
| 60 | 
            +
                end
         | 
| 61 | 
            +
             | 
| 62 | 
            +
                def to_s
         | 
| 63 | 
            +
                  name
         | 
| 64 | 
            +
                end
         | 
| 65 | 
            +
             | 
| 66 | 
            +
                def web_uri
         | 
| 67 | 
            +
                  Railsquest.web_uri + "#" + html_id
         | 
| 68 | 
            +
                end
         | 
| 69 | 
            +
             | 
| 70 | 
            +
                def attempts
         | 
| 71 | 
            +
                    
         | 
| 72 | 
            +
                end
         | 
| 73 | 
            +
             | 
| 74 | 
            +
                def remove!
         | 
| 75 | 
            +
                  path.rmtree
         | 
| 76 | 
            +
                end
         | 
| 77 | 
            +
             | 
| 78 | 
            +
                def to_hash
         | 
| 79 | 
            +
                  {
         | 
| 80 | 
            +
                    "name" => name,
         | 
| 81 | 
            +
                    "uri" => uri,
         | 
| 82 | 
            +
                    "host_name" => Railsquest.host_name
         | 
| 83 | 
            +
                  }
         | 
| 84 | 
            +
                end
         | 
| 85 | 
            +
              end
         | 
| 86 | 
            +
            end
         |