snarl-snp 0.1.1 → 0.2.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.
@@ -1,11 +1,11 @@
1
1
  require 'socket'
2
-
2
+ require 'yaml'
3
3
  $LOAD_PATH.unshift(File.dirname(File.expand_path(__FILE__)))
4
4
 
5
5
  require 'snp/action'
6
- require 'snp/snp'
6
+ require 'snp/snp_procedure'
7
+ require 'snp/config'
7
8
  require 'snp/request'
8
9
  require 'snp/response'
9
10
  require 'snp/error'
10
- require 'snp/config'
11
- # require 'snp/addon'
11
+ require 'snp/snp'
@@ -13,9 +13,10 @@ class Snarl
13
13
  #
14
14
  # Snarl sends back a casual error when +app+ is already registered.
15
15
  # It is treated as SNP::SNPError::Casual::SNP_ERROR_ALREADY_REGISTERED.
16
- def register(app=nil)
17
- @app = app
18
- cmds = {:action => 'register', :app => app}
16
+ def register(app = nil)
17
+ # when self['app'] == nil/unset and register(nil), SNARL receives SNP_ERROR_BAD_PACKET
18
+ self['app'] = app if app
19
+ cmds = {:action => 'register', :app => self['app']}
19
20
  request(Request.new(cmds))
20
21
  end
21
22
  alias :app= :register
@@ -33,8 +34,8 @@ class Snarl
33
34
  def add_class(classid, classtitle=nil)
34
35
  # TODO: add_class(app=nil, classid, classtitle=nil)
35
36
  # type=SNP#?version=1.0#?action=add_class#?class=t returns (107) Bad Packet
36
- raise "#{self}#register(appname) required before add_class" unless @app
37
- cmds = {:action => 'add_class', :app => @app, :class => classid.to_s, :title => classtitle}
37
+ raise "registering is required. #{self}#register(appname) before #add_class" unless self['app']
38
+ cmds = {:action => 'add_class', :app => self['app'], :class => classid.to_s, :title => classtitle}
38
39
  request(Request.new(cmds))
39
40
  end
40
41
 
@@ -66,7 +67,7 @@ class Snarl
66
67
  # Snarl sends back a casual error when +app+ is not registered.
67
68
  # It is treated as SNP::SNPError::Casual::SNP_ERROR_NOT_REGISTERED.
68
69
  def unregister(app=nil)
69
- app = app || @app
70
+ app = app || self['app']
70
71
  raise "#{self}#unregister requires appname." unless app
71
72
  cmds = {:action => 'unregister', :app => app}
72
73
  request(Request.new(cmds))
@@ -126,11 +127,14 @@ class Snarl
126
127
  res[command] = keyhash_value if keyhash_value
127
128
  end
128
129
  res[:action] = 'notification'
129
- res[:app] = @app if (res[:app].nil? && @app) # default notification
130
- res[:app] = nil if res[:app] == :anonymous # from snp.show_message
131
- res[:title] = (@title || DEFAULT_TITLE) unless res[:title]
132
- res[:timeout] = (@timeout || DEFAULT_TIMEOUT) unless res[:timeout]
133
- res[:icon] = icon(res[:icon]) if res[:icon]
130
+ res[:app] = self['app'] if (res[:app].nil? && self['app']) # default notification
131
+ res[:title] = (res[:title] || self['title'] || DEFAULT_TITLE)
132
+ res[:timeout] = (res[:timeout] || self['timeout'] || DEFAULT_TIMEOUT)
133
+ if res[:icon] then
134
+ res[:icon] = icon(res[:icon])
135
+ elsif self['icon'] then
136
+ res[:icon] = self['icon']
137
+ end
134
138
  return res
135
139
  end
136
140
  end
