langrove 0.0.2

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.
Files changed (62) hide show
  1. data/.rspec +1 -0
  2. data/.rvmrc +62 -0
  3. data/.watchr +27 -0
  4. data/Gemfile +16 -0
  5. data/Gemfile.lock +61 -0
  6. data/Rakefile +24 -0
  7. data/functional/.gitignore +1 -0
  8. data/functional/bin/datagram +6 -0
  9. data/functional/config/.gitignore +0 -0
  10. data/functional/config/boot.rb +64 -0
  11. data/functional/config/daemons.yml +12 -0
  12. data/functional/config/environment.rb +28 -0
  13. data/functional/config/environments/development.rb +0 -0
  14. data/functional/config/environments/production.rb +0 -0
  15. data/functional/config/environments/test.rb +0 -0
  16. data/functional/lib/daemon/datagram.rb +21 -0
  17. data/functional/lib/handler/socket_to_file.rb +36 -0
  18. data/functional/lib/protocol/socket_to_file.rb +55 -0
  19. data/functional/libexec/daemon.rb +68 -0
  20. data/functional/log/.gitignore +3 -0
  21. data/functional/tmp/README +1 -0
  22. data/lib/langrove/_base.rb +26 -0
  23. data/lib/langrove/adaptor/base.rb +3 -0
  24. data/lib/langrove/adaptor/datagram.rb +27 -0
  25. data/lib/langrove/adaptor_base.rb +89 -0
  26. data/lib/langrove/client/base.rb +2 -0
  27. data/lib/langrove/client/datagram.rb +25 -0
  28. data/lib/langrove/client_base.rb +114 -0
  29. data/lib/langrove/daemon/base.rb +2 -0
  30. data/lib/langrove/daemon_base.rb +175 -0
  31. data/lib/langrove/ext/class_loader.rb +148 -0
  32. data/lib/langrove/ext/config_item.rb +34 -0
  33. data/lib/langrove/ext/config_loader.rb +16 -0
  34. data/lib/langrove/ext/fake_logger.rb +8 -0
  35. data/lib/langrove/ext/persistable.rb +103 -0
  36. data/lib/langrove/ext/string.rb +35 -0
  37. data/lib/langrove/ext.rb +7 -0
  38. data/lib/langrove/handler/base.rb +2 -0
  39. data/lib/langrove/handler_base.rb +141 -0
  40. data/lib/langrove/protocol/base.rb +2 -0
  41. data/lib/langrove/protocol/syslog.rb +32 -0
  42. data/lib/langrove/protocol_base.rb +32 -0
  43. data/lib/langrove/version.rb +3 -0
  44. data/lib/langrove.rb +1 -0
  45. data/spec/functional/daemon/datagram_spec.rb +115 -0
  46. data/spec/langrove/adaptor/datagram_spec.rb +6 -0
  47. data/spec/langrove/adaptor_base_spec.rb +48 -0
  48. data/spec/langrove/client/datagram_spec.rb +1 -0
  49. data/spec/langrove/client_base_spec.rb +5 -0
  50. data/spec/langrove/daemon_base_spec.rb +101 -0
  51. data/spec/langrove/ext/class_loader_spec.rb +83 -0
  52. data/spec/langrove/ext/config_item_spec.rb +81 -0
  53. data/spec/langrove/ext/config_loader_spec.rb +5 -0
  54. data/spec/langrove/ext/fake_logger_spec.rb +0 -0
  55. data/spec/langrove/ext/persistable_spec.rb +117 -0
  56. data/spec/langrove/ext/string_spec.rb +16 -0
  57. data/spec/langrove/handler_base_spec.rb +57 -0
  58. data/spec/langrove/protocol/syslog_spec.rb +45 -0
  59. data/spec/langrove/protocol_base_spec.rb +6 -0
  60. data/spec/todo_spec.rb +12 -0
  61. data/tmp/README +2 -0
  62. metadata +200 -0
