serverside 0.3.1 → 0.4.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.
@@ -0,0 +1,173 @@
1
+ require 'metaid'
2
+ require 'json'
3
+
4
+ module ServerSide
5
+ # Serializes data into a Javscript literal hash format. For example:
6
+ # ServerSide::JS.new {|j| j}
7
+ class JS
8
+ # blank slate
9
+ instance_methods.each do |m|
10
+ undef_method m unless (m =~ /^__|instance_eval|meta|respond_to|nil|is_a/)
11
+ end
12
+
13
+ # Initializes a new document. A callback function name can be supplied to
14
+ # wrap the hash.
15
+ def initialize(callback = nil, &block)
16
+ @callback = callback
17
+ @stack = [self]
18
+ block.call(self) if block
19
+ end
20
+
21
+ # Catches calls to define keys and creates methods on the fly.
22
+ def method_missing(key, *args, &block)
23
+ value = nil
24
+ if block
25
+ @stack.push JS.new
26
+ block.call(self)
27
+ value = @stack.pop.__content
28
+ else
29
+ value = args.first
30
+ end
31
+ @stack.last.__add_hash_value(key, value)
32
+ self
33
+ end
34
+
35
+ def __add_hash_value(key, value)
36
+ @content ||= {}
37
+ @content[key] = value
38
+ end
39
+
40
+ def __add_array_value(value)
41
+ @content ||= []
42
+ @content << value
43
+ end
44
+
45
+ def <<(value)
46
+ value = value.__content if value.respond_to?(:__content)
47
+ @stack.last.__add_array_value(value)
48
+ end
49
+
50
+ # Returns the current document content.
51
+ def __content
52
+ @content
53
+ end
54
+
55
+ NULL = 'null'.freeze
56
+
57
+ # Serializes the specified object into JS/JSON format.
58
+ def __serialize(obj, quote_keys)
59
+ case obj
60
+ when Time: JSON.generate(obj.to_f)
61
+ else
62
+ JSON.generate(obj)
63
+ end
64
+ end
65
+
66
+ # Returns the document content as a literal Javascript object. If a callback was specified,
67
+ # the object is wrapped in a Javascript function call.
68
+ def to_s
69
+ j = __serialize(@content, false)
70
+ @callback ? "#{@callback}(#{j});" : j
71
+ end
72
+
73
+ # Returns the content in JSON format.
74
+ def to_json
75
+ __serialize(@content, true)
76
+ end
77
+
78
+ alias_method :inspect, :to_s
79
+ end
80
+ end
81
+
82
+ __END__
83
+
84
+ module ServerSide
85
+ # Serializes data into a Javscript literal hash format. For example:
86
+ # ServerSide::JS.new {|j| j}
87
+ class JS
88
+ # blank slate
89
+ instance_methods.each do |m|
90
+ undef_method m unless (m =~ /^__|instance_eval|meta|respond_to|nil|is_a/)
91
+ end
92
+
93
+ # Initializes a new document. A callback function name can be supplied to
94
+ # wrap the hash.
95
+ def initialize(callback = nil, &block)
96
+ @callback = callback
97
+ @stack = [self]
98
+ block.call(self) if block
99
+ end
100
+
101
+ # Catches calls to define keys and creates methods on the fly.
102
+ def method_missing(key, *args, &block)
103
+ value = nil
104
+ if block
105
+ @stack.push JS.new
106
+ block.call(self)
107
+ value = @stack.pop.__content
108
+ else
109
+ value = args.first
110
+ end
111
+ @stack.last.__add_hash_value(key, value)
112
+ self
113
+ end
114
+
115
+ def __add_hash_value(key, value)
116
+ @content ||= {}
117
+ @content[key] = value
118
+ end
119
+
120
+ def __add_array_value(value)
121
+ @content ||= []
122
+ @content << value
123
+ end
124
+
125
+ def <<(value)
126
+ value = value.__content if value.respond_to?(:__js)
127
+ @stack.last.__add_array_value(value)
128
+ end
129
+
130
+ # Returns the current document content.
131
+ def __content
132
+ @content
133
+ end
134
+
135
+ NULL = 'null'.freeze
136
+
137
+ # Serializes the specified object into JS/JSON format.
138
+ def __serialize(obj, quote_keys)
139
+ if obj.nil?
140
+ NULL
141
+ elsif obj.is_a? Array
142
+ "[#{obj.map{|v| __serialize(v, quote_keys)}.join(', ')}]"
143
+ elsif obj.is_a? Hash
144
+ if quote_keys
145
+ fields = obj.to_a.map{|kv| "\"#{kv[0]}\": #{__serialize(kv[1], quote_keys)}"}
146
+ else
147
+ fields = obj.to_a.map{|kv| "#{kv[0]}: #{__serialize(kv[1], quote_keys)}"}
148
+ end
149
+ "{#{fields.join(', ')}}"
150
+ elsif obj.is_a? Symbol
151
+ obj.to_s
152
+ elsif obj.is_a? Time
153
+ obj.to_f
154
+ else
155
+ obj.inspect
156
+ end
157
+ end
158
+
159
+ # Returns the document content as a literal Javascript object. If a callback was specified,
160
+ # the object is wrapped in a Javascript function call.
161
+ def to_s
162
+ j = __serialize(@content, false)
163
+ @callback ? "#{@callback}(#{j});" : j
164
+ end
165
+
166
+ # Returns the content in JSON format.
167
+ def to_json
168
+ __serialize(@content, true)
169
+ end
170
+
171
+ alias_method :inspect, :to_s
172
+ end
173
+ end
@@ -0,0 +1,79 @@
1
+ require 'logger'
2
+
3
+ module ServerSide
4
+ @@logger = nil
5
+
6
+ def self.logger
7
+ @@logger
8
+ end
9
+
10
+ def self.logger=(l)
11
+ @@logger = l
12
+ end
13
+
14
+ def self.setup_stock_logger(logdev, shift_age = 0, shift_size = 1048576)
15
+ @@logger = Logger.new(logdev, shift_age, shift_size)
16
+ @@logger.datetime_format = "%Y-%m-%d %H:%M:%S"
17
+ @@logger
18
+ end
19
+
20
+ def self.logger_level=(level)
21
+ @@logger.level = level if @@logger
22
+ end
23
+
24
+ def self.log(level, text)
25
+ @@logger.log(level, text) if @@logger
26
+ end
27
+
28
+ def self.debug(text)
29
+ @@logger.debug(text) if @@logger
30
+ end
31
+
32
+ def self.info(text)
33
+ @@logger.info(text) if @@logger
34
+ end
35
+
36
+ def self.warn(text)
37
+ @@logger.warn(text) if @@logger
38
+ end
39
+
40
+ def self.error(text)
41
+ @@logger.error(text) if @@logger
42
+ end
43
+
44
+ def self.fatal(text)
45
+ @@logger.fatal(text) if @@logger
46
+ end
47
+
48
+ def self.log_error(e)
49
+ if @@logger
50
+ @@logger.error("#{e.message}:\r\n" + e.backtrace.join("\r\n"))
51
+ end
52
+ end
53
+
54
+ REFERER = "Referer".freeze
55
+ USER_AGENT = "User-Agent".freeze
56
+ HOST = "Host".freeze
57
+ HOST_REGEXP = /([^\:]*)(\:(.*))?$/
58
+
59
+ def self.log_request(c)
60
+ if @@logger
61
+ return unless request_line = c.request_line
62
+ req_line.chomp!
63
+ if c.method == :post && c.content_type == HTTP::Request::CONTENT_TYPE_URL_ENCODED
64
+ req_line << " (#{r.body})"
65
+ end
66
+ host = (r.request_headers[HOST] || "") =~ HOST_REGEXP ? $1 : ""
67
+ msg = "%s %s %s %s %s %s %s" % [
68
+ r.client_name,
69
+ host.inspect,
70
+ request_line.inspect,
71
+ r.status || '?',
72
+ r.content_length || '?',
73
+ (r.headers[REFERER] || "").inspect,
74
+ (r.headers[USER_AGENT] || "").inspect
75
+ ]
76
+ @@logger.info(msg)
77
+ end
78
+ end
79
+ end
@@ -1,8 +1,9 @@
1
- require 'erb'
1
+ require 'rubygems'
2
+ require 'erubis'
2
3
 