@@ -0,0 +1,69 @@
1
+ require 'snp'
2
+ class Snarl
3
+ class SNP
4
+ module AutoSNP
5
+ @host=nil
6
+ @port=nil
7
+ @icondir=nil
8
+ @icon_ok=nil
9
+ @icon_fail=nil
10
+ @icon_pending=nil
11
+ @timeout_ok=nil
12
+ @timeout_fail=nil
13
+
14
+ attr_accessor :host, :port
15
+ attr_writer :icondir, :icon_ok, :icon_fail, :icon_pending, :timeout_ok, :timeout_fail
16
+
17
+ def snp
18
+ Snarl::SNP.load(<<YAML)
19
+ host : #{host}
20
+ port : #{port}
21
+ app : Autotest::Snarl
22
+ "class" :
23
+ - ['green', 'test ok']
24
+ - ['red', 'test fail']
25
+ - ['yellow', 'test pending']
26
+ - ['info', 'system message']
27
+ iconset :
28
+ green : #{File.join(icondir, icon_ok)}
29
+ red : #{File.join(icondir, icon_fail)}
30
+ yellow : #{File.join(icondir, icon_pending)}
31
+ YAML
32
+ end
33
+
34
+ # Windows Snarl shortcut's "working folder" (left click property)
35
+ # %HOME%\Application Data\full phat\snarl\styles
36
+ def icondir ; @icondir ||= './' ; end
37
+
38
+ # %HOME%\Application Data\full phat\snarl\styles\ok.png works fine
39
+ def icon_ok ; @icon_ok ||= "ok.png" ; end
40
+ def icon_fail ; @icon_fail ||= "fail.png" ; end
41
+ def icon_pending ; @icon_pending ||= "pending.png" ; end
42
+
43
+ def timeout_ok ; @timeout_ok ||= 5 ; end
44
+ def timeout_fail ; @timeout_fail ||= 10 ; end
45
+
46
+ def timeout(state)
47
+ case state
48
+ when :green then timeout_ok.to_i
49
+ when :red then timeout_fail.to_i
50
+ else 5
51
+ end
52
+ end
53
+
54
+ def classname(state)
55
+ state.to_s
56
+ end
57
+
58
+ def snarl(title, text, state)
59
+ snp.notification(
60
+ :title => title,
61
+ :text => text,
62
+ :icon => state.to_s,
63
+ :timeout => timeout(state),
64
+ :class => classname(state)
65
+ )
66
+ end
67
+ end
68
+ end
69
+ end
@@ -1,31 +1,123 @@
1
-
2
- # when config file exists, require this config
3
1
  class Snarl
4
2
  class SNP
5
3
  class Config
6
4
 
7
5
  DEFAULT_HOST = '127.0.0.1'
8
6
  DEFAULT_PORT = 9887
9
- @host = nil
10
- @port = nil
11
7
 
12
- def self.host
13
- @host || ENV['SNARL_HOST'] || DEFAULT_HOST
14
- end
15
- def self.host=(v)
16
- @host = if v then v.to_s else nil end
8
+ def initialize
9
+ @config = {}
10
+ default_config
17
11
  end
18
12
 
19
- def self.port
20
- @port || ENV['SNARL_PORT'] || DEFAULT_PORT
21
- end
22
- def self.port=(v)
23
- @port = if v then v.to_i else nil end
13
+ attr_reader :config # mainly for debug
14
+
15
+ def default_config
16
+ self['host'] = if $SAFE > 0 then DEFAULT_HOST else ENV['SNARL_HOST'] || DEFAULT_HOST end
17
+ self['port'] = if $SAFE > 0 then DEFAULT_PORT else ENV['SNARL_PORT'] || DEFAULT_PORT end
24
18
  end
