odania 0.0.15 → 0.0.16

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 (48) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +19 -1
  3. data/config/varnish_config.json +5 -0
  4. data/features/plugin.feature +35 -0
  5. data/features/step_definitions/plugin_steps.rb +79 -0
  6. data/features/step_definitions/varnish_steps.rb +7 -0
  7. data/features/support/env.rb +1 -0
  8. data/features/varnish.feature +8 -0
  9. data/lib/odania/consul.rb +116 -6
  10. data/lib/odania/plugin.rb +57 -31
  11. data/lib/odania/plugin_config/models/backend.rb +31 -0
  12. data/lib/odania/plugin_config/models/backend_group.rb +38 -0
  13. data/lib/odania/plugin_config/models/domain.rb +38 -0
  14. data/lib/odania/plugin_config/models/layout.rb +31 -0
  15. data/lib/odania/plugin_config/models/page.rb +23 -0
  16. data/lib/odania/plugin_config/models/redirect.rb +20 -0
  17. data/lib/odania/plugin_config/models/sub_domain.rb +99 -0
  18. data/lib/odania/plugin_config/plugin_config.rb +247 -0
  19. data/lib/odania/template/asset.rb +15 -0
  20. data/lib/odania/template/config.rb +15 -0
  21. data/lib/odania/template/page.rb +16 -0
  22. data/lib/odania/template.rb +8 -0
  23. data/lib/odania/varnish/generators/generate_backend_vcl.rb +19 -0
  24. data/lib/odania/varnish/generators/generate_catch_all_vcl.rb +17 -0
  25. data/lib/odania/varnish/generators/generate_default_vcl.rb +17 -0
  26. data/lib/odania/varnish/generators/generate_general_vcl.rb +17 -0
  27. data/lib/odania/varnish/generators/generate_redirects_vcl.rb +18 -0
  28. data/lib/odania/varnish/generators/generate_site_assets_vcl.rb +27 -0
  29. data/lib/odania/varnish/generators/generate_site_vcl.rb +25 -0
  30. data/lib/odania/varnish/generators/generate_sites_vcl.rb +31 -0
  31. data/lib/odania/varnish.rb +88 -0
  32. data/lib/odania/version.rb +1 -1
  33. data/lib/odania.rb +17 -20
  34. data/odania.gemspec +3 -0
  35. data/spec/odania/odania_spec.rb +0 -2
  36. data/tasks/odania.rake +12 -0
  37. data/templates/varnish/backend.vcl.erb +43 -0
  38. data/templates/varnish/catch_all.vcl.erb +4 -0
  39. data/templates/varnish/default.vcl.erb +16 -0
  40. data/templates/varnish/general.vcl.erb +95 -0
  41. data/templates/varnish/redirects.vcl.erb +11 -0
  42. data/templates/varnish/site.vcl.erb +50 -0
  43. data/templates/varnish/site_assets.vcl.erb +24 -0
  44. data/templates/varnish/sites.vcl.erb +10 -0
  45. data/templates/varnish/websocker.vcl.erb +17 -0
  46. metadata +86 -5
  47. data/lib/odania/service.rb +0 -46
  48. data/spec/odania/plugin_spec.rb +0 -18
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 873375ded1a7a7134b41490f71ac169daff40d22
4
- data.tar.gz: 583057469c6cb16fb84bee391f0a0b5784ff9107
3
+ metadata.gz: d27b77aee7f7a7879b1bbee2a376f58f1654b812
4
+ data.tar.gz: c5ceaebf77e1071d407805175c951b16d46d5e3a
5
5
  SHA512:
6
- metadata.gz: 588cc513d44e2d505a7475685aa428aeb5ce91508faa55f02708e02155b262704372b7a8f5d915f8c551ece85df639507b70546b3706e51f75193a7fe0ab9718
7
- data.tar.gz: c2d6875df759483ca33e007fdbddacd157c00ea6a0ddf04512460707a9038b951c10797e48ea3adfd6012bebb0b8f41869136c60de9530257ebdf55c6ba0189b
6
+ metadata.gz: 1c1e8c3902d96ce0b74998f7755a71e4874e7597fcaba907842ce4227ef621fb0046415892249f067a58046555a0cabd927362d50b1ecf348a0a72495b709f9f
7
+ data.tar.gz: 0e8a2a31c990c247c550d224e2ff95c669d56df085c2993adabfa043d243029ca81db6cf04a39cdf722ada8da8da4a3181e7454bb86795a4333984025f82e3e2
data/Gemfile.lock CHANGED
@@ -1,20 +1,37 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- odania (0.0.15)
4
+ odania (0.0.16)
5
5
  diplomat