3
4
  module ServerSide
4
- # The Template module implements an ERB template rendering system. Templates
5
- # are cached and automatically reloaded if the file changes.
5
+ # The Template module implements an Erubis template rendering system.
6
+ # Templates are cached and automatically reloaded if the file changes.
6
7
  class Template
7
8
  # The @@templates variable caches templates in use. The values are
8
9
  # arrays containing 2 objects: a file stamp (if the template comes from a
@@ -12,7 +13,7 @@ module ServerSide
12
13
  # Caches a template for later use. The stamp parameter is used only when
13
14
  # the content of a template file is stored.
14
15
  def self.set(name, body, stamp = nil)
15
- @@templates[name] = [stamp, ERB.new(body)]
16
+ @@templates[name] = [stamp, Erubis::Eruby.new(body)]
16
17
  end
17
18
 
18
19
  # Validates the referenced template by checking its stamp. If the name
@@ -0,0 +1,84 @@
1
+ require 'metaid'
2
+
3
+ module ServerSide
4
+ class XML
5
+ # blank slate
6
+ instance_methods.each {|m|
7
+ undef_method m unless (m =~ /^__|instance_eval|meta|respond_to/)}
8
+
9
+ TAG_LEFT_OPEN = '<'.freeze
10
+ TAG_LEFT_CLOSE = '</'.freeze
11
+ TAG_RIGHT = '>'.freeze
12
+
13
+ def __open_tag(tag, atts)
14
+ @doc << TAG_LEFT_OPEN
15
+ @doc << tag.to_s
16
+ @doc << __fmt_atts(atts) if atts
17
+ @doc << TAG_RIGHT
18
+ end
19
+
20
+ def __close_tag(tag)
21
+ @doc << TAG_LEFT_CLOSE
22
+ @doc << tag.to_s
23
+ @doc << TAG_RIGHT
24
+ end
25
+
26
+ def __value(value)
27
+ @doc << value.to_s
28
+ end
29
+
30
+ INSTRUCT_LEFT = '<?xml'.freeze
31
+ INSTRUCT_RIGHT = '?>'.freeze
32
+
33
+ def __instruct(arg)
34
+ @doc << INSTRUCT_LEFT
35
+ @doc << __fmt_atts(arg)
36
+ @doc << INSTRUCT_RIGHT
37
+ end
38
+
39
+ SPACE = ' '.freeze
40
+
41
+ def __fmt_atts(atts)
42
+ atts.inject('') {|m, i| m << " #{i[0]}=#{i[1].to_s.inspect}"}
43
+ end
44
+
45
+
46
+ def initialize(tag = nil, atts = nil, &block)
47
+ @doc = ''
48
+ __open_tag(tag, atts) if tag
49
+ block.call(self) if block
50
+ __close_tag(tag) if tag
51
+ end
52
+
53
+ def method_missing(tag, *args, &block)
54
+ if block
55
+ __open_tag(tag, args.first)
56
+ block.call(self)
57
+ __close_tag(tag)
58
+ else
59
+ value, atts = args.pop, args.pop
60
+ subtags, atts = atts, nil if atts.is_a?(Array)
61
+ if subtags
62
+ __open_tag(tag, atts)
63
+ subtags.each {|k| __send__(k, value[k])}
64
+ __close_tag(tag)
65
+ else
66
+ __open_tag(tag, atts)
67
+ __value(value)
68
+ __close_tag(tag)
69
+ end
70
+ end
71
+ self
72
+ end
73
+
74
+ def instruct!(atts = nil)
75
+ __instruct(atts || {:version => "1.0", :encoding => "UTF-8"})
76
+ end
77
+
78
+ def to_s
79
+ @doc
80
+ end
81
+
82
+ alias_method :inspect, :to_s
83
+ end
84
+ end
data/lib/serverside.rb CHANGED
@@ -9,5 +9,14 @@
9
9
  module ServerSide