19
+ private :default_config
20
+
21
+ def []=(k, v) ; Normalize.store(k, v, @config) ; end
22
+ def [](k) ; @config[k.to_s] ; end
23
+ def reset ; @config = {} ; end
24
+ def to_yaml ; @config.to_yaml ; end
25
+
26
+ class Normalize
27
+
28
+ def self.store(k, v, hash)
29
+ nkey, nvalue = new(k, v).normalize
30
+ hash.store(nkey, nvalue) if nkey && nvalue != nil
31
+ end
32
+
33
+ def initialize(k, v)
34
+ @key, @value = k, v
35
+ end
36
+
37
+ def normalize
38
+ # NOTE: [xxx, nil] is not set to @config
39
+ # TODO : [k, v] should be equal to {k ,v}
40
+ v = @value
41
+ case @key.to_s
42
+ when 'host' then
43
+ ['host', if is_yaml_undef?(v) then nil else v end]
44
+ when 'port' then
45
+ # {:port => '9887'} is {'port' => 9887}
46
+ ['port', if is_yaml_undef?(v) then nil else v.to_i end]
47
+ when 'app', 'application', 'name', 'register' then
48
+ ['app', if is_yaml_undef?(v) then nil else v end]
49
+ when 'class', 'add_class', 'classes' then
50
+ # {:class => 'cls'} and {'add_class' => {'cls' => nil}} are {'class' => ['cls', nil]}
51
+ ['class', extract_classes(v)]
52
+ when 'title' then
53
+ ['title', if is_yaml_undef?(v) then nil else v end]
54
+ when 'text', 'body', 'msg' then
55
+ ['text', if is_yaml_undef?(v) then nil else v end]
56
+ when 'timeout', 'duration', 'sec' then
57
+ ['timeout', if is_yaml_undef?(v) then 0 else v.to_i end] # "timeout: nil" is sticky
58
+ when 'sticky' then
59
+ ['timeout', if is_yaml_false?(v) then nil else 0 end]
60
+ when 'icon' then # NOTE: is "this notification icon", not "iconset setting"
61
+ ['icon', if is_yaml_undef?(v) then nil else v end]
62
+ when 'notification', 'notify', 'message' then
63
+ ['notification', if is_yaml_undef?(v) then nil else v end]
64
+ when 'unregister' then
65
+ ['unregister', if is_yaml_false?(v) then false else true end]
66
+ when 'iconset' then
67
+ ['iconset', if is_yaml_undef?(v) then nil else v end]
68
+
69
+ when 'log', 'logger' then # useless on yaml?
70
+ ['logger', v]
71
+ when 'logfile' then
72
+ ['logfile', extract_logfile(v)]
73
+ when 'loglevel', 'logger.level', 'log_level' then
74
+ ['loglevel', extract_loglevel(v)]
75
+
76
+ when 'config' then
77
+ ['config', if is_yaml_undef?(v) then nil else v end]
78
+ when 'yaml' then
79
+ ['yaml', if is_yaml_undef?(v) then nil else v end]
80
+
81
+ else [@key.to_s, v] # or raise?
82
+ end
83
+ end
84
+
85
+ private
86
+
87
+ def is_yaml_undef?(v)
88
+ v == 'nil' || v == nil || v == ':nil' || v == :nil || v == '' || v == [] || v == {}
89
+ end
90
+ def is_yaml_false?(v)
91
+ is_yaml_undef?(v) || v == false || v == 'false' || v == :false || v == ':false'
92
+ end
93
+
94
+ def extract_classes(v)
95
+ if v.kind_of?(Array) then
96
+ res = []
97
+ v.each do |a|
98
+ pair = [a].flatten
99
+ pair.push(nil) if pair.size == 1
100
+ res << pair
101
+ end
102
+ return res
103
+ elsif v.kind_of?(String)
104
+ return [[v, nil]]
105
+ else
106
+ return nil
107
+ end
108
+ end
109
+
110
+ def extract_logfile(v)
111
+ if is_yaml_undef?(v) then nil else {'$stdout' => $stdout, '$stderr' => $stderr}[v.to_s] || v end
112
+ end
25
113
 
26
- def self.reset
27
- self.host = nil
28
- self.port = nil
114
+ def extract_loglevel(v)
115
+ if is_yaml_undef?(v) then
116
+ return 0
117
+ else
118
+ return /\d/ =~ v.to_s ? v.to_s.to_i : (%w(DEBUG INFO WARN ERROR FATAL).index(v.to_s.upcase) || 0)
119
+ end
120
+ end
29
121
  end
30
122
  end
31
123
  end
@@ -16,6 +16,9 @@ class Snarl
16
16
  class SNP_OK < Casual ; end
17
17
  # (0) OK
18
18
 
19
+ class SNP_ERROR_NOT_RUNNING < Casual ; end
20
+ # (201) Incoming network notification handling has been disabled by the user.
21
+
19
22
  class SNP_ERROR_NOT_REGISTERED < Casual ; end
20
23
  # (202) The application hasn't been registered.
21
24
 
@@ -25,11 +28,12 @@ class Snarl
25
28
  class SNP_ERROR_CLASS_ALREADY_EXISTS < Casual ; end
26
29
  # (204) Class is already registered.
27
30
 
28
- class Fatal < SNPError ; end
29
-
30
- class SNP_ERROR_FAILED < Fatal ; end
31
+ # "Do not Disturb" often raises this error...
32
+ class SNP_ERROR_FAILED < Casual ; end #Fatal ; end
31
33
  # (101) An internal error occurred - usually this represents a fault within Snarl itself.
