Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Add initial documentation

  • Loading branch information...
commit 42a54ac81b5e02a9b6e5503a67df586e45140f27 1 parent 78a6c7a
@pusewicz pusewicz authored
View
2  .gitignore
@@ -5,3 +5,5 @@ tmp/**/*
.DS_Store
*~
example/config.yml
+.yardoc/
+doc/
View
7 Gemfile
@@ -21,8 +21,9 @@ end
#gem "cucumber"
#end
-#group :development do
+group :development do
+ gem "yard"
#gem "ruby_core_source"
- #gem "jeweler"
+ gem "jeweler"
#gem "ruby-debug19"
-#end
+end
View
31 Rakefile
@@ -30,19 +30,26 @@ rescue LoadError
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
end
-require 'spec/rake/spectask'
-Spec::Rake::SpecTask.new(:spec) do |spec|
- spec.libs << 'lib' << 'spec'
- spec.spec_files = FileList['spec/**/*_spec.rb']
-end
+begin
+ require 'spec/rake/spectask'
+ Spec::Rake::SpecTask.new(:spec) do |spec|
+ spec.libs << 'lib' << 'spec'
+ spec.spec_files = FileList['spec/**/*_spec.rb']
+ end
-Spec::Rake::SpecTask.new(:rcov) do |spec|
- spec.libs << 'lib' << 'spec'
- spec.pattern = 'spec/**/*_spec.rb'
- spec.rcov = true
-end
+ Spec::Rake::SpecTask.new(:rcov) do |spec|
+ spec.libs << 'lib' << 'spec'
+ spec.pattern = 'spec/**/*_spec.rb'
+ spec.rcov = true
+ end
-task :spec => :check_dependencies
+ task :spec => :check_dependencies
+ task :default => :spec
+rescue LoadError
+ task :spec do
+ abort "RSpec is not available. In order to run rspec, you must: sudo gem install rspec"
+ end
+end
begin
require 'cucumber/rake/task'
@@ -55,8 +62,6 @@ rescue LoadError
end
end
-task :default => :spec
-
begin
require 'yard'
YARD::Rake::YardocTask.new
View
7 lib/sonia.rb
@@ -3,6 +3,7 @@
require 'logger'
require 'active_support'
+# @author Piotr Usewicz
module Sonia
autoload :Server, 'sonia/server'
autoload :Widget, 'sonia/widget'
@@ -11,10 +12,16 @@ module Sonia
autoload :Config, 'sonia/config'
autoload :Helpers, 'sonia/helpers'
+ # Returns application logger
+ #
+ # @return [Logger]
def self.log
@logger ||= Logger.new(STDOUT)
end
+ # Returns expanded path to the root directory
+ #
+ # @return [String] expanded path to the root directory
def self.root
@@root ||= File.expand_path(File.join(File.dirname(__FILE__), '..'))
end
View
26 lib/sonia/config.rb
@@ -1,20 +1,35 @@
module Sonia
+ # Simple configuration class
+ #
+ # @example
+ # "Config.new({:name => "Piotr"}).piotr" #=> "Piotr"
class Config
+ # @param [Hash] data Hash containing configuration data
def initialize(data={})
@data = {}
update!(data)
end
+ # Updates configuration data
+ #
+ # @param [Hash] data
def update!(data)
data.each do |key, value|
self[key] = value
end
end
+ # Returns configuration value
+ #
+ # @param [#to_sym] key Key of the value
def [](key)
@data[key.to_sym]
end
+ # Allows setting a value
+ #
+ # @param [#to_sym] key Key of the value
+ # @param [Object] value Configuration value
def []=(key, value)
if value.class == Hash
@data[key.to_sym] = Config.new(value)
@@ -23,6 +38,10 @@ def []=(key, value)
end
end
+ # Returns each configuration key - value pair or an iterator
+ #
+ # @yield [key, value]
+ # @param [Enumerator]
def each
if block_given?
@data.each do |k, v|
@@ -33,6 +52,10 @@ def each
end
end
+ # Returns configuration value by missing method name
+ #
+ # @param [Symbol] sym Key name as symbol
+ # @return [Object]
def method_missing(sym, *args)
if sym.to_s =~ /(.+)=$/
self[$1] = args.first
@@ -41,6 +64,9 @@ def method_missing(sym, *args)
end
end
+ # Returns whole configuration data as hash
+ #
+ # @return [Hash]
def to_hash
@data
end
View
6 lib/sonia/helpers.rb
@@ -1,5 +1,8 @@
module Sonia
module Helpers
+ # Returns all available widgets with relative path recognized by the webserver
+ #
+ # @return [Array] Array of relalive widget Javascript paths
def widget_javascripts
Dir[Sonia.root + "/widgets/*/*.js"].map do |file|
widget_name = File.basename(file, ".js")
@@ -7,6 +10,9 @@ def widget_javascripts
end
end
+ # Returns all available widget stylesheets with relative paths recognized by the webserver
+ #
+ # @return [Array] Array of relative widget CSS files
def widget_stylesheets
Dir[Sonia.root + "/widgets/*/*.css"].map do |file|
widget_name = File.basename(file, ".css")
View
29 lib/sonia/server.rb
@@ -18,6 +18,11 @@ def send(data)
end
module Sonia
+ # Main Sonia event loop
+ #
+ # Starts both WebServer and WebSocket server
+ #
+ # @author Piotr Usewicz
module Server
extend self
@@ -27,24 +32,38 @@ module Server
WEBSERVER_HOST = "localhost"
WEBSERVER_PORT = 3000
+ # Starts the server
+ #
+ # @param [Hash] args Startup options
def run!(args, &block)
@start_block = block
configure(args)
serve
end
+ # Returns configuration from the config file
+ #
+ # @return [Config]
def config
@@config
end
+ # Loads the configuration file
+ #
+ # @param [Hash] options
+ # @return [Config]
def configure(options)
@@config = Config.new(YAML.load_file(File.expand_path(options.config)))
end
+ # Returns [Logger] object
+ #
+ # @return [Logger]
def log
Sonia.log
end
+ # Starts main [EventMachine] loop
def serve
EventMachine.run {
initialize_widgets
@@ -54,6 +73,7 @@ def serve
}
end
+ # Goes through configured widgets and initializes them
def initialize_widgets
@widgets = []
@@ -64,11 +84,14 @@ def initialize_widgets
end
end
+ # Returns websocket configuration options
+ #
+ # @return [Hash] Websocket's host and port number
def websocket_options
{ :host => WEBSOCKET_HOST, :port => WEBSOCKET_PORT }
end
-
+ # Starts WebSocket server
def start_web_socket_server
EventMachine::WebSocket.start(websocket_options) do |ws|
ws.onopen {
@@ -101,10 +124,14 @@ def start_web_socket_server
log.info("Server") { "WebSocket Server running at #{websocket_options[:host]}:#{websocket_options[:port]}" }
end
+ # Starts Thin WebServer
def start_web_server
Thin::Server.start(WEBSERVER_HOST, WEBSERVER_PORT, ::Sonia::WebServer)
end
+ # Returns WebServer URL
+ #
+ # @return [String] WebServer URL
def webserver_url
"http://#{WEBSERVER_HOST}:#{WEBSERVER_PORT}"
end
View
5 lib/sonia/web_server.rb
@@ -2,6 +2,11 @@
require 'haml'
module Sonia
+ # Sonia's Web Server
+ #
+ # Allows dynamically generating HTML pages
+ #
+ # @author Piotr Usewicz
class WebServer < Sinatra::Base
helpers Sonia::Helpers
View
36 lib/sonia/widget.rb
@@ -4,6 +4,7 @@
require "digest/sha1"
module Sonia
+ # @abstract
class Widget
class << self
def inherited(subclass)
@@ -17,6 +18,9 @@ def widgets
attr_reader :widget_id, :channel, :sid, :config, :log
+ # Initalizes the widget
+ #
+ # @param [Hash] config Configuration of the widget from the config file
def initialize(config)
@log = Sonia.log
@channel = EM::Channel.new
@@ -28,22 +32,40 @@ def initialize(config)
].join)
end
+ # Returns JSON encode
+ #
+ # @return [Yajl::Encoder]
def encoder
@encoder ||= Yajl::Encoder.new
end
+ # Returns JSON parser
+ #
+ # @return [Yajl::Parser]
def parser
@decoder ||= Yajl::Parser.new
end
+ # Parses JSON
+ #
+ # @param [String] payload JSON string
+ # @return [Hash] Parsed JSON represented as a hash
def parse_json(payload)
Yajl::Parser.parse(payload)
end
+ # Parses YAML
+ #
+ # @param [String] payload YAML string
+ # @return [Hash] Parsed YAML represented as a hash
def parse_yaml(payload)
YAML.load(payload)
end
+ # Parse XML
+ #
+ # @param [String] payload XML string
+ # @return [Nokogiri::XML::Document] Parsed Nokogiri document
def parse_xml(payload)
Nokogiri(payload)
end
@@ -51,18 +73,26 @@ def parse_xml(payload)
# Used to push initial data after setup
#def initial_push; end
+ # Subscribes a websocket to widget's data channel
+ #
+ # @param [EventMachine::WebSocket] websocket
+ # @return [String] Subscriber ID
def subscribe!(websocket)
@sid = channel.subscribe { |msg| websocket.send msg }
ensure
log.info(widget_name) { "Subscribed #{sid} via #{channel}" }
end
+ # Unsubscribes a subscriber id from data channel
def unsubscribe!
channel.unsubscribe(sid)
ensure
log.info(widget_name) { "Unsubscribed #{sid} via #{channel}" }
end
+ # Pushes data to the channel
+ #
+ # @param [Hash] msg Data which can be JSONified
def push(msg)
payload = {
:payload => msg,
@@ -77,10 +107,16 @@ def push(msg)
log.info(widget_name) { "Pushing #{message.inspect} via #{channel}" }
end
+ # Returns widget name
+ #
+ # @return [String] Name of the widget
def widget_name
self.class.name.split("::").last
end
+ # Initial widget setup data that gets pushed to the client
+ #
+ # @return [Hash] Initial widget information and configuration
def setup
{
:widget => self.widget_name,
View
1  lib/sonia/widgets.rb
@@ -1,6 +1,7 @@
require 'active_support/inflector'
module Sonia
+ # Responsible for just namespacing widgets
module Widgets
Dir[File.join(Sonia.root, "/widgets/*/*.rb")].each do |file|
autoload File.basename(file, '.rb').classify.to_sym, file
Please sign in to comment.
Something went wrong with that request. Please try again.