10
10
  end
11
11
 
12
- path = File.join(File.dirname(__FILE__), 'serverside')
13
- Dir.foreach(path) {|fn| require File.join(path, fn) if fn =~ /\.rb$/}
12
+ lib_dir = File.join(File.dirname(__FILE__), 'serverside')
13
+
14
+ require File.join(lib_dir, 'core_ext')
15
+ require File.join(lib_dir, 'http')
16
+ require File.join(lib_dir, 'daemon')
17
+ require File.join(lib_dir, 'cluster')
18
+ require File.join(lib_dir, 'log')
19
+ require File.join(lib_dir, 'template')
20
+ require File.join(lib_dir, 'js')
21
+ require File.join(lib_dir, 'xml')
22
+
@@ -21,72 +21,27 @@ context "String" do
21
21
  ('touch'/'/me/'/'hold'/'/me').should == 'touch/me/hold/me'
22
22
  end
23
23
 
24
- specify ".underscore should turn camel-cased phrases to underscored ones" do
24
+ specify "#underscore should turn camel-cased phrases to underscored ones" do
25
25
  'CamelCase'.underscore.should == 'camel_case'
26
26
  'Content-Type'.underscore.should == 'content_type'
27
27
  end
28
- end
29
-
30
- # Symbol extensions
31
-
32
- class Symbol
33
- attr_reader :_to_s
34
- end
35
-
36
- context "Symbol.to_s" do
37
- specify "should convert the symbol to a string" do
38
- :abc_def.to_s.should == 'abc_def'
39
- :def_ghi.to_s.should_be_instance_of String
40
- :ghi_jkl.to_s.should == :ghi_jkl.id2name
41
- end
42
-
43
- specify "should cache the id2name value" do
44
- :kwantz_mit_krantz._to_s.should_be_nil
45
- :kwantz_mit_krantz.to_s
46
- :kwantz_mit_krantz._to_s.should == :kwantz_mit_krantz.id2name
47
- end
48
-
49
- specify "should always return the same cached value" do
50
- :item.to_s.should_be :item.to_s
51
- end
52
- end
53
-
54
- context "Proc.proc_tag" do
55
- setup do
56
- @l1 = lambda {1 + 1}
57
- @l2 = lambda {1 + 1}
58
- end
59
-
60
- specify "should return a unique tag for the proc object" do
61
- @l1.proc_tag.should_not_equal @l2.proc_tag
62
- end
63
-
64
- specify "should return the same tag always" do
65
- @l1.proc_tag.should == @l1.proc_tag
66
- end
67
28
 
