webmate 0.1.0
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 +19 -0
- data/GUIDE.md +115 -0
- data/Gemfile +10 -0
- data/LICENSE.txt +22 -0
- data/README.md +47 -0
- data/Rakefile +1 -0
- data/bin/webmate +70 -0
- data/lib/webmate/application.rb +138 -0
- data/lib/webmate/config.rb +23 -0
- data/lib/webmate/decorators/base.rb +38 -0
- data/lib/webmate/env.rb +17 -0
- data/lib/webmate/logger.rb +20 -0
- data/lib/webmate/observers/base.rb +24 -0
- data/lib/webmate/presenters/base.rb +69 -0
- data/lib/webmate/presenters/base_presenter.rb +115 -0
- data/lib/webmate/presenters/scoped.rb +30 -0
- data/lib/webmate/responders/abstract.rb +127 -0
- data/lib/webmate/responders/base.rb +21 -0
- data/lib/webmate/responders/callbacks.rb +79 -0
- data/lib/webmate/responders/exceptions.rb +4 -0
- data/lib/webmate/responders/response.rb +36 -0
- data/lib/webmate/responders/templates.rb +65 -0
- data/lib/webmate/route_helpers/route.rb +91 -0
- data/lib/webmate/route_helpers/routes_collection.rb +273 -0
- data/lib/webmate/socket.io/actions/connection.rb +14 -0
- data/lib/webmate/socket.io/actions/handshake.rb +34 -0
- data/lib/webmate/socket.io/packets/ack.rb +5 -0
- data/lib/webmate/socket.io/packets/base.rb +156 -0
- data/lib/webmate/socket.io/packets/connect.rb +5 -0
- data/lib/webmate/socket.io/packets/disconnect.rb +5 -0
- data/lib/webmate/socket.io/packets/error.rb +5 -0
- data/lib/webmate/socket.io/packets/event.rb +5 -0
- data/lib/webmate/socket.io/packets/heartbeat.rb +5 -0
- data/lib/webmate/socket.io/packets/json.rb +5 -0
- data/lib/webmate/socket.io/packets/message.rb +5 -0
- data/lib/webmate/socket.io/packets/noop.rb +5 -0
- data/lib/webmate/support/em_mongoid.rb +53 -0
- data/lib/webmate/version.rb +3 -0
- data/lib/webmate/views/scope.rb +25 -0
- data/lib/webmate/websockets.rb +50 -0
- data/lib/webmate.rb +129 -0
- data/spec/lib/route_helpers/route_spec.rb +41 -0
- data/spec/spec_helper.rb +18 -0
- data/vendor/.DS_Store +0 -0
- data/vendor/assets/.DS_Store +0 -0
- data/vendor/assets/javascripts/.DS_Store +0 -0
- data/vendor/assets/javascripts/webmate/.DS_Store +0 -0
- data/vendor/assets/javascripts/webmate/auth.coffee +63 -0
- data/vendor/assets/javascripts/webmate/backbone_ext/.DS_Store +0 -0
- data/vendor/assets/javascripts/webmate/backbone_ext/resources.coffee +60 -0
- data/vendor/assets/javascripts/webmate/backbone_ext/sync.coffee +131 -0
- data/vendor/assets/javascripts/webmate/client.coffee +133 -0
- data/vendor/assets/javascripts/webmate/init.coffee +7 -0
- data/vendor/assets/javascripts/webmate/libs/.DS_Store +0 -0
- data/vendor/assets/javascripts/webmate/libs/backbone.js +1572 -0
- data/vendor/assets/javascripts/webmate/libs/benchmark.coffee +27 -0
- data/vendor/assets/javascripts/webmate/libs/icanhaz.js +542 -0
- data/vendor/assets/javascripts/webmate/libs/socket.io.js +3871 -0
- data/vendor/assets/javascripts/webmate/libs/underscore.js +1 -0
- data/vendor/assets/javascripts/webmate.js +10 -0
- data/webmate.gemspec +31 -0
- metadata +290 -0
data/.gitignore
ADDED
data/GUIDE.md
ADDED
@@ -0,0 +1,115 @@
|
|
1
|
+
#webmate app skeleton
|
2
|
+
see working example on https://github.com/evgeniypetrov/webmate-app-skeleton
|
3
|
+
|
4
|
+
## Quick start
|
5
|
+
|
6
|
+
to start application, run
|
7
|
+
webmate start
|
8
|
+
application will be available on localhost:3000 url
|
9
|
+
|
10
|
+
to run irb/console
|
11
|
+
webmate console
|
12
|
+
|
13
|
+
type webmate without params to know all options
|
14
|
+
|
15
|
+
show all available routes [ without assets ]
|
16
|
+
rake routes k
|
17
|
+
|
18
|
+
## Tutorial
|
19
|
+
### Skeleton
|
20
|
+
require files
|
21
|
+
|
22
|
+
to create file, add following gem to Gemfile
|
23
|
+
|
24
|
+
Gemfile
|
25
|
+
gem 'webmate'
|
26
|
+
gem 'slim'
|
27
|
+
gem 'sass', group: assets
|
28
|
+
gem 'alphasights-sinatra-sprockets', require: 'sinatra-sprockets', group: assets
|
29
|
+
|
30
|
+
base required files:
|
31
|
+
|
32
|
+
config.ru
|
33
|
+
require './config/environment'
|
34
|
+
if configatron.assets.compile
|
35
|
+
map '/assets' do
|
36
|
+
run Sinatra::Sprockets.environment
|
37
|
+
end
|
38
|
+
end
|
39
|
+
run ExampleApp
|
40
|
+
|
41
|
+
config/config.rb
|
42
|
+
Webmate::Application.configure do |config|
|
43
|
+
# add directory to application load paths
|
44
|
+
#config.app.load_paths << ["app/uploaders"]
|
45
|
+
config.app.cache_classes = true
|
46
|
+
config.assets.compile = false
|
47
|
+
|
48
|
+
config.websockets.namespace = 'api'
|
49
|
+
config.websockets.enabled = true
|
50
|
+
config.websockets.port = 9020
|
51
|
+
end
|
52
|
+
|
53
|
+
Webmate::Application.configure(:development) do |config|
|
54
|
+
config.app.cache_classes = false
|
55
|
+
config.assets.compile = true
|
56
|
+
config.websockets.port = 3503
|
57
|
+
end
|
58
|
+
|
59
|
+
config/application.rb
|
60
|
+
require 'digest/sha1'
|
61
|
+
require 'base64'
|
62
|
+
|
63
|
+
class ExampleApp < Webmate::Application
|
64
|
+
# do other things)
|
65
|
+
end
|
66
|
+
|
67
|
+
config/environment.rb
|
68
|
+
WEBMATE_ROOT = File.expand_path('.')
|
69
|
+
require 'webmate'
|
70
|
+
|
71
|
+
Dir[File.join(WEBMATE_ROOT, 'app', 'routes', '**', '*.rb')].each do |file|
|
72
|
+
require file
|
73
|
+
end
|
74
|
+
|
75
|
+
### Hello world
|
76
|
+
adding route
|
77
|
+
app/routes/facade_routes.rb
|
78
|
+
ExampleApp.define_routes do
|
79
|
+
get '/', to: 'pages#index', transport: [:http]
|
80
|
+
end
|
81
|
+
|
82
|
+
adding responder to this route
|
83
|
+
create files in app/responders
|
84
|
+
base_responder
|
85
|
+
class BaseResponder < Webmate::Responders::Base
|
86
|
+
# Available options
|
87
|
+
# before_filter :do_something
|
88
|
+
|
89
|
+
rescue_from Webmate::Responders::ActionNotFound do
|
90
|
+
render_not_found
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
pages_responder
|
95
|
+
class PagesResponder < BaseResponder
|
96
|
+
include Webmate::Responders::Templates
|
97
|
+
|
98
|
+
def index
|
99
|
+
slim :index, layout: 'application'
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
also, required files
|
104
|
+
app/views/layouts/application.html.slim
|
105
|
+
app/views/pages/index.html.slim
|
106
|
+
|
107
|
+
### Assets
|
108
|
+
assets will be searched in app/assets folder, so place them to
|
109
|
+
app/assets/javascripts
|
110
|
+
app/assets/stylesheets
|
111
|
+
|
112
|
+
### Utilities - rake task, scripts
|
113
|
+
Rakefile
|
114
|
+
require File.expand_path('../config/environment', __FILE__)
|
115
|
+
Webmate::Application.load_tasks
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Iskander Haziev
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
# Webmate
|
2
|
+
|
3
|
+
TODO: Write a gem description
|
4
|
+
|
5
|
+
## Prerequisites
|
6
|
+
git
|
7
|
+
mongo
|
8
|
+
redis
|
9
|
+
|
10
|
+
sinatra 4.2
|
11
|
+
to make this, use sinatra-contrib at least 1.4.0 version
|
12
|
+
Specify in your Gemfile
|
13
|
+
gem 'sinatra-contrib', git: 'git://github.com/sinatra/sinatra-contrib.git'
|
14
|
+
|
15
|
+
## Installation
|
16
|
+
|
17
|
+
Add this line to your application's Gemfile:
|
18
|
+
|
19
|
+
gem 'webmate'
|
20
|
+
|
21
|
+
And then execute:
|
22
|
+
|
23
|
+
$ bundle
|
24
|
+
|
25
|
+
Or install it yourself as:
|
26
|
+
|
27
|
+
$ gem install webmate
|
28
|
+
|
29
|
+
## Usage
|
30
|
+
|
31
|
+
TODO: Write usage instructions here
|
32
|
+
|
33
|
+
start server as
|
34
|
+
webmate server [-p port]
|
35
|
+
|
36
|
+
running interactive console
|
37
|
+
webmate console [environment]
|
38
|
+
or
|
39
|
+
webmate console [-e environment]
|
40
|
+
|
41
|
+
## Contributing
|
42
|
+
|
43
|
+
1. Fork it
|
44
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
45
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
46
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
47
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/bin/webmate
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# -*- encoding: utf-8 -*-
|
3
|
+
|
4
|
+
require 'optparse'
|
5
|
+
require 'ostruct'
|
6
|
+
|
7
|
+
options = OpenStruct.new(
|
8
|
+
port: 3000,
|
9
|
+
environment: 'development',
|
10
|
+
daemon: false,
|
11
|
+
rackup: 'config.ru'
|
12
|
+
)
|
13
|
+
|
14
|
+
opt_parser = OptionParser.new do |opt|
|
15
|
+
opt.banner = "Usage: webmate COMMAND [OPTIONS]"
|
16
|
+
opt.separator ""
|
17
|
+
opt.separator "Commands"
|
18
|
+
opt.separator " server: start server"
|
19
|
+
opt.separator " console [environment]: application console"
|
20
|
+
opt.separator " generate [app_name]: do something useful"
|
21
|
+
opt.separator ""
|
22
|
+
opt.separator "Options"
|
23
|
+
|
24
|
+
opt.on("-e","--environment ENVIRONMENT","which environment you want to run") do |environment|
|
25
|
+
options.environment = environment
|
26
|
+
end
|
27
|
+
|
28
|
+
opt.on("-d","--daemon","daemon mode") do |daemon|
|
29
|
+
options.daemon = daemon
|
30
|
+
end
|
31
|
+
|
32
|
+
opt.on("-p","--port PORT", Integer, "port number") do |port|
|
33
|
+
options.port = port
|
34
|
+
end
|
35
|
+
|
36
|
+
opt.on("-R","--rackup FILE", "rackup file") do |file|
|
37
|
+
options.rackup_file = file
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
opt_parser.parse!
|
42
|
+
|
43
|
+
command = ARGV.shift
|
44
|
+
case command
|
45
|
+
when 'server'
|
46
|
+
#TODO refactor to
|
47
|
+
# require 'lib/commands/server'
|
48
|
+
# with options
|
49
|
+
require './config/environment'
|
50
|
+
require 'thin'
|
51
|
+
|
52
|
+
cmd = ["start"]
|
53
|
+
cmd += ["-p", options.port.to_s]
|
54
|
+
|
55
|
+
Thin::Runner.new(cmd).run!
|
56
|
+
when 'console'
|
57
|
+
require 'irb'
|
58
|
+
ENV["RACK_ENV"] ||= ARGV.first || options.environment || 'development'
|
59
|
+
puts "Running Webmate console with env: #{ENV["RACK_ENV"]}"
|
60
|
+
require './config/environment'
|
61
|
+
ARGV.clear
|
62
|
+
IRB.start
|
63
|
+
|
64
|
+
when 'generate'
|
65
|
+
puts 'not yet implemented'
|
66
|
+
puts opt_parser
|
67
|
+
|
68
|
+
else
|
69
|
+
puts opt_parser
|
70
|
+
end
|
@@ -0,0 +1,138 @@
|
|
1
|
+
module Webmate
|
2
|
+
class Application < Sinatra::Base
|
3
|
+
# override sinatra's method
|
4
|
+
def route!(base = settings, pass_block = nil)
|
5
|
+
transport = @request.websocket? ? 'WS' : 'HTTP'
|
6
|
+
|
7
|
+
route_info = base.routes.match(@request.request_method, transport, @request.path)
|
8
|
+
|
9
|
+
# no route case - use default sinatra's processors
|
10
|
+
if !route_info
|
11
|
+
route_eval(&pass_block) if pass_block
|
12
|
+
route_missing
|
13
|
+
end
|
14
|
+
|
15
|
+
if @request.websocket?
|
16
|
+
unless authorized_to_open_connection?(route_info[:params][:scope])
|
17
|
+
return [401, {}, []]
|
18
|
+
end
|
19
|
+
|
20
|
+
session_id = route_info[:params][:session_id].inspect
|
21
|
+
Webmate::Websockets.subscribe(session_id, @request) do |message|
|
22
|
+
if route_info = base.routes.match(message['method'], 'WS', message.path)
|
23
|
+
request_info = {
|
24
|
+
path: message.path,
|
25
|
+
metadata: message.metadata || {},
|
26
|
+
action: route_info[:action],
|
27
|
+
params: message.params.merge(route_info[:params]),
|
28
|
+
request: @request
|
29
|
+
}
|
30
|
+
# here we should create subscriber who can live
|
31
|
+
# between messages.. but not between requests.
|
32
|
+
response = route_info[:responder].new(request_info).respond
|
33
|
+
|
34
|
+
# result of block will be sent back to user
|
35
|
+
response
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# this response not pass to user - so we keep connection alive.
|
40
|
+
# passing other response will close connection and socket
|
41
|
+
non_pass_response = [-1, {}, []]
|
42
|
+
return non_pass_response
|
43
|
+
|
44
|
+
else # HTTP
|
45
|
+
# this should return correct Rack response..
|
46
|
+
request_info = params_for_responder(route_info)
|
47
|
+
response = route_info[:responder].new(request_info).respond
|
48
|
+
|
49
|
+
return response.rack_format
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# this method prepare data for responder
|
54
|
+
# {
|
55
|
+
# path: '/',
|
56
|
+
# metadata: {},
|
57
|
+
# action: 'index',
|
58
|
+
# params: { test: true }
|
59
|
+
# }
|
60
|
+
def params_for_responder(route_info)
|
61
|
+
# create unified request info
|
62
|
+
# request_info = { path: '/', metadata: {}, action: 'index', params: { test: true } }
|
63
|
+
request_params = parsed_request_params
|
64
|
+
metadata = request_params.delete(:metadata)
|
65
|
+
{
|
66
|
+
path: @request.path,
|
67
|
+
metadata: metadata || {},
|
68
|
+
action: route_info[:action],
|
69
|
+
params: request_params.merge(route_info[:params]),
|
70
|
+
request: @request
|
71
|
+
}
|
72
|
+
end
|
73
|
+
|
74
|
+
# @request.params working only for get params
|
75
|
+
# and params in url line ?key=value
|
76
|
+
def parsed_request_params
|
77
|
+
request_params = HashWithIndifferentAccess.new
|
78
|
+
request_params.merge!(@request.params || {})
|
79
|
+
|
80
|
+
# read post or put params. this will erase params
|
81
|
+
# {code: 123, mode: 123}
|
82
|
+
# "code=123&mode=123"
|
83
|
+
request_body = @request.body.read
|
84
|
+
if request_body.present?
|
85
|
+
body_params = begin
|
86
|
+
JSON.parse(request_body) # {code: 123, mode: 123}
|
87
|
+
rescue JSON::ParserError
|
88
|
+
Rack::Utils.parse_nested_query(request_body) # "code=123&mode=123"
|
89
|
+
end
|
90
|
+
else
|
91
|
+
body_params = {}
|
92
|
+
end
|
93
|
+
|
94
|
+
request_params.merge(body_params)
|
95
|
+
end
|
96
|
+
|
97
|
+
# update this method to create auth restrictions
|
98
|
+
def authorized_to_open_connection?(scope = :user)
|
99
|
+
return true
|
100
|
+
end
|
101
|
+
|
102
|
+
class << self
|
103
|
+
def configure(env = nil, &block)
|
104
|
+
if !env || Webmate.env?(env)
|
105
|
+
block.call(configatron)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def define_routes(&block)
|
110
|
+
settings = Webmate::Application
|
111
|
+
unless settings.routes.is_a?(RoutesCollection)
|
112
|
+
routes = RoutesCollection.new()
|
113
|
+
settings.set(:routes, routes)
|
114
|
+
end
|
115
|
+
settings.routes.define_routes(&block)
|
116
|
+
|
117
|
+
routes
|
118
|
+
end
|
119
|
+
|
120
|
+
def get_channel_name_for(user_id)
|
121
|
+
channel_name = "some-unique-key-for-app-#{user_id}"
|
122
|
+
end
|
123
|
+
|
124
|
+
def dump(obj)
|
125
|
+
Yajl::Encoder.encode(obj)
|
126
|
+
end
|
127
|
+
|
128
|
+
def restore(str)
|
129
|
+
Yajl::Parser.parse(str)
|
130
|
+
end
|
131
|
+
|
132
|
+
def load_tasks
|
133
|
+
file_path = Pathname.new(__FILE__)
|
134
|
+
load File.join(file_path.dirname, "../../tasks/routes.rake")
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
Webmate::Application.configure do |config|
|
2
|
+
config.app.load_paths = ["app/responders", "app/models", "app/services", "app/observers", "app/decorators"]
|
3
|
+
config.app.cache_classes = false
|
4
|
+
|
5
|
+
config.app.name = 'webmate'
|
6
|
+
config.app.host = 'localhost'
|
7
|
+
config.app.port = 80
|
8
|
+
config.app.host_with_port = Configatron::Delayed.new { "#{configatron.app.host}:#{configatron.app.port}" }
|
9
|
+
|
10
|
+
config.logger.path = "#{Webmate.root}/log"
|
11
|
+
|
12
|
+
config.assets.debug = false
|
13
|
+
config.assets.compress = false
|
14
|
+
config.assets.compile = true
|
15
|
+
config.assets.digest = false
|
16
|
+
|
17
|
+
config.cookies.key = Configatron::Delayed.new { "_#{configatron.app.name}_session" }
|
18
|
+
config.cookies.domain = nil
|
19
|
+
config.cookies.secret = "65e604cae451847ff2722ba84cb13db90f1b0a9ddc35a37169bec"
|
20
|
+
|
21
|
+
config.websockets.enabled = true
|
22
|
+
config.websockets.port = 80
|
23
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Webmate::Decorators
|
2
|
+
class Base
|
3
|
+
attr_reader :entity, :options
|
4
|
+
|
5
|
+
def initialize(entity, options = {})
|
6
|
+
@entity, @options = entity, options
|
7
|
+
end
|
8
|
+
|
9
|
+
class << self
|
10
|
+
def decorate(entity_or_collection, options = {})
|
11
|
+
if entity_or_collection.respond_to?(:map)
|
12
|
+
entity_or_collection.map { |e| self.decorate(e, options) }
|
13
|
+
else
|
14
|
+
self.new(entity_or_collection, options)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def id
|
20
|
+
entity.id.to_s
|
21
|
+
end
|
22
|
+
|
23
|
+
def method_missing(method, *args, &block)
|
24
|
+
if entity.respond_to?(method)
|
25
|
+
self.class.send :define_method, method do |*args, &blokk|
|
26
|
+
entity.send method, *args, &blokk
|
27
|
+
end
|
28
|
+
|
29
|
+
send method, *args, &block
|
30
|
+
else
|
31
|
+
super
|
32
|
+
end
|
33
|
+
rescue NoMethodError => no_method_error
|
34
|
+
super if no_method_error.name == method
|
35
|
+
raise no_method_error
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
data/lib/webmate/env.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
module Webmate
|
2
|
+
class Logger < Rack::CommonLogger
|
3
|
+
cattr_accessor :logger_file
|
4
|
+
def initialize(app = nil)
|
5
|
+
@app = app
|
6
|
+
Dir.mkdir(configatron.logger.path) unless File.exists?(configatron.logger.path)
|
7
|
+
@@logger_file ||= File.new(File.join(configatron.logger.path, "#{Webmate.env}.log"), 'a')
|
8
|
+
end
|
9
|
+
|
10
|
+
def log(env, status, header, began_at)
|
11
|
+
dump(%Q{HTTP #{env["REQUEST_METHOD"]}: #{env["PATH_INFO"]} #{status} \nParams: #{env['rack.request.query_hash']}})
|
12
|
+
end
|
13
|
+
|
14
|
+
def dump(text)
|
15
|
+
[@@logger_file, STDOUT].each do |out|
|
16
|
+
out.write %Q{[#{Time.now.strftime("%D %H:%M:%S")}] #{text} \n\n}
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Webmate::Observers
|
2
|
+
class Base
|
3
|
+
class << self
|
4
|
+
cattr_accessor :subscriptions
|
5
|
+
|
6
|
+
def subscribe(action, &block)
|
7
|
+
Webmate::Observers::Base.subscribe!(action, &block)
|
8
|
+
end
|
9
|
+
|
10
|
+
def subscribe!(action, &block)
|
11
|
+
self.subscriptions ||= {}
|
12
|
+
self.subscriptions[action] ||= []
|
13
|
+
self.subscriptions[action] << block
|
14
|
+
end
|
15
|
+
|
16
|
+
def execute_all(action, data)
|
17
|
+
self.subscriptions ||= {}
|
18
|
+
(self.subscriptions[action] || []).each do |block|
|
19
|
+
block.call(data)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
module Webmate::Presenters
|
2
|
+
class Base
|
3
|
+
attr_reader :attrs
|
4
|
+
|
5
|
+
def initialize(object, options = {})
|
6
|
+
@object, @options = object, options.with_indifferent_access
|
7
|
+
@attrs = {}
|
8
|
+
end
|
9
|
+
|
10
|
+
def namespace(name, &block)
|
11
|
+
serializer = self.class.new(@object, @options)
|
12
|
+
serializer.instance_exec(@object, &block)
|
13
|
+
self.attrs[name] = serializer.attrs
|
14
|
+
end
|
15
|
+
|
16
|
+
def resource(name, object = nil, &block)
|
17
|
+
raise "You should set name for resource" if name.blank?
|
18
|
+
raise "You should specify object" if @object.nil? && object.nil?
|
19
|
+
nested_name = name.to_s
|
20
|
+
nested_object = object || @object.send(nested_name)
|
21
|
+
if nested_object.blank?
|
22
|
+
self.attrs[nested_name] = {}
|
23
|
+
else
|
24
|
+
self.attrs[nested_name] = nested_resource(nested_name, nested_object, @options, &block)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def resources(name, objects = nil, &block)
|
29
|
+
raise "You should specify object" if @object.nil? && objects.nil?
|
30
|
+
objects = objects.flatten unless objects.nil?
|
31
|
+
nested_objects = objects || @object.send(name.to_s)
|
32
|
+
if nested_objects.blank?
|
33
|
+
self.attrs[name.to_s] = []
|
34
|
+
else
|
35
|
+
self.attrs[name.to_s] = (nested_objects || []).inject([]) do |result, obj|
|
36
|
+
resource = nested_resource(name, obj, @options, &block)
|
37
|
+
resource.empty? ? result : (result << resource)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def attributes(*attrs, &block)
|
43
|
+
if @object.blank?
|
44
|
+
object = attrs.last
|
45
|
+
attrs.delete(attrs.last)
|
46
|
+
raise ArgumentError, "Object was not specified" if object.is_a?(Symbol)
|
47
|
+
end
|
48
|
+
|
49
|
+
target = object || @object
|
50
|
+
Array.wrap(attrs).flatten.each do |attribute|
|
51
|
+
self.attrs[attribute.to_s] = target.send(attribute.to_s)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def attribute(attr, &block)
|
56
|
+
self.attrs[attr.to_s] = yield
|
57
|
+
end
|
58
|
+
|
59
|
+
protected
|
60
|
+
|
61
|
+
def nested_resource(name, object, options, &block)
|
62
|
+
return nil if !object || object.blank?
|
63
|
+
serializer = self.class.new(object, options)
|
64
|
+
serializer.instance_exec(object, &block)
|
65
|
+
serializer.attrs
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
end
|