6
+ erubis
7
+ public_suffix
6
8
 
7
9
  GEM
8
10
  remote: https://rubygems.org/
9
11
  specs:
12
+ builder (3.2.2)
13
+ cucumber (2.1.0)
14
+ builder (>= 2.1.2)
15
+ cucumber-core (~> 1.3.0)
16
+ diff-lcs (>= 1.1.3)
17
+ gherkin3 (~> 3.1.0)
18
+ multi_json (>= 1.7.5, < 2.0)
19
+ multi_test (>= 0.1.2)
20
+ cucumber-core (1.3.0)
21
+ gherkin3 (~> 3.1.0)
10
22
  diff-lcs (1.2.5)
11
23
  diplomat (0.14.0)
12
24
  faraday (~> 0.9)
13
25
  json (~> 1.8)
26
+ erubis (2.7.0)
14
27
  faraday (0.9.2)
15
28
  multipart-post (>= 1.2, < 3)
29
+ gherkin3 (3.1.2)
16
30
  json (1.8.3)
31
+ multi_json (1.11.2)
32
+ multi_test (0.1.2)
17
33
  multipart-post (2.0.0)
34
+ public_suffix (1.5.2)
18
35
  rake (10.4.2)
19
36
  rspec (3.3.0)
20
37
  rspec-core (~> 3.3.0)
@@ -35,6 +52,7 @@ PLATFORMS
35
52
 
36
53
  DEPENDENCIES
37
54
  bundler (~> 1.5)
55
+ cucumber
38
56
  odania!
39
57
  rake
40
58
  rspec