68
- specify "should return the object id in base 36 prefixed with 'proc_'" do
69
- @l1.proc_tag.should == 'proc_' + @l1.object_id.to_s(36).sub('-', '_')
29
+ specify "#camelize should turn an underscore name to camelcase" do
30
+ 'wowie_zowie'.camelize.should == 'WowieZowie'
70
31
  end
71
32
  end
72
33
 
73
- context "Object.const_tag" do
74
- setup do
75
- @o1 = Object.new
76
- @o2 = Object.new
77
- end
78
-
79
- specify "should return a unique tag for the object" do
80
- @o1.const_tag.should_not_equal @o2.const_tag
81
- end
82
-
83
- specify "should return the same tag always" do
84
- @o1.const_tag.should == @o1.const_tag
85
- @o2.const_tag.should == @o2.const_tag
34
+ context "Process.exists?" do
35
+ specify "should return false for a non-existing process" do
36
+ # bogus pid
37
+ pid = nil
38
+ while pid = rand(10000)
39
+ break if `ps #{pid}` !~ /#{pid}/
40
+ end
41
+ Process.exists?(pid).should be_false
86
42
  end
87
43
 
88
- specify "should return the object id in base 36 (upcase) prefixed with 'C'" do
89
- @o1.const_tag.should == 'C' + @o1.object_id.to_s(36).upcase.sub('-', '_')
90
- @o2.const_tag.should == 'C' + @o2.object_id.to_s(36).upcase.sub('-', '_')
44
+ specify "should return true for an existing process" do
45
+ Process.exists?(Process.pid).should be_true
91
46
  end
92
47
  end
data/spec/daemon_spec.rb CHANGED
@@ -1,3 +1,5 @@
1
+ __END__
2
+
1
3
  require File.join(File.dirname(__FILE__), '../lib/serverside')
2
4
 
3
5
  class TestDaemon < Daemon::Base
@@ -62,38 +64,69 @@ context "Daemon::PidFile" do
62
64
  end
