slnky 0.3.5 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 511db9f81650717557115069f4f0c506835b6c3f
4
- data.tar.gz: 7be97d4637c490cc2f758c3a464b3fdab7403b85
3
+ metadata.gz: 77b112accb022f9dd0533da6fe381c765d05b91b
4
+ data.tar.gz: 87ed869ce697d6e3a9952804297737fec5f02464
5
5
  SHA512:
6
- metadata.gz: 8b1cd9411c9f2043de755e1eeb4c32675b0a70b638812fc6380f8db349fd6ceaa92ff34a26f793a8ed4d0d7e7557f44b6d904c201c504523beaf690bef148f64
7
- data.tar.gz: c117a14627713d77259a47a39b3fd8504bcd1ff5a7234c6471c76635d2b9b3f004f0fff4aee7b7c0dce72808c979bfd3b249ae81e0f90a5ec0bc925fb6ef202d
6
+ metadata.gz: 8d7a928f9aad0803eed260afdd551b403217c9e8af6dad6ebc3e48380af75d38c164eab540f6fe1450e6022c6681c14f53447c4f7f2056f5769c43cea205aeab
7
+ data.tar.gz: c3b71ab7ec862c1848acef70c172d1d704c0f8275486d606f10ce533e96786aff9b55fffcabdc7bd8df23c71479cb03735f5cec81592118a43586d9d269bc3ad
@@ -4,7 +4,6 @@ module Slnky
4
4
  desc 'service NAME [DIR]', 'generate a service named NAME'
5
5
  def service(name, dir=nil)
6
6
  generator = Slnky::Generator::Service.new(name, dir)
7
- raise Thor::Error, "error: directory '#{generator.dir}' exists" if File.exists?(generator.dir)
8
7
  generator.generate
9
8
  end
10
9
 
data/lib/slnky/cli.rb CHANGED
@@ -11,6 +11,13 @@ end
11
11
  module Slnky
12
12
  module CLI
13
13
  class Main < Thor
14
+ map %w[--version -v] => :__print_version
15
+
16
+ desc "--version, -v", "print the version"
17
+ def __print_version
18
+ puts "Slnky version: #{Slnky::VERSION}"
19
+ end
20
+
14
21
  desc 'init', 'initialize configuration directory'
15
22
  def init
16
23
  dir = "#{ENV['HOME']}/.slnky"
@@ -10,42 +10,82 @@ module Slnky
10
10
 
11
11
  def initialize(name, dir)
12
12
  @name = name
13
- @dir = dir == nil ? "slnky-#{name}" : dir
13
+ @dir = File.expand_path(dir == nil ? "slnky-#{name}" : dir)
14
14
  short = self.class.name.split('::').last.downcase
15
15
  @template = File.expand_path("../template/#{short}", __FILE__)
16
16
  end
17
17
 
18
18
  def generate
19
- # copy dir
20
- puts "creating directory and processing templates..."
21
- FileUtils.cp_r(@template, @dir)
22
- puts "#{@dir}:"
23
- # process templates
24
- Find.find(@dir).each do |f|
25
- next unless File.file?(f) && File.extname(f) == '.erb'
26
- template(f)
27
- end
19
+ puts "generating service #{@name}:"
20
+ puts " from: #{@template}"
21
+ puts " to: #{@dir}"
22
+ process_files
28
23
  # make service executable
29
24
  `chmod 755 #{@dir}/service-slnky-#{@name}`
30
25
  # git init
31
26
  puts "initializing git..."
32
27
  `cd #{@dir} && git init . || true`
28
+ `cd #{@dir} && git add .`
33
29
  end
34
30
 
35
31
  protected
36
32
 