@@ -0,0 +1,5 @@
1
+ {
2
+ "name": "odania-varnish",
3
+ "default": false,
4
+ "domains": {}
5
+ }
@@ -0,0 +1,35 @@
1
+ Feature: Plugins
2
+ I can register and query all available plugins for the odania portal.
3
+
4
+ Scenario: Registering a new plugin
5
+ Given I initialize the gem
6
+ When I registered the plugin "test-plugin"
7
+ Then I should see "1" instances of the plugin "test-plugin"
8
+
9
+ Scenario: Registering multiple instances of one plugin
10
+ Given I initialize the gem
11
+ When I registered the plugin "test-plugin"
12
+ And I registered the plugin "test-plugin"
13
+ And I registered the plugin "test-plugin"
14
+ Then I should see "3" instances of the plugin "test-plugin"
15
+
16
+ Scenario: Registering multiple plugins
17
+ Given I initialize the gem
18
+ When I registered the plugin "test-plugin"
19
+ And I registered the plugin "test-plugin"
20
+ And I registered the plugin "test-plugin-2"
21
+ Then I should see "2" instances of the plugin "test-plugin"
22
+ And I should see "1" instances of the plugin "test-plugin-2"
23
+
24
+ Scenario: Retrieving plugin instance name
25
+ Given I initialize the gem
26
+ When I retrieve plugin instance name for "test-plugin"
27
+ And I retrieve plugin instance name for "test-plugin"
28
+ Then I should retrieve "test-plugin_1"
29
+
30
+ Scenario: Build new joined plugin config
31
+ Given I initialize the gem
32
+ When I registered the plugin "test-plugin"
33
+ And I generate the joined plugin config
34
+ Then I have a joined plugin config
35
+ And The joined plugin config contains the plugin "test-plugin"
@@ -0,0 +1,79 @@
1
+ $instances = Hash.new(0)
2
+
3
+ Given(/^I initialize the gem$/) do
4
+ $instances = Hash.new(0)
5
+ `rm -f #{Odania::Plugin::INSTANCE_FILES_PATH}*`
6
+
7
+ Odania.plugin.get_all.each_pair do |name, instances|
8
+ instances.each do |instance|
9
+ if instance.ServiceName.start_with? 'test-plugin'
10
+ puts "Deregister: #{instance}" if $debug
11
+ Odania.plugin.deregister instance.ServiceID
12
+ end
13
+ end
14
+ end
15
+ end
16
+
17
+ Given(/^I registered the plugin "([^"]*)"$/) do |plugin_name|
18
+ plugin_cfg = {
19
+ 'name' => plugin_name,
20
+ 'ip' => '127.0.0.1',
21
+ 'port' => 80,
22
+ 'tags' => ["plugin-#{plugin_name}"],
23
+ 'default_subdomains' => {
24
+ '_general' => 'default_sub'
25
+ },
26
+ 'domains' => {
27
+ 'odania.com' => {
28
+ 'contents' => {
29
+ },
30
+ 'assets' => {
31
+ },
32
+ 'config' => {
33
+ 'asset_url' => 'assets.odania.com',
34
+ 'languages' => %w(de en),
35
+ 'layout' => 'simple',
36
+ 'language_selector' => 'prefix',
37
+ 'default_subdomain' => 'page_sub',
38
+ 'redirects' => {
39
+ '^/test' => '/games'
40
+ }
41
+ },
42
+ 'public_pages' => {
43
+ }
44
+ }
45
+ },
46
+ 'layouts' => {
47
+ }
48
+ }
49
+
50
+ $instances[plugin_name] += 1
51
+ plugin_instance_name = "#{plugin_name}_#{$instances[plugin_name]}"
52
+ Odania.plugin.register plugin_instance_name, plugin_cfg
53
+ end
54
+
55
+ Then(/^I should see "([^"]*)" instances of the plugin "([^"]*)"$/) do |amount, plugin_name|
56
+ plugins = Odania.plugin.get_all
57
+ expect(plugins[plugin_name].count).to eq(amount.to_i)
58
+ end
59
+
60
+ When(/^I retrieve plugin instance name for "([^"]*)"$/) do |plugin_name|
61
+ @plugin_instance_name = Odania.plugin.get_plugin_instance_name plugin_name
62
+ end
63
+
64
+ Then(/^I should retrieve "([^"]*)"$/) do |plugin_instance_name|
65
+ expect(plugin_instance_name).to eq(@plugin_instance_name)
66
+ end
67
+
68
+ When(/^I generate the joined plugin config$/) do
69
+ Odania.plugin.plugin_config.load_from_consul
70
+ Odania.plugin.plugin_config.generate
71
+ end
72
+
73
+ Then(/^I have a joined plugin config$/) do
74
+ pending # Write code here that turns the phrase above into concrete actions
75
+ end
76
+
77
+ Then(/^The joined plugin config contains the plugin "([^"]*)"$/) do |arg1|
78
+ pending # Write code here that turns the phrase above into concrete actions
79
+ end
@@ -0,0 +1,7 @@
1
+ When(/^I generate the varnish config$/) do
2
+ Odania.varnish.generate('/tmp/varnish')
3
+ end
4
+
5
+ Then(/^I have a valid varnish config$/) do
6
+ pending # Write code here that turns the phrase above into concrete actions
7
+ end
@@ -0,0 +1 @@
1
+ require 'odania'
@@ -0,0 +1,8 @@
1
+ Feature: Varnish
2
+ Configuration for varnish. It is the gateway from the web to the plugin infrastructure.
3
+
4
+ Scenario: Generate config
5
+ Given I initialize the gem
6
+ When I registered the plugin "test-plugin"
7
+ And I generate the varnish config
8
+ Then I have a valid varnish config
data/lib/odania/consul.rb CHANGED
@@ -1,12 +1,122 @@
1
1
  module Odania
2
2
  class Consul
3
- protected
3
+ attr_accessor :service, :config, :event, :health
4
4
 