@@ -0,0 +1,114 @@
1
+ require 'eventmachine'
2
+ require 'langrove'
3
+
4
+ module LanGrove
5
+
6
+ module Client
7
+
8
+ class Base < EventMachine::Connection
9
+
10
+ #
11
+ # Misnomer.
12
+ #
13
+ #
14
+ # This is not the client,
15
+ #
16
+ #
17
+ # It is the server's perspective of the client,
18
+ #
19
+ # These are generally stored in the daemon's
20
+ # handler collection,
21
+ #
22
+ # Which contains this and all similar clients,
23
+ #
24
+ # Each bonded through the daemon's connection
25
+ # adaptor to the socket that couples them to
26
+ # their actual client..
27
+ #
28
+
29
+ #
30
+ # The handler collection specified in config
31
+ # is bound to this attribute after connect.
32
+ #
33
+ attr_accessor :handler
34
+ attr_accessor :protocol
35
+ attr_accessor :config
36
+
37
+ #
38
+ # Data arriving through the Adaptor is passed
39
+ # through the config assigned protocol and
40
+ # then arrives here in whatever format the
41
+ # <Protocol>.decode() method returned.
42
+ #
43
+ #
44
+ def receive( decoded_data )
45
+
46
+ #
47
+ # OVERRIDE
48
+ #
49
+ # - Inbound data arrived at the adaptor
50
+ #
51
+ # - It has passed through the protocol as
52
+ # defined in config.
53
+ #
54
+ # The protocol arranged the data into
55
+ # a Hash of:
56
+ #
57
+ # - key and value pairs
58
+ #
59
+ # - key and more_complicated_thing pairs.
60
+ #
61
+ # - The decoded data was then passed into
62
+ # this function.
63
+ #
64
+ #
65
+
66
+ end
67
+
68
+ def receive_data( data )
69
+
70
+ #
71
+ # Note: The protocol currently lives on the Handler
72
+ #
73
+ # It MIG( ...in progress... )HT move,
74
+ #
75
+ # To a separate instance of the protocol object
76
+ # residing in each Client::ClientType instance
77
+ # in the Handler::Base.client collection.
78
+ #
79
+ # Pending examining the logical barrier of having
80
+ # the Handler route to the Cleint per a key value
81
+ # extracted from the decoded data - as decoded by
82
+ # the client's instance of the protocol not yet
83
+ # accessable within the as yet undeterminable
84
+ # instance in the Handlers collection.
85
+ #
86
+ # Catch22(ish)
87
+ #
88
+ # But that was suitable as a pettern for the
89
+ # datagram server.
90
+ #
91
+ # Perhaps after implementing Adapter::TcpServer
92
+ #
93
+ # And gaining a more knowsome notion of the
94
+ # intricacies of the application layer, esp
95
+ # ACKing and NAKing.
96
+ #
97
+ # Then the possibilities of the Client::ClientType
98
+ # being a decendant of <self> --
99
+ #
100
+ # ie. The EM::Connection as instanciated upon each
101
+ # socket connect.
102
+ #
103
+ # -- will likely resolve.
104
+ #
105
+ #
106
+ receive( @protocol.decode( data ) )
107
+
108
+ end
109
+
110
+ end
111
+
112
+ end
113
+
114
+ end
@@ -0,0 +1,2 @@
1
+ module LanGrove::Daemon; end
2
+ require 'langrove/daemon_base'
@@ -0,0 +1,175 @@
1
+ require 'eventmachine'
2
+
3
+ require 'langrove'
4
+
5
+ module LanGrove::Daemon class Base
6
+
7
+ def listen
8
+
9
+ #
10
+ # Handler - The CollectionOfClients, is passed
11
+ # through the adaptor to be assigned
12
+ # to the attaching Clients.
13
+ #
14
+ @adaptor.listen( @handler, @handler.protocol )
15
+
16
+ end
17
+
18
+ def stop_daemon
19
+
20
+ #
21
+ # Handle stopping daemon
22
+ #
23
+ @logger.info "#{self} received stop_daemon"
24
+ @handler.stop_daemon
25
+
26
+ end
27
+
28
+ def reload_daemon
29
+
30
+ #
31
+ # TODO: Handle reloading daemon properly
32
+ #
33
+ # [ !!complexities abound around reloading config!! ]
34
+ #
35
+
36
+ @logger.info "#{self} received reload_daemon"
37
+ @handler.reload_daemon
38
+
39
+ end
40
+
41
+ def schedule
42
+
43
+ EM::add_periodic_timer( @my_config['periodic'] ) do
44
+
45
+ @logger.info "#{self} Scheduling periodic ...TODO..."
46
+
47
+ #
48
+ # TODO: This can be a lot more usefull than
49
+ # then one ticker to one function call
50
+ #
51
+
52
+ @handler.periodic
53
+
54
+ end if @my_config.has_key? 'periodic'
55
+
56
+ end
57
+
58
+ def run
59
+
60
+ EventMachine::run do
61
+
62
+ #
63
+ # https://github.com/eventmachine/eventmachine/wiki/Protocol-Implementations
64
+ #
65
+
66
+ schedule
67
+
68
+ listen if @server
69
+
70
+ end
71
+
72
+ end
73
+
74
+ def initialize( config_hash, daemon_name, logger )
75
+
76
+ @config = config_hash
77
+ @daemon_name = daemon_name
78
+ @logger = logger
79
+ @server = true
80
+
81
+ #
82
+ # A network connection.
83
+ #
84
+ @adaptor = nil
85
+
86
+ #
87
+ # A handler to direct the traffic.
88
+ #
89
+ @handler = nil
90
+
91
+
92
+ begin
93
+
94
+ @logger.info "Starting daemon: #{@daemon_name}"
95
+
96
+ rescue
97
+
98
+ #
99
+ # you shall not pass
100
+ #
101
+ raise LanGrove::DaemonConfigException.new( "Requires a logger" )
102
+
103
+ end
104
+
105
+ begin
106
+
107
+ @my_config = @config[ :daemons ][ @daemon_name ]
108
+
109
+ @server = @my_config[ :server ] if @my_config.has_key? :server
110
+
111
+ adaptor = @my_config[ :adaptor ][ :connection ]
112
+
113
+ handler = @my_config[ :handler ][ :collection ]
114
+
115
+ rescue
116
+
117
+ error = "Missing config item for daemon: #{@daemon_name}"
118
+
119
+ @logger.error "EXIT: #{error}"
120
+
121
+ raise LanGrove::DaemonConfigException.new(
122
+
123
+ "Missing config item for daemon: #{@daemon_name}"
124
+
125
+ )
126
+
127
+ end
128
+
129
+ #
130
+ # initialize the connection adaptor specified in config
131
+ #
132
+ # daemons:
133
+ # name_of_daemon:
134
+ # adaptor:
135
+ # connection: TcpServer <----------
136
+ # handler:
137
+ # collection: CollectionOfClients
138
+ #
139
+
140
+ @logger.info "Initialize instance: Adaptor::#{adaptor}.new( @my_config[ :adaptor ], logger )"
141
+
142
+ @adaptor = LanGrove::ClassLoader.create( {
143
+
144
+ :module => 'Adaptor',
145
+ :class => adaptor
146
+
147
+ } ).new( @my_config[ :adaptor ], logger )
148
+
149
+
150
+ #
151
+ # bind to the handler collection specified in config
152
+ #
153
+ # daemons:
154
+ # name_of_daemon:
155
+ # adaptor:
156
+ # connection: TcpServer
157
+ # handler:
158
+ # collection: CollectionOfClients <----------
159
+ #
160
+ # CollectionOfClients ---> is the handler passed into listen()
161
+ #
162
+
163
+ @logger.info "Initialize instance: Handler::#{handler}.new( config_hash, '#{daemon_name}', logger )"
164
+
165
+ @handler = LanGrove::ClassLoader.create( {
166
+
167
+ :module => 'Handler',
168
+ :class => handler
169
+
170
+ } ).new( @config, @daemon_name, @logger )
171
+
172
+
173
+ end
174
+
175
+ end; end
@@ -0,0 +1,148 @@
1
+ require 'langrove/ext/string'
2
+ module LanGrove
3
+
4
+ class ClassLoaderException < Exception; end
5
+
6
+ class ClassLoader
7
+
8
+ def self.create( class_config, logger = nil )
9
+
10
+ logger.info( "#{self} is loading Class with #{class_config.inspect}" ) unless logger.nil?
11
+
12
+ #
13
+ # When <config> = {
14
+ #
15
+ # :module => 'ModuleName'
16
+ # :class => 'ClassName'
17
+ #
18
+ # }
19
+ #
20
+ # Then this will return the constantized
21
+ # definition instance of ClassName as
22
+ # loaded from the F1RST found .rb file
23
+ # according to:
24
+ #
25
+ # - lib/module_name/class_name.rb
26
+ # - langrove/module_name/class_name.rb
27
+ #
28
+ # Which then facilitates the following
29
+ # construct:
30
+ #
31
+ # planet = ClassLoader.create(
32
+ #
33
+ # :module => 'Planet',
34
+ # :class => 'Mercury'
35
+ #
36
+ # ).new( *initializer_parameters )
37
+ #
38
+
39
+ raise ClassLoaderException.new(
40
+
41
+ "class_config requires :module"
42
+
43
+ ) unless class_config.has_key? :module
44
+
45
+ raise ClassLoaderException.new(
46
+
47
+ "class_config requires :class"
48
+
49
+ ) unless class_config.has_key? :class
50
+
51
+
52
+ module_name = class_config[:module]
53
+ class_name = class_config[:class]
54
+
55
+
56
+ #
57
+ # SIGNIFICANT DECISION
58
+ #
59
+ # - Late binding to the extent of also calling
60
+ # to require the actual class.rb file could
61
+ # potentially be avoided by
62
+ #
63
+ # << using this layer in the abstraction >>
64
+ #
65
+ # to do the necessary requiring for the specific
66
+ # daemon being spawned.
67
+ #
68
+ # - Obviously there are downsides to eval...
69
+ #
70
+ # - But there are upsides to having this layer
71
+ # totally aliteral - it leaves the window open
72
+ # to the later posibility of collecting the class
73
+ # definition itself from across the network.
74
+ #
75
+ #
76
+ # Which was the cental purpose behind daemons
77
+ # by configuration in the first place.
78
+ #
79
+ # Taking latebinding to a whole new level...
80
+ #
81
+
82
+
83
+ #
84
+ # First try local implementation root
85
+ #
86
+
87
+ exception = nil
88
+
89
+ location = nil
90
+
91
+ begin
92
+
93
+ location = "#{module_name.underscore}/#{class_name.underscore}"
94
+
95
+ eval "require '#{location}'"
96
+
97
+ return Object.const_get( module_name ).const_get( class_name )
98
+
99
+ rescue LoadError => e
100
+
101
+ exception = e
102
+
103
+ logger.error "Missing class definition in lib/#{location}.rb" unless logger.nil?
104
+
105
+ #rescue Exception => e
106
+ #
107
+ # #
108
+ # # Incase of more... (discovery phase)
109
+ # #
110
+ #
111
+
112
+
113
+ end
114
+
115
+ #
116
+ # Fall back to langrove gem lib
117
+ #
118
+
119
+ begin
120
+
121
+ path = File.expand_path('../../', __FILE__)
122
+
123
+ location = "#{path}/#{module_name.underscore}/#{class_name.underscore}"
124
+
125
+ eval "require '#{location}'"
126
+
127
+ return LanGrove.const_get( module_name ).const_get( class_name )
128
+
129
+ rescue Exception => e
130
+
131
+ #
132
+ # Raise from the original exception to
133
+ # inform the local implementation
134
+ # it is missing a module/class .rb
135
+ # "no such file 'module/class"
136
+ #
137
+ # And not confuse the issue by raising
138
+ # "no such file 'langrove/module/class"
139
+ #
140
+ raise ClassLoaderException.new "#{exception.message}.rb"
141
+
142
+ end
143
+
144
+ end
145
+
146
+ end
147
+
148
+ end
@@ -0,0 +1,34 @@
1
+
2
+ module LanGrove class ConfigException < Exception; end; end
3
+
4
+ module LanGrove class ConfigItem
5
+
6
+ def self.get(config_hash, key_array, mandatory = true)
7
+
8
+ # raise ConfigException.new("match")
9
+
10
+ hash = config_hash
11
+
12
+ key_array.each do |key|
13
+
14
+ if mandatory != false then
15
+ # raises a config exception if the item isnt present
16
+ raise ConfigException.new("Missing config item '#{key}'") unless hash.has_key?(key)
17
+
18
+ # raises a config exception if the nested item isnt present NEITHER NOT
19
+ # raise ConfigException.new("Missing config item '#{key}'") if hash[key].empty?
20
+
21
+ hash = hash[key]
22
+
23
+ # raises a config exception if the key isnt present BLANK NEITHER
24
+ raise ConfigException.new("Missing config item '#{key}'") if hash == ""
25
+
26
+ end
27
+
28
+ end
29
+
30
+ return hash
31
+
32
+ end
33
+
34
+ end; end
@@ -0,0 +1,16 @@
1
+ # TODO: spec
2
+
3
+ require 'yaml'
4
+
5
+ module LanGrove class ConfigLoader
6
+
7
+ def self.yaml( file )
8
+
9
+ # no rescue block
10
+ # daemon fails on missing config file
11
+
12
+ YAML.load_file( file )
13
+
14
+ end
15
+
16
+ end; end
@@ -0,0 +1,8 @@
1
+ module LanGrove class FakeLogger
2
+ def initialize( parameter = :noisey )
3
+ @parameter = parameter
4
+ end
5
+ def method_missing( symbol, *args, &block )
6
+ puts "#{symbol}: #{args}" unless @parameter == :silent
7
+ end
8
+ end; end
@@ -0,0 +1,103 @@
1
+ require 'resque'
2
+ require 'yaml'
3
+
4
+ # require 'updated_puppet_state'
5
+
6
+ module LanGrove class Persistable
7
+
8
+ def initialize( notifies = [] )
9
+
10
+ @notifies = notifies
11
+
12
+ @notifies.each do |worker|
13
+
14
+ Object.const_set(
15
+
16
+ worker.camelize, Class.new
17
+
18
+ ) ### ??unless defined already??
19
+
20
+ end
21
+
22
+ end
23
+
24
+ def load_hash( hash_instance, from_file )
25
+ #
26
+ # <hash_instance> as String (name of) variable storing the Hash
27
+ #
28
+
29
+ @logger.debug "loading from: #{from_file}" if @logger
30
+
31
+ #
32
+ # get the instance variable (late bind)
33
+ #
34
+ hash = self.instance_variable_get( hash_instance.to_sym )
35
+ hash ||= {}
36
+
37
+ begin
38
+
39
+ #
40
+ # load file contents, merge into hash and
41
+ # store it at the instance variable
42
+ #
43
+ persisted = YAML.load_file( from_file )
44
+ hash.merge!( persisted ) if persisted.is_a?( Hash )
45
+ self.instance_variable_set( hash_instance.to_sym, hash )
46
+
47
+ #
48
+ # set flag to 'already in storage'
49
+ #
50
+ @stored = true
51
+
52
+ rescue Exception => e
53
+ @logger.error "FAILED loading from #{from_file} #{e}" if @logger
54
+
55
+ end
56
+ end
57
+
58
+
59
+ def store_hash( hash_instance, to_file )
60
+ #
61
+ # <hash_instance> as String (name of) variable storing the Hash
62
+ #
63
+
64
+ if @stored != nil && @stored
65
+
66
+ @logger.debug "storing to: #{to_file} skipped - no change"
67
+ return
68
+
69
+ end
70
+
71
+ @logger.debug "storing to: #{to_file}"
72
+
73
+ #
74
+ # get the instance variable (late bind)
75
+ #
76
+ hash = self.instance_variable_get( hash_instance.to_sym )
77
+
78
+ begin
79
+
80
+ File.open(to_file, 'w') do |f|
81
+
82
+ YAML::dump( hash, f )
83
+ @stored = true
84
+
85
+ end
86
+
87
+ #@notifies.each do |worker|
88
+ #
89
+ # Resque.enqueue(
90
+ #
91
+ # Object.const_get( worker.camelize ), to_file
92
+ #
93
+ # )
94
+ #
95
+ #end
96
+
97
+ rescue Exception => e
98
+ @logger.error "FAILED storing to: #{to_file} #{e}" if @logger
99
+ @stored = false
100
+
101
+ end
102
+ end
103
+ end; end
@@ -0,0 +1,35 @@
1
+ # extend ruby String class
2
+
3
+ class String
4
+ #
5
+ # from the rails inflector
6
+ # (de-camelize)
7
+ #
8
+ # ie. CamelThing becomes camel_thing
9
+ #
10
+ def underscore
11
+ gsub(/::/, '/').
12
+ gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
13
+ gsub(/([a-z\d])([A-Z])/,'\1_\2').
14
+ tr("-", "_").
15
+ downcase
16
+ end
17
+
18
+ #
19
+ # unless already defined
20
+ #
21
+ end unless String.method_defined?(:underscore)
22
+
23
+ class String
24
+ #
25
+ # from the rails inflector
26
+ # (camelize)
27
+ #
28
+ # ie. CamelThing becomes camel_thing
29
+ #
30
+ def camelize(first_letter_in_uppercase = :upper)
31
+ s = gsub(/\/(.?)/){|x| "::#{x[-1..-1].upcase unless x == '/'}"}.gsub(/(^|_)(.)/){|x| x[-1..-1].upcase}
32
+ s[0...1] = s[0...1].downcase unless first_letter_in_uppercase == :upper
33
+ s
34
+ end
35
+ end unless String.method_defined?(:camelize)
@@ -0,0 +1,7 @@
1
+ module Langrove; end
2
+ require 'langrove/ext/class_loader'
3
+ require 'langrove/ext/config_item'
4
+ require 'langrove/ext/config_loader'
5
+ require 'langrove/ext/fake_logger'
6
+ require 'langrove/ext/persistable'
7
+ require 'langrove/ext/string'
@@ -0,0 +1,2 @@
1
+ module LanGrove::Handler; end
2
+ require 'langrove/handler_base'