63
65
 
64
66
  context "Daemon.control" do
65
- # teardown {Daemon.control(TestDaemon, :stop) rescue nil}
67
+ teardown {Daemon.control(TestDaemon, :stop) rescue nil}
66
68
 
67
- # specify "should start and stop the daemon" do
68
- # Daemon::PidFile.remove(TestDaemon)
69
- # Daemon.control(TestDaemon, :start)
70
- # sleep 0.2
71
- # File.file?(TestDaemon.pid_fn).should == true
72
- # sleep 0.5
73
- # proc {Daemon::PidFile.recall(TestDaemon)}.should_not_raise
74
- # Daemon.control(TestDaemon, :stop)
75
- # sleep 0.2
76
- # File.file?(TestDaemon.result_fn).should == false
77
- # end
69
+ specify "should start and stop the daemon" do
70
+ Daemon::PidFile.remove(TestDaemon)
71
+ Daemon.control(TestDaemon, :start)
72
+ sleep 0.2
73
+ File.file?(TestDaemon.pid_fn).should == true
74
+ sleep 0.5
75
+ proc {Daemon::PidFile.recall(TestDaemon)}.should_not_raise
76
+ Daemon.control(TestDaemon, :stop)
77
+ sleep 0.2
78
+ File.file?(TestDaemon.result_fn).should == false
79
+ end
78
80
 
79
- # specify "should restart the daemon" do
80
- # Daemon::PidFile.remove(TestDaemon)
81
- # Daemon.control(TestDaemon, :start)
82
- # begin
83
- # sleep 1
84
- # pid1 = Daemon::PidFile.recall(TestDaemon)
85
- # Daemon.control(TestDaemon, :restart)
86
- # sleep 1
87
- # pid2 = Daemon::PidFile.recall(TestDaemon)
88
- # pid1.should_not == pid2
89
- # ensure
90
- # Daemon.control(TestDaemon, :stop)
91
- # Daemon::PidFile.remove(TestDaemon)
92
- # end
93
- # end
81
+ specify "should restart the daemon" do
82
+ Daemon::PidFile.remove(TestDaemon)
83
+ Daemon.control(TestDaemon, :start)
84
+ begin
85
+ sleep 1
86
+ pid1 = Daemon::PidFile.recall(TestDaemon)
87
+ Daemon.control(TestDaemon, :restart)
88
+ sleep 1
89
+ pid2 = Daemon::PidFile.recall(TestDaemon)
90
+ pid1.should_not == pid2
91
+ ensure
92
+ Daemon.control(TestDaemon, :stop)
93
+ Daemon::PidFile.remove(TestDaemon)
94
+ end
95
+ end
94
96
 
95
97
  specify "should raise RuntimeError for invalid command" do
96
- proc {Daemon.control(TestDaemon, :invalid)}.should_raise RuntimeError
98
+ proc {Daemon.control(TestDaemon, :invalid)}.should_raise
99
+ end
100
+ end
101
+
102
+ context "Daemon.alive?" do
103
+ teardown {Daemon.control(TestDaemon, :stop) rescue nil}
104
+
105
+ specify "should return false if not running" do
106
+ Daemon.alive?(TestDaemon).should_be false
107
+ end
108
+
109
+ specify "should return true if running" do
110
+ Daemon.control(TestDaemon, :start)
111
+ sleep 1
112
+ Daemon.alive?(TestDaemon).should_be true
113
+ sleep 1
114
+ Daemon.control(TestDaemon, :stop)
115
+ sleep 1
116
+ Daemon.alive?(TestDaemon).should_be false
117
+ end
118
+
119
+ specify "should return false if the daemon process is killed" do
120
+ Daemon.control(TestDaemon, :start)
121
+ sleep 1
122
+ pid = Daemon::PidFile.recall(TestDaemon)
123
+ Daemon.alive?(TestDaemon).should_be true
124
+ Process.kill("TERM", pid)
125
+ while `ps #{pid}` =~ /#{pid}/
126
+ sleep 0.2
127
+ end
128
+ Daemon.alive?(TestDaemon).should_be false
129
+ Daemon.control(TestDaemon, :stop)
97
130
  end
98
131
  end
99
132