37
- def template(file)
33
+ def process_files
34
+ Find.find(@template).each do |path|
35
+ next unless File.file?(path)
36
+ file = path.gsub(/^#{@template}\//, '')
37
+ ext = File.extname(path)
38
+ mkdir(File.dirname("#{@dir}/#{file}"))
39
+ if ext == '.erb'
40
+ tmpl(file)
41
+ else
42
+ file(file)
43
+ end
44
+ end
45
+ end
46
+
47
+ def mkdir(dir)
48
+ return if File.directory?(dir)
49
+ # puts "mkdir: #{dir}"
50
+ FileUtils.mkdir_p(dir)
51
+ end
52
+
53
+ def file(file)
54
+ # puts "file: #{file}"
55
+ FileUtils.cp("#{@template}/#{file}", "#{@dir}/#{file}")
56
+ end
57
+
58
+ def tmpl(file)
59
+ # puts "tmpl: #{file}"
60
+ path = "#{@template}/#{file}"
38
61
  var = {
39
62
  name: @name,
40
63
  dir: @dir
41
64
  }
42
- dest = file.gsub(/\.erb$/, '').gsub('NAME', @name)
43
- puts " #{dest}"
44
- template = Tilt.new(file)
65
+ out = file.gsub(/\.erb$/, '').gsub('NAME', @name)
66
+ dest = "#{@dir}/#{out}"
67
+ # puts " #{dest}"
68
+ template = Tilt.new(path)
45
69
  output = template.render(self, var)
46
70
  File.write(dest, output)
47
- FileUtils.rm(file) if file != dest
48
71
  end
72
+
73
+ # def generate_old
74
+ # # copy dir
75
+ # puts "creating directory and processing templates..."
76
+ # FileUtils.cp_r(@template, @dir)
77
+ # puts "#{@dir}:"
78
+ # # process templates
79
+ # Find.find(@dir).each do |f|
80
+ # next unless File.file?(f) && File.extname(f) == '.erb'
81
+ # template(f)
82
+ # end
83
+ # # make service executable
84
+ # `chmod 755 #{@dir}/service-slnky-#{@name}`
85
+ # # git init
86
+ # puts "initializing git..."
87
+ # `cd #{@dir} && git init . || true`
88
+ # end
49
89
  end
50
90
  end
51
91
  end
@@ -0,0 +1,33 @@
1
+ module Slnky
2
+ module Service
3
+ class Exchanges
4
+ def initialize(channel)
5
+ @channel = channel
6
+ @exchanges = {}
7
+ end
8
+
9
+ def create(name, overrides={})
10
+ options = {
11
+ type: :fanout
12
+ }.merge(overrides)
13
+ @exchanges[name] =
14
+ case options[:type]
15
+ when :fanout
16
+ @channel.fanout("slnky.#{name}")
17
+ else
18
+ raise "unknown exchange type: #{options[:type]}"
19
+ end
20
+ end
21
+
22
+ def [](name)
23
+ @exchanges["slnky.#{name}"] || @exchanges[name] || raise("no exchange found: #{name}")
24
+ end
25
+
26
+ def each
27
+ @exchanges.each do |name, exchange|
28
+ yield name, exchange
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,19 @@
1
+ module Slnky
2
+ module Service
3
+ class Periodics
4
+ def initialize
5
+ @timers = []
6
+ end
7
+
8
+ def add(seconds, method)
9
+ @timers << {seconds: seconds, method: method}
10
+ end
11
+
12
+ def each
13
+ @timers.each do |timer|
14
+ yield timer['seconds'], timer['method']
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,27 @@
1
+ module Slnky
2
+ module Service
3
+ class Queues
4
+ def initialize(channel)
5
+ @channel = channel
6
+ @queues = {}
7
+ end
8
+
9
+ def create(name, exchange, overrides={})
10
+ options = {
11
+ durable: true
12
+ }.merge(overrides)
13
+ @queues[name] = @channel.queue("service.#{@name}.events", options).bind(exchange)
14
+ end
15
+
16
+ def [](name)
17
+ @queues["service.#{@name}.events"] || @queues[name] || raise("no queue found: #{name}")
18
+ end
19
+
20
+ def each
21
+ @queues.each do |name, exchange|
22
+ yield name, exchange
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,27 @@
1
+ module Slnky
2
+ module Service
3
+ class Subscriptions
4
+ def initialize
5
+ @subs = []
6
+ end
7
+
8
+ def add(name, method)
9
+ @subs << {name: name, method: method}
10
+ end
11
+
12
+ def each
13
+ @subs.each do |sub|
14
+ yield sub[:name], sub[:method]
15
+ end
16
+ end
17
+
18
+ def for(name)
19
+ @subs.each do |sub|
20
+ if sub[:name] == name || File.fnmatch(sub[:name], name)
21
+ yield sub[:name], sub[:method] if block_given?
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
data/lib/slnky/service.rb CHANGED
@@ -1,57 +1,66 @@
1
- require 'slnky/version'
2
- require 'slnky/message'
3
1
  require 'amqp'
4
2
  require 'open-uri'
5
3
  require 'json'
4
+ require 'socket'
5
+
6
+ require 'slnky/version'
7
+ require 'slnky/message'
8
+ require 'slnky/service/subscriptions'
9
+ require 'slnky/service/periodics'
10
+ require 'slnky/service/queues'
11
+ require 'slnky/service/exchanges'
6
12
 
7
13
  module Slnky
8
14
  module Service
9
15
  class Base
10
- def initialize(url, env='development')
16
+ attr_reader :config
17
+
18
+ def initialize(url, options={})
11
19
  @server = url
12
- @environment = env
13
20
  @name = self.class.name.split('::').last.downcase
14
- json = open("#{@server}/configs/#{@name}") {|f| f.read }
15
- @config = JSON.parse(json)
16
- @host = @config['rabbit']['host']
17
- @port = @config['rabbit']['port']
18
- @subscriptions = {}
19
- @periodics = []
20
- @exchanges = {}
21
+ @environment = options.delete(:env) || options.delete(:environment) || 'development'
22
+ @config = load_config(options)
23
+
24
+ @subscriptions = self.class.subscriptions || Slnky::Service::Subscriptions.new
25
+ @periodics = self.class.periodics || Slnky::Service::Periodics.new
26
+ @hostname = Socket.gethostname
27
+ @ipaddress = Socket.ip_address_list.find { |ai| ai.ipv4? && !ai.ipv4_loopback? }.ip_address
21
28
  end
22
29
 
23
30
  def start
24
- AMQP.start("amqp://#{@host}:#{@port}") do |connection|
31
+ AMQP.start("amqp://#{config.rabbit.host}:#{config.rabbit.port}") do |connection|
25
32
  @channel = AMQP::Channel.new(connection)
26
33
  @channel.on_error do |ch, channel_close|
27
34
  raise "Channel-level exception: #{channel_close.reply_text}"
28
35
  end
29
- @exchanges['events'] = @channel.fanout("slnky.events")
30
- @exchanges['logs'] = @channel.fanout("slnky.logs")
31
36
 
32
- log :info, "slnky.service.#{@name}: running"
37
+ @exchanges = Slnky::Service::Exchanges.new(@channel)
38
+ @exchanges.create('events')
39
+ @exchanges.create('logs')
40
+
41
+ @queues = Slnky::Service::Queues.new(@channel)
42
+ @queues.create(@name, @exchanges['events'])
43
+
44
+ log :info, "running"
33
45
 
34
46
  run
35
47
 
36
- @subscriptions.each do |name, block|
37
- log :info, "slnky.service.#{@name}: subscribed to: #{name}"
48
+ @subscriptions.each do |name, method|
49
+ log :info, "subscribed to: #{name} -> #{self.class.name}.#{method}"
38
50
  end
39
51
 
40
- @channel.queue("service.#{@name}.events", durable: true).bind(@exchanges['events']).subscribe do |raw|
41
- payload = parse(raw)
42
- if @subscriptions[payload.name]
43
- @subscriptions[payload.name].call(payload)
44
- end
45
- # TODO: support this better
46
- if @subscriptions['*']
47
- @subscriptions['*'].call(payload)
52
+ @queues[@name].subscribe do |raw|
53
+ message = parse(raw)
54
+ event = message.name
55
+ data = message.payload
56
+ @subscriptions.for(event) do |name, method|
57
+ self.send(method.to_sym, event, data)
48
58
  end
49
59
  end
50
60
 
51
- @periodics.each do |p|
52
- EventMachine.add_periodic_timer(p[:timer]) do
53
- b = p[:block]
54
- instance_eval &b
61
+ @periodics.each do |seconds, method|
62
+ EventMachine.add_periodic_timer(seconds) do
63
+ self.send(method.to_sym)
55
64
  end
56
65
  end
57
66
 
@@ -69,7 +78,7 @@ module Slnky
69
78
  protected
70
79
 
71
80
  def run
72
- # nothing here
81
+ # nothing here - overridden in subclasses
73
82
  end
74
83
 
75
84
  def msg(data)
@@ -80,16 +89,57 @@ module Slnky
80
89
  Slnky::Message.parse(data)
81
90
  end
82
91
 
83
- def subscribe(name, &block)
84
- @subscriptions[name] = block
92
+ def subscribe(name, method)
93
+ raise "move this to class level, use methods instead of blocks"
94
+ # @subscriptions.add(name, method)
85
95
  end
86
96
 
87
- def periodic(timer, &block)
88
- @periodics << {timer: timer, block: block}
97
+ def periodic(seconds, method)
98
+ raise "move this to class level, use methods instead of blocks"
99
+ # @periodics.add(seconds, method)
89
100
  end
90
101
 
91
102
  def log(level, message)
92
- @exchanges['logs'].publish(msg({service: "#{@name}-#{$$}", level: level, message: message}))
103
+ data = {
104
+ service: "#{@name}-#{$$}",
105
+ level: level,
106
+ hostname: @hostname,
107
+ ipaddress: @ipaddress,
108
+ message: "slnky.service.#{@name}: #{message}"
109
+ }
110
+ @exchanges['logs'].publish(msg(data)) if @exchanges && @exchanges['logs'] # only log to the exchange if it's created
111
+ puts "%s [%6s] %s" % [Time.now, data[:level], data[:message]] if development? # log to the console if in development
112
+ end
113
+
114
+ def development?
115
+ @environment == 'development'
116
+ end
117
+
118
+ def load_config(config)
119
+ # if you specify config, it will not load from server
120
+ # this is useful for testing, so you won't need to be running
121
+ # a server locally or configure your development service to
122
+ # talk to production server
123
+ if !config || config.count == 0
124
+ config = JSON.parse(open("#{@server}/configs/#{@name}") {|f| f.read })
125
+ end
126
+ DeepStruct.new(config)
127
+ end
128
+
129
+ class << self
130
+ attr_reader :subscriptions
131
+ attr_reader :periodics
132
+
133
+ def subscribe(name, method)
134
+ @subscriptions ||= Slnky::Service::Subscriptions.new
135
+ @subscriptions.add(name, method)
136
+ end
137
+
138
+ def periodic(seconds, method)
139
+ @periodics ||= Slnky::Service::Periodics.new
140
+ @periodics.add(seconds, method)
141
+ end
142
+ alias_method :timer, :periodic
93
143
  end
94
144
 
95
145
  # def pub(data)
@@ -8,4 +8,6 @@
8
8
  /tmp/
9
9
  /log/*
10
10
  /.env
11
+ /slnky-cli/lib/slnky/template/service/deploy.env.erb
12
+ /test/config.yaml
11
13
  /deploy.env
@@ -3,12 +3,13 @@ require 'slnky'
3
3
  module Slnky
4
4
  module Service
5
5
  class <%= name.capitalize %> < Base
6
- def run
7
- subscribe 'event.name' do |message|
8
- name = message.name
9
- data = message.payload
10
- # do something
11
- end
6
+ subscribe 'slnky.service.test', :handler
7
+ # you can also subscribe to heirarchies, this gets
8
+ # all events under something.happened
9
+ # subscribe 'something.happened.*', :other_handler
10
+
11
+ def handler(name, data)
12
+ name == 'slnky.service.test' && data.hello == 'world!'
12
13
  end
13
14
  end
14
15
  end
@@ -15,4 +15,4 @@ env = ARGV[1]
15
15
  # Become a daemon
16
16
  Daemons.daemonize if env == 'production'
17
17
 
18
- Slnky::Service::<%= name.capitalize %>.new(ENV['SLNKY_URL'], env).start
18
+ Slnky::Service::<%= name.capitalize %>.new(ENV['SLNKY_URL'], env: env).start
@@ -1,7 +1,10 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Slnky::Service::<%= name.capitalize %> do
4
- it 'does something useful' do
5
- expect(false).to eq(true)
4
+ subject { described_class.new("http://localhost:3000", test_config) }
5
+ let(:test_event) { event_load('test')}
6
+
7
+ it 'handles event' do
8
+ expect(subject.handler(test_event.name, test_event.payload)).to eq(true)
6
9
  end
7
10
  end
@@ -1,2 +1,28 @@
1
1
  $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
2
+ require 'slnky'
2
3
  require 'slnky/service/<%= name %>'
4
+ require 'yaml'
5
+ require 'erb'
6
+ require 'tilt'
7
+
8
+ require 'dotenv'
9
+ @dotenv = Dotenv.load
10
+
11
+ def event_load(name)
12
+ @events ||= {}
13
+ @events[name] ||= begin
14
+ file = File.expand_path("../../test/events/#{name}.json", __FILE__)
15
+ raise "file #{file} not found" unless File.exists?(file)
16
+ Slnky::Message.new(JSON.parse(File.read(file)))
17
+ end
18
+ end
19
+
20
+ def test_config
21
+ @config ||= begin
22
+ file = File.expand_path("../../test/config.yaml", __FILE__)
23
+ template = Tilt.new(file)
24
+ output = template.render(self, @dotenv)
25
+ YAML.load(output)
26
+ end
27
+ end
28
+
@@ -0,0 +1,7 @@
1
+ ---
2
+ # config.yaml for Slnky::Service::Test
3
+ # this file is processed through ERB, you can inject
4
+ # values into the config from the environment, by specifying them
5
+ # in the .env file
6
+ environment: development # should specify this
7
+ service: test # just an example
@@ -0,0 +1,7 @@
1
+ ---
2
+ # config.yaml for Slnky::Service::<%= name.capitalize %>
3
+ # this file is processed through ERB, you can inject
4
+ # values into the config from the environment, by specifying them
5
+ # in the .env file
6
+ environment: development # should specify this
7
+ service: <%= name %> # just an example
@@ -0,0 +1,6 @@
1
+ {
2
+ "name": "slnky.service.test",
3
+ "payload": {
4
+ "hello": "world!"
5
+ }
6
+ }
data/lib/slnky/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Slnky
2
- VERSION = "0.3.5"
2
+ VERSION = "0.4.0"
3
3
  end
data/slnky.gemspec CHANGED
@@ -9,9 +9,9 @@ Gem::Specification.new do |spec|
9
9
  spec.authors = ["Shawn Catanzarite"]
10
10
  spec.email = ["me@shawncatz.com"]
11
11
 
12
- spec.summary = %q{core slnky lib}
13
- spec.description = %q{core slnky lib}
14
- spec.homepage = "https://github.com/shawncatz/slnky"
12
+ spec.summary = %q{core slnky lib and command line}
13
+ spec.description = %q{core slnky lib and command line}
14
+ spec.homepage = "https://github.com/slnky/slnky-cli"
15
15
  spec.license = "MIT"
16
16
 
17
17
  # # Prevent pushing this gem to RubyGems.org by setting 'allowed_push_host', or
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: slnky
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.5
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shawn Catanzarite
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-03-05 00:00:00.000000000 Z
11
+ date: 2016-03-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -122,7 +122,7 @@ dependencies:
122
122
  - - "~>"
123
123
  - !ruby/object:Gem::Version
124
124
  version: 2.0.2
125
- description: core slnky lib
125
+ description: core slnky lib and command line
126
126
  email:
127
127
  - me@shawncatz.com
128
128
  executables:
@@ -152,6 +152,10 @@ files:
152
152
  - lib/slnky/generator/service.rb
153
153
  - lib/slnky/message.rb
154
154
  - lib/slnky/service.rb
155
+ - lib/slnky/service/exchanges.rb
156
+ - lib/slnky/service/periodics.rb
157
+ - lib/slnky/service/queues.rb
158
+ - lib/slnky/service/subscriptions.rb
155
159
  - lib/slnky/template/service/.env.sample
156
160
  - lib/slnky/template/service/.gitignore.erb
157
161
  - lib/slnky/template/service/.rspec
@@ -162,16 +166,19 @@ files:
162
166
  - lib/slnky/template/service/config/deploy.rb.erb
163
167
  - lib/slnky/template/service/config/deploy/production.rb
164
168
  - lib/slnky/template/service/config/deploy/staging.rb
165
- - lib/slnky/template/service/deploy.env
169
+ - lib/slnky/template/service/deploy.env.erb
166
170
  - lib/slnky/template/service/lib/capistrano/tasks/NAME.rake.erb
167
171
  - lib/slnky/template/service/lib/slnky/service/NAME.rb.erb
168
172
  - lib/slnky/template/service/log/.keep
169
173
  - lib/slnky/template/service/service-slnky-NAME.erb
170
174
  - lib/slnky/template/service/spec/slnky/service/NAME_spec.rb.erb
171
175
  - lib/slnky/template/service/spec/spec_helper.rb.erb
176
+ - lib/slnky/template/service/test/config.yaml
177
+ - lib/slnky/template/service/test/config.yaml.erb
178
+ - lib/slnky/template/service/test/events/test.json
172
179
  - lib/slnky/version.rb
173
180
  - slnky.gemspec
174
- homepage: https://github.com/shawncatz/slnky
181
+ homepage: https://github.com/slnky/slnky-cli
175
182
  licenses:
176
183
  - MIT
177
184
  metadata: {}
@@ -194,5 +201,5 @@ rubyforge_project:
194
201
  rubygems_version: 2.4.7
195
202
  signing_key:
196
203
  specification_version: 4
197
- summary: core slnky lib
204
+ summary: core slnky lib and command line
198
205
  test_files: []