5
- def retrieve_value(plugin_path)
6
- begin
7
- Diplomat::Kv.get(plugin_path, :recurse => true)
8
- rescue Diplomat::KeyNotFound
9
- []
5
+ def initialize(consul_url)
6
+ consul_url = "http://#{ENV['CONSUL_PORT_8500_TCP_ADDR']}:#{ENV['CONSUL_PORT_8500_TCP_PORT']}" if consul_url.nil?
7
+ puts "Consul URL: #{consul_url}" if $debug
8
+ Diplomat.configure do |config|
9
+ # Set up a custom Consul URL
10
+ config.url = consul_url
11
+ end
12
+
13
+ @service = Service.new
14
+ @config = Config.new
15
+ @event = Event.new
16
+ @health = Health.new
17
+ end
18
+
19
+ class Config
20
+ def get(path)
21
+ begin
22
+ JSON.parse Diplomat::Kv.get path
23
+ rescue Diplomat::KeyNotFound
24
+ nil
25
+ end
26
+ end
27
+
28
+ def get_all(path)
29
+ retrieve_value path
30
+ end
31
+
32
+ def set(key, value)
33
+ Diplomat::Kv.put(key, JSON.dump(value))
34
+ end
35
+
36
+ def delete(key)
37
+ Diplomat::Kv.delete(key)
38
+ end
39
+
40
+ protected
41
+
42
+ def retrieve_value(plugin_path)
43
+ begin
44
+ result = {}
45
+ Diplomat::Kv.get(plugin_path, :recurse => true).each do |data|
46
+ result[data[:key]] = JSON.parse data[:value]
47
+ end
48
+ result
49
+ rescue Diplomat::KeyNotFound
50
+ {}
51
+ end
52
+ end
53
+ end
54
+
55
+ class Service
56
+ def get_all
57
+ services = {}
58
+ Diplomat::Service.get_all.each_pair do |key, value|
59
+ services[key.to_s] = get_all_for(key)
60
+ end
61
+ puts "SERVICES: #{JSON.pretty_generate services}" if $debug
62
+ services
63
+ end
64
+
65
+ def get_all_for(plugin_name)
66
+ instances = get(plugin_name, :all)
67
+ instances.is_a?(Array) ? instances : [instances]
68
+ end
69
+
70
+ def get(key, scope=:first)
71
+ Diplomat::Service.get(key, scope)
72
+ end
73
+
74
+ def register(consul_config)
75
+ if Diplomat::Service.register consul_config
76
+ puts 'Service registered' if $debug
77
+ else
78
+ puts 'Error registering service' if $debug
79
+ end
80
+ end
81
+
82
+ def deregister(name)
83
+ Diplomat::Service.deregister name
84
+ end
85
+
86
+ def build_config(plugin_name, plugin_instance_name, ip, tags=[], port=80)
87
+ {
88
+ 'id' => plugin_instance_name,
89
+ 'name' => plugin_name,
90
+ 'tags' => tags,
91
+ 'address' => ip,
92
+ 'port' => port,
93
+ 'token' => plugin_instance_name,
94
+ 'checks' => [
95
+ {
96
+ 'id' => plugin_name,
97
+ 'name' => "HTTP on port #{port}",
98
+ 'http' => "http://#{ip}:#{port}/health",
99
+ 'interval' => '10s',
100
+ 'timeout' => '1s'
101
+ }
102
+ ]
103
+ }
104
+ end
105
+ end
106
+
107
+ class Event
108
+ def fire(key, value)
109
+ Diplomat::Event.fire key, value
110
+ end
111
+ end
112
+
113
+ class Health
114
+ def state(state=:any)
115
+ Diplomat::Health.state(state)
116
+ end
117
+
118
+ def service(name)
119
+ Diplomat::Health.service(name)
10
120
  end
11
121
  end
12
122
  end
data/lib/odania/plugin.rb CHANGED
@@ -1,56 +1,82 @@
1
- module Odania
2
- class Plugin < Odania::Consul
3
- def plugins_config
4
- configs = retrieve_value 'plugins'
5
- configs = [configs] unless configs.kind_of?(Array)
1
+ require_relative 'plugin_config/plugin_config'
6
2
 
7
- result = []
8
- configs.each do |json_data|
9
- begin
10
- result << JSON.parse(json_data[:value])
11
- rescue => e
12
- puts "Can not parse config: #{e} \n\n #{json_data.inspect}"
13
- end
14
- end
3
+ module Odania
4
+ class Plugin
5
+ INSTANCE_FILES_PATH = '/tmp/plugin_instance_name_'
15
6
 