32
34
 
35
+ class Fatal < SNPError ; end
36
+
33
37
  class SNP_ERROR_UNKNOWN_COMMAND < Fatal ; end
34
38
  # (102) An unknown action was specified.
35
39
 
@@ -39,9 +43,6 @@ class Snarl
39
43
  class SNP_ERROR_BAD_PACKET < Fatal ; end
40
44
  # (107) The command packet is wrongly formed.
41
45
 
42
- class SNP_ERROR_NOT_RUNNING < Fatal ; end
43
- # (201) Incoming network notification handling has been disabled by the user.
44
-
45
46
  class RUBYSNARL_UNKNOWN_RESPONSE < Fatal ; end
46
47
  # (???) Snarl returns unknown return code.
47
48
 
@@ -23,60 +23,70 @@ class Snarl
23
23
  # make SNP request string from command hash and is Request object.
24
24
  def initialize(cmd_hash={})
25
25
  @commands = {}.update(cmd_hash)
26
+ normalize unless @commands.empty?
26
27
  end
27
28
 
28
29
  attr_reader :commands
29
30
 
30
31
  # Adds command key and value to Request
31
- def []=(cmdkey, value) ; @commands[cmdkey] = value ; end # TODO: normalize
32
+ def []=(cmdkey, value)
33
+ norm_cmdkey = normalize_cmdkey(cmdkey)
34
+ if norm_cmdkey == 'action' then
35
+ @commands['action'] = normalize_action_value(value)
36
+ else
37
+ @commands[norm_cmdkey] = normalize_value(value)
38
+ end
39
+ end
32
40
  # Returns command value for command key
33
- def [](cmdkey) ; @commands[cmdkey] ; end
41
+ def [](cmdkey) ; @commands[cmdkey] ; end
34
42
 
35
43
  # Returns Request query string with SNP_TERMINAL_STRING "\r\n"
36
44
  def to_str ; query + SNP_TERMINAL_STRING ; end # FIXME: include "\r\n"?
37
45
  alias :to_s :to_str
38
46
  # Returns Request query string. has no SNP_TERMINAL_STRING "\r\n".
39
- def inspect ; query ; end
47
+ def inspect ; query.inspect ; end
40
48
  def action ; @commands['action'] ; end
41
49
 
42
50
  private
43
51
 
44
- def order_command_pair
45
- action = @commands['action']
52
+ def align_command_pair
46
53
  @commands.to_a.sort_by{|pair| SNP_ACTIONS[action].index(pair[0])}
47
54
  end
48
55
 
49
56
  def query
50
- normalize
51
- order_command_pair.map{|pair| pair.join('=')}.join(SNP_SEPARATOR)
57
+ # @commands is already normalized
58
+ align_command_pair.map{|pair| pair.join('=')}.join(SNP_SEPARATOR)
52
59
  end
53
60
 
54
- # normalize item pairs
61
+ # normalize command item pairs {cmdkey => value}
55
62
  # - symbol keys and upcase KEYS are normalized into downcased string keys.
56
- # - query should not have any "\r". use "\n"
57
- # - when value is nil, delete the item pair.
63
+ # - value should not have any "\r". use one "\n" as a newline.
64
+ # - {'action' => 'add-class' or :add_class} are {'action' => 'add_class'}.
65
+ # - when value is nil, delete the pair.
58
66
  def normalize
59
- norm_commands = Hash[*@commands.map{|k, v| [crlf2lf(k).downcase, crlf2lf(v)]}.flatten]
60
- action = normalize_action(norm_commands['action'])
61
- norm_commands['action'] = action
67
+ unnormalized_commands = @commands.dup
62
68
  @commands = SNP_HEADER.dup
63
- available_commands_in(action).each do |cmd|
64
- @commands[cmd] = norm_commands[cmd] if norm_commands[cmd]
65
- end
69
+ unnormalized_commands.each{|cmdkey, value| self[cmdkey] = value}
70
+ available_commands = SNP_ACTIONS[self.action] || [] # or raise
71
+ @commands.delete_if{|cmdkey, value| !available_commands.include?(cmdkey)}
72
+ @commands.delete_if{|cmdkey, value| value.nil?} # || value.empty? # TODO: "timeout=" is vaild SNP?
73
+ @commands
66
74
  end
67
75
 
68
- def normalize_action(action_type)
69
- action_type.downcase.gsub(/-/){'_'}.sub(/\Aaddclass\Z/){'add_class'}
76
+ def normalize_cmdkey(cmdkey)
77
+ crlf2lf(cmdkey).downcase
70
78
  end
71
-
72
- def available_commands_in(action)
73
- SNP_ACTIONS[action] || {}
79
+ def normalize_value(value)
80
+ crlf2lf(value)
74
81
  end
75
82
 
76
83
  def crlf2lf(s)
77
84
  s ? s.to_s.gsub(/\r\n/){"\n"}.gsub(/\r/){"\n"} : s
78
85
  end
79
86
 
87
+ def normalize_action_value(value)
88
+ normalize_value(value).downcase.gsub(/-/){'_'}.sub(/\Aaddclass\Z/){'add_class'}
89
+ end
80
90
  end
81
91
  end
82
92
  end
@@ -31,6 +31,7 @@ class Snarl
31
31
  def inspect ; @response.chomp ; end # p response #=> "SNP/1.1/0/OK/456"
32
32
 
33
33
  def ok? ; code.to_i.zero? ; end
34
+ def request_str ; request ? request.to_s : nil ; end
34
35
  end
35
36
  end
36
37
  end
@@ -3,6 +3,7 @@ class Snarl # conpat for ruby-snarl
3
3
  class SNP
4
4
 
5
5
  include Action
6
+ include SNPProcedure
6
7
 
7
8
  # default "timeout command" value. popup disappers in 10 seconds.
8
9
  DEFAULT_TIMEOUT = 10
@@ -11,49 +12,60 @@ class Snarl # conpat for ruby-snarl
11
12
  DEFAULT_TITLE = 'Ruby-Snarl'
12
13
 
13
14
  # Snarl::SNP.new('127.0.0.1', 9887)
14
- def initialize(host=nil, port=nil, verbose=false)
15
- @host = host
16
- @port = port
17
- @verbose = verbose
18
- @logger = nil
19
- @app = nil
20
- @timeout = nil
21
- @iconset = {}
22
- @title = nil
15
+ def initialize(host=nil, port=nil, verbose=false, &block)
16
+ @config = Config.new
17
+ if host && YAML.load(host).kind_of?(Hash) then
18
+ port.respond_to?(:debug) ? load(host, port, &block) : load(host, nil, &block)
19
+ else
20
+ self['host'] = host
21
+ self['port'] = port
22
+ self['verbose'] = verbose
23
+ yield(self) if block_given?
24
+ end
23
25
  end
26
+ attr_reader :config
27
+
28
+ def []=(k, v) ; @config[k] =v ; end
29
+ def [](k) ; @config[k] ; end
24
30
 
25
31
  # When you set it true, all unimportant SNP errors raise.
26
32
  # Default is false, Snarl::SNP::Error::Casual are disabled.
27
- attr_accessor :verbose
28
-
29
- # a value of SNP command "app".
30
- attr_reader :app
33
+ def verbose=(v) ; self['verbose'] = v ; end
34
+ def verbose ; self['verbose'] ; end
31
35
 
32
- attr_accessor :title
36
+ # # a value of SNP command "app".
37
+ # attr_reader :app
38
+ #
39
+ # attr_accessor :title
33
40
 
34
- # a value of SNP command "timeout".
35
- attr_accessor :timeout
41
+ # # a value of SNP command "timeout".
42
+ # attr_accessor :timeout
36
43
 
37
44
  # set Logger object. It is used when sending request and getting response.
38
45
  def logger=(logger)
39
- @logger = (logger.kind_of?(Class) ? logger.new($stdout) : logger)
46
+ self['logger'] = (logger.kind_of?(Class) ? logger.new($stdout) : logger)
40
47
  end
48
+ def logger ; self['logger'] ; end
41
49
 
42
50
  # send Snarl::SNP::Request/Hash/String and get Snarl::SNP::Response fron Snarl.
43
51
  # When the response "fatal" response, raise errors.
44
52
  # When method +verbose+ returns true, "casual" errors also raises.
45
53
  def request(req)
46
54
  req = Request.new(req) if req.kind_of?(Hash)
47
- debug(req)
55
+ action = if req.kind_of?(Request) then req.action else '(string)' end
56
+ debug("#{action}: #{req.inspect}")
48
57
  begin
49
- action = req.kind_of?(Request) ? req.action : '(string)'
50
58
  res = get_response(req)
51
59
  info("#{action}: #{res.inspect}")
52
60
  rescue Error::Casual => ex
53
61
  info("#{action}: (ignored) #{ex.message}")
54
62
  raise if verbose
63
+ res = ex.response
55
64
  rescue Error::Fatal => ex
56
- info("#{action}: #{ex.message}")
65
+ error("#{action}: #{ex.message}")
66
+ raise
67
+ rescue Errno::ECONNREFUSED => ex
68
+ error("#{ex.message} / #{self['host'].inspect}:#{self['port'].inspect}")
57
69
  raise
58
70
  end
59
71
  return res
@@ -63,25 +75,22 @@ class Snarl # conpat for ruby-snarl
63
75
 
64
76
  # add_classes('type1', 'type2', 'type3')
65
77
  # add_classes(['type1', desc1], ['type2', desc2], ['type3', desc3])
78
+ # add_classes(*array_of_pairs)
79
+ # returns [add_class_response1, add_class_response2, add_class_response3]
66
80
  def add_classes(*classes)
67
- classes.each do |classpair|
68
- classpair = [classpair, nil] if classpair.kind_of?(String)
69
- add_class(*classpair)
70
- end
81
+ classes.map{|e| e.kind_of?(String) ? [e, nil] : e}.map{|p| add_class(*p)}
71
82
  end
72
83
 
73
84
  # returns icon path if SNP knows. optional.
74
85
  def icon(s)
75
- if @iconset.has_key?(s) then @iconset[s] else s end
86
+ if self['iconset'] && self['iconset'].has_key?(s) then self['iconset'][s] else s end
76
87
  end
77
88
 
78
89
  # set icons pair. quite optional.
79
- # snp.iconset(:red => 'red.jpg')
90
+ # snp.iconset({:red => 'red.jpg'})
80
91
  # snp.notification('title', 'text', :red) #=> sends "icon=red.jpg"
81
92
  # snp.notification('title', 'text', 'blue') #=> sends "icon=blue"
82
- def iconset(icons)
83
- @iconset = icons
84
- end
93
+ def iconset(icons) ; self['iconset'] = icons ; end
85
94
  alias :icons :iconset
86
95
 
87
96
  def ping
@@ -106,9 +115,11 @@ class Snarl # conpat for ruby-snarl
106
115
  client
107
116
  end
108
117
 
109
- # send message only. app is "anonymous".
110
- # Snarl::SNP.show_message(host, 9887, title, text, tomeout, icon)
111
- # Snarl::SNP.show_message(host, title, text, tomeout, icon)
118
+ # send message only. app and class is "anonymous".
119
+ # Snarl::SNP.show_message(host, 9887, title, text, timeout, icon)
120
+ # Snarl::SNP.show_message(host, title, text, timeout, icon)
121
+ # Snarl::SNP.show_message(host, title, text)
122
+ # Snarl::SNP.show_message(host, text)
112
123
  def self.show_message(host, port, title=nil, text=nil, timeout=nil, icon=nil)
113
124
  # TODO: (host, title, text, 10)
114
125
  if port.kind_of?(String) && icon.nil? then
@@ -124,19 +135,23 @@ class Snarl # conpat for ruby-snarl
124
135
 
125
136
  private
126
137
 
127
- def get_response(req)
128
- host = @host || Config.host
129
- port = @port || Config.port
130
- TCPSocket.open(host, port) do |s|
138
+ def send(req)
139
+ # normalize just before using # FIXME:
140
+ TCPSocket.open(self['host'], self['port']) do |s|
131
141
  s.write(req)
132
- res = Response.new(s.gets)
133
- res.request = req
134
- res
142
+ s.gets
135
143
  end
136
144
  end
137
145
 
138
- def info(m); @logger.info(m) if @logger; end
139
- def debug(m); @logger.debug(m) if @logger; end
146
+ def get_response(req)
147
+ res = Response.new(send(req))
148
+ res.request = req
149
+ res
150
+ end
151
+
152
+ def error(m); logger.error(m) if logger ; end
153
+ def info(m); logger.info(m) if logger ; end
154
+ def debug(m); logger.debug(m) if logger ; end
140
155
 
141
156
  end
142
157
  end