16
- result
7
+ def initialize(consul)
8
+ @consul = consul
9
+ @plugin_config = PluginConfig::Base.new
17
10
  end
18
11
 
19
- def add_plugin(plugin_config)
12
+ def register(plugin_instance_name, plugin_config)
20
13
  plugin_name = plugin_config['name']
21
- plugin_instance_name = get_plugin_instance_name plugin_name
22
14
 
23
- puts "Writing plugin instance config: #{plugin_instance_name}"
24
- Diplomat::Kv.put("#{get_plugin_path(plugin_name)}#{plugin_instance_name}", JSON.dump(plugin_config))
15
+ # Write configuration of the plugin
16
+ @consul.config.set(get_plugin_config_path_for(plugin_name), plugin_config)
25
17
 
26
- consul_config = Odania.service.consul_service_config(plugin_name, plugin_instance_name, plugin_config['ip'], plugin_config['tags'], plugin_config['port'])
27
- Odania.service.register_service(consul_config)
18
+ # Register service
19
+ consul_config = @consul.service.build_config(plugin_name, plugin_instance_name, plugin_config['ip'], plugin_config['tags'], plugin_config['port'])
20
+ @consul.service.register(consul_config)
28
21
 
29
- Diplomat::Event.fire('updated_plugin_config', "#{plugin_name}|#{plugin_instance_name}")
22
+ # Fire event
23
+ @consul.event.fire 'service-registered', "#{plugin_name}|#{plugin_instance_name}"
30
24
  end
31
25
 
32
- private
26
+ def deregister(plugin_instance_name)
27
+ @consul.service.deregister(plugin_instance_name)
28
+ end
29
+
30
+ def get_all
31
+ @consul.service.get_all
32
+ end
33
33
 
34
- def get_plugin_path(plugin_name)
35
- "plugins/#{plugin_name}/"
34
+ def config_for(plugin_name)
35
+ @consul.config.get get_plugin_config_path_for(plugin_name)
36
36
  end
37
37
 
38
38
  # Generate a unique number for this instance of the plugin
39
39
  def get_plugin_instance_name(plugin_name)
40
- puts 'Detecting plugin instance name'
41
- plugin_instance_name_file = '/tmp/plugin_instance_name'
40
+ plugin_instance_name_file = "#{INSTANCE_FILES_PATH}#{plugin_name}"
42
41
 
43
42
  plugin_instance_name = nil
44
43
  plugin_instance_name = File.read plugin_instance_name_file if File.exists? plugin_instance_name_file
45
44
  return plugin_instance_name unless plugin_instance_name.nil?
46
45
 
47
- available_plugins = retrieve_value(get_plugin_path(plugin_name))
48
- puts 'Current plugins'
49
- puts available_plugins.inspect
50
-
51
- plugin_instance_name = "#{plugin_name}_#{available_plugins.length + 1}"
46
+ available_instances = @consul.service.get_all_for plugin_name
47
+ plugin_instance_name = "#{plugin_name}_#{available_instances.length + 1}"
52
48
  File.write plugin_instance_name_file, plugin_instance_name
53
49
  plugin_instance_name
54
50
  end
51
+
52
+ def plugin_config
53
+ @plugin_config
54
+ end
55
+
56
+ def set_global_config(config)
57
+ @consul.config.set get_global_plugin_config_path, config
58
+ end
59
+
60
+ def get_global_config
61
+ @consul.config.get get_global_plugin_config_path
62
+ end
63
+
64
+ def health
65
+ @consul.health
66
+ end
67
+
68
+ private
69
+
70
+ def get_global_plugin_config_path
71
+ 'global_plugins_config'
72
+ end
73
+
74
+ def get_plugin_config_path
75
+ 'plugins_config'
76
+ end
77
+
78
+ def get_plugin_config_path_for(plugin_name)
79
+ "#{get_plugin_config_path}/#{plugin_name}"
80
+ end
55
81
  end
56
82
  end
@@ -0,0 +1,31 @@
1
+ module Odania
2
+ module PluginConfig
3
+ class Backend
4
+ attr_accessor :service_name, :instance_name, :host, :port
5
+
6
+ def initialize(service_name, instance_name, host, port)
7
+ self.service_name = service_name
8
+ self.instance_name = instance_name
9
+ self.host = host
10
+ self.port = port
11
+ end
12
+
13
+ def dump
14
+ {
15
+ 'service_name' => service_name,
16
+ 'instance_name' => instance_name,
17
+ 'host' => host,
18
+ 'port' => port
19
+ }
20
+ end
21
+
22
+ def load(data)
23
+ self.service_name = data['service_name']
24
+ self.instance_name = data['instance_name']
25
+ self.host = data['host']
26
+ self.port = data['port']
27
+ self
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,38 @@
1
+ module Odania
2
+ module PluginConfig
3
+ class BackendGroup
4
+ attr_accessor :name, :backends
5
+
6
+ def initialize(name, backends=[])
7
+ self.name = name
8
+ self.backends = backends
9
+ end
10
+
11
+ def add_backend(backend)
12
+ self.backends << backend
13
+ end
14
+
15
+ def dump
16
+ backend_data = []
17
+ backends.each do |backend|
18
+ backend_data << backend.dump
19
+ end
20
+
21
+ {
22
+ 'name' => name,
23
+ 'backends' => backend_data
24
+ }
25
+ end
26
+
27
+ def load(data)
28
+ self.name = data['name']
29
+
30
+ unless data['backends'].nil?
31
+ data['backends'].each do |data|
32
+ self.backends << Backend.new.load(data)
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,38 @@
1
+ module Odania
2
+ module PluginConfig
3
+ class Domain
4
+ attr_accessor :name, :subdomains
5
+
6
+ def initialize(name)
7
+ self.name = name
8
+ self.subdomains = Hash.new { |hash, key| hash[key] = SubDomain.new(key) }
9
+ end
10
+
11
+ def add_subdomain(subdomain_name)
12
+ self.subdomains[subdomain_name]
13
+ end
14
+
15
+ def dump
16
+ subdomain_data = {}
17
+ subdomains.each_pair do |name, subdomain|
18
+ subdomain_data[name] = subdomain.dump
19
+ end
20
+
21
+ {
22
+ 'name' => name,
23
+ 'subdomains' => subdomain_data
24
+ }
25
+ end
26
+
27
+ def load(data)
28
+ self.name = data['name']
29
+
30
+ unless data['subdomains'].nil?
31
+ data['subdomains'].each_pair do |name, data|
32
+ self.subdomains[name].load(data)
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,31 @@
1
+ module Odania
2
+ module PluginConfig
3
+ class Layout
4
+ attr_accessor :config
5
+
6
+ def initialize
7
+ @plugins = []
8
+ end
9
+
10
+ def set_config(config, plugin_name)
11
+ result = true
12
+ self.config = config
13
+ result = false unless @plugins.empty?
14
+ @plugins << plugin_name
15
+ result
16
+ end
17
+
18
+ def plugins
19
+ @plugins
20
+ end
21
+
22
+ def dump
23
+ config
24
+ end
25
+
26
+ def load(data)
27
+ self.config = data
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,23 @@
1
+ module Odania
2
+ module PluginConfig
3
+ class Page
4
+ attr_accessor :group_name, :plugin_url
5
+
6
+ def dump
7
+ {
8
+ 'group_name' => group_name,
9
+ 'plugin_url' => plugin_url
10
+ }
11
+ end
12
+
13
+ def load(data)
14
+ self.group_name = data['group_name']
15
+ self.plugin_url = data['plugin_url']
16
+ end
17
+
18
+ def director
19
+ "#{Odania.varnish_sanitize(group_name)}_director"
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,20 @@
1
+ module Odania
2
+ module PluginConfig
3
+ class Redirect
4
+ attr_accessor :src, :target, :plugins
5
+
6
+ def initialize(src)
7
+ self.src = src
8
+ self.plugins = []
9
+ end
10
+
11
+ def add(target, plugin_name)
12
+ self.target = target
13
+ self.plugins << plugin_name
14
+
15
+ return false if self.plugins.count > 1
16
+ true
17
+ end
18
+ end
19
+ end
20
+ end