killbill 3.2.4 → 4.0.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.
Files changed (117) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +0 -2
  3. data/.travis.yml +26 -5
  4. data/Gemfile +1 -1
  5. data/Gemfile.head +5 -0
  6. data/Gemfile.lock +125 -0
  7. data/Jarfile +9 -9
  8. data/Jarfile.lock +54 -0
  9. data/NEWS +3 -0
  10. data/README.md +13 -0
  11. data/VERSION +1 -1
  12. data/generators/active_merchant/templates/.gitignore.rb +2 -3
  13. data/generators/active_merchant/templates/.travis.yml.rb +22 -3
  14. data/generators/active_merchant/templates/Gemfile.head.rb +5 -0
  15. data/generators/active_merchant/templates/Gemfile.rb +1 -1
  16. data/generators/active_merchant/templates/Jarfile.rb +9 -8
  17. data/generators/active_merchant/templates/LICENSE.rb +2 -2
  18. data/generators/active_merchant/templates/config.yml.rb +16 -6
  19. data/generators/active_merchant/templates/lib/plugin.rb +0 -1
  20. data/generators/active_merchant/templates/plugin.gemspec.rb +17 -15
  21. data/generators/active_merchant/templates/pom.xml.rb +1 -1
  22. data/generators/active_merchant/templates/release.sh.rb +34 -14
  23. data/generators/active_merchant/templates/spec/base_plugin_spec.rb +5 -7
  24. data/generators/active_merchant/templates/spec/integration_spec.rb +7 -18
  25. data/killbill.gemspec +21 -15
  26. data/lib/killbill/ext/active_merchant/typhoeus_connection.rb +3 -4
  27. data/lib/killbill/gen/api/account.rb +1 -1
  28. data/lib/killbill/gen/api/account_data.rb +1 -1
  29. data/lib/killbill/gen/api/audit_log.rb +2 -2
  30. data/lib/killbill/gen/api/audit_user_api.rb +3 -3
  31. data/lib/killbill/gen/api/block.rb +1 -1
  32. data/lib/killbill/gen/api/blocking_state.rb +1 -1
  33. data/lib/killbill/gen/api/call_context.rb +2 -2
  34. data/lib/killbill/gen/api/control_tag.rb +2 -2
  35. data/lib/killbill/gen/api/currency_conversion.rb +1 -1
  36. data/lib/killbill/gen/api/currency_conversion_api.rb +2 -2
  37. data/lib/killbill/gen/api/custom_field.rb +1 -1
  38. data/lib/killbill/gen/api/custom_field_user_api.rb +2 -2
  39. data/lib/killbill/gen/api/dry_run_arguments.rb +22 -3
  40. data/lib/killbill/gen/api/duration.rb +1 -1
  41. data/lib/killbill/gen/api/entitlement.rb +3 -3
  42. data/lib/killbill/gen/api/entitlement_ao_status_dry_run.rb +3 -3
  43. data/lib/killbill/gen/api/entitlement_api.rb +25 -7
  44. data/lib/killbill/gen/api/fixed.rb +1 -1
  45. data/lib/killbill/gen/api/invoice.rb +1 -1
  46. data/lib/killbill/gen/api/invoice_formatter.rb +2 -2
  47. data/lib/killbill/gen/api/invoice_item.rb +2 -2
  48. data/lib/killbill/gen/api/invoice_item_formatter.rb +2 -2
  49. data/lib/killbill/gen/api/invoice_payment.rb +3 -3
  50. data/lib/killbill/gen/api/invoice_user_api.rb +2 -2
  51. data/lib/killbill/gen/api/migration_plan.rb +1 -1
  52. data/lib/killbill/gen/api/mutable_account_data.rb +1 -1
  53. data/lib/killbill/gen/api/payment.rb +1 -1
  54. data/lib/killbill/gen/api/payment_api.rb +11 -11
  55. data/lib/killbill/gen/api/payment_transaction.rb +4 -4
  56. data/lib/killbill/gen/api/plan.rb +1 -1
  57. data/lib/killbill/gen/api/plan_change_result.rb +2 -2
  58. data/lib/killbill/gen/api/plan_phase.rb +1 -1
  59. data/lib/killbill/gen/api/plan_phase_price_override.rb +93 -0
  60. data/lib/killbill/gen/api/plan_phase_price_overrides_with_call_context.rb +77 -0
  61. data/lib/killbill/gen/api/plan_phase_specifier.rb +3 -3
  62. data/lib/killbill/gen/api/plan_specifier.rb +2 -2
  63. data/lib/killbill/gen/api/price.rb +1 -1
  64. data/lib/killbill/gen/api/product.rb +1 -1
  65. data/lib/killbill/gen/api/rate.rb +2 -2
  66. data/lib/killbill/gen/api/record_id_api.rb +1 -1
  67. data/lib/killbill/gen/api/recurring.rb +1 -1
  68. data/lib/killbill/gen/api/refund.rb +2 -2
  69. data/lib/killbill/gen/api/require_gen.rb +2 -0
  70. data/lib/killbill/gen/api/static_catalog.rb +2 -2
  71. data/lib/killbill/gen/api/subscription.rb +3 -3
  72. data/lib/killbill/gen/api/subscription_event.rb +3 -3
  73. data/lib/killbill/gen/api/tag.rb +1 -1
  74. data/lib/killbill/gen/api/tag_definition.rb +1 -1
  75. data/lib/killbill/gen/api/tag_user_api.rb +6 -6
  76. data/lib/killbill/gen/api/tiered_block.rb +1 -1
  77. data/lib/killbill/gen/api/usage.rb +3 -3
  78. data/lib/killbill/gen/plugin-api/currency_plugin_api.rb +1 -1
  79. data/lib/killbill/gen/plugin-api/ext_bus_event.rb +2 -2
  80. data/lib/killbill/gen/plugin-api/payment_transaction_info_plugin.rb +3 -3
  81. data/lib/killbill/helpers/active_merchant.rb +2 -2
  82. data/lib/killbill/helpers/active_merchant/active_record.rb +1 -1
  83. data/lib/killbill/helpers/active_merchant/active_record/active_record_helper.rb +14 -0
  84. data/lib/killbill/helpers/active_merchant/active_record/models/helpers.rb +8 -0
  85. data/lib/killbill/helpers/active_merchant/active_record/models/payment_method.rb +10 -8
  86. data/lib/killbill/helpers/active_merchant/active_record/models/response.rb +16 -3
  87. data/lib/killbill/helpers/active_merchant/active_record/models/streamy_result_set.rb +1 -3
  88. data/lib/killbill/helpers/active_merchant/active_record/models/transaction.rb +2 -0
  89. data/lib/killbill/helpers/active_merchant/configuration.rb +119 -27
  90. data/lib/killbill/helpers/active_merchant/gateway.rb +40 -27
  91. data/lib/killbill/helpers/active_merchant/killbill_spec_helper.rb +63 -45
  92. data/lib/killbill/helpers/active_merchant/payment_plugin.rb +121 -95
  93. data/lib/killbill/helpers/active_merchant/private_payment_plugin.rb +1 -1
  94. data/lib/killbill/helpers/active_merchant/properties.rb +9 -2
  95. data/lib/killbill/helpers/active_merchant/sinatra.rb +4 -8
  96. data/lib/killbill/helpers/active_merchant/utils.rb +44 -0
  97. data/lib/killbill/helpers/properties_helper.rb +41 -0
  98. data/lib/killbill/http_servlet.rb +11 -4
  99. data/lib/killbill/killbill_api.rb +6 -4
  100. data/lib/killbill/rake_task.rb +542 -102
  101. data/spec/killbill/helpers/configuration_spec.rb +117 -33
  102. data/spec/killbill/helpers/payment_method_spec.rb +20 -17
  103. data/spec/killbill/helpers/payment_plugin_spec.rb +401 -201
  104. data/spec/killbill/helpers/private_payment_plugin_spec.rb +3 -16
  105. data/spec/killbill/helpers/response_spec.rb +92 -22
  106. data/spec/killbill/helpers/streamy_result_set_spec.rb +3 -2
  107. data/spec/killbill/helpers/test_payment_method.rb +9 -0
  108. data/spec/killbill/helpers/test_response.rb +11 -0
  109. data/spec/killbill/helpers/test_schema.rb +6 -6
  110. data/spec/killbill/helpers/test_transaction.rb +11 -0
  111. data/spec/killbill/helpers/transaction_spec.rb +3 -13
  112. data/spec/killbill/helpers/utils_spec.rb +127 -69
  113. data/spec/spec_helper.rb +69 -18
  114. metadata +77 -71
  115. data/lib/killbill/ext/active_merchant/jdbc_connection.rb +0 -92
  116. data/lib/killbill/ext/active_merchant/proxy_support.rb +0 -58
  117. data/spec/killbill/helpers/gateway_spec.rb +0 -36
@@ -87,7 +87,7 @@ module Killbill
87
87
 
88
88
  def gateway(payment_processor_account_id=:default, kb_tenant_id=nil)
89
89
  gateway = ::Killbill::Plugin::ActiveMerchant.gateways(kb_tenant_id)[payment_processor_account_id.to_sym]
90
- raise "Unable to lookup gateway for payment_processor_account_id #{payment_processor_account_id}, , kb_tenant_id = #{kb_tenant_id}, gateways: #{::Killbill::Plugin::ActiveMerchant.gateways(kb_tenant_id)}" if gateway.nil?
90
+ raise "Unable to lookup gateway for payment_processor_account_id #{payment_processor_account_id}, kb_tenant_id = #{kb_tenant_id}, gateways: #{::Killbill::Plugin::ActiveMerchant.gateways(kb_tenant_id)}" if gateway.nil?
91
91
  gateway
92
92
  end
93
93
 
@@ -1,3 +1,6 @@
1
+ require 'erb'
2
+ require 'yaml'
3
+
1
4
  module Killbill
2
5
  module Plugin
3
6
  module ActiveMerchant
@@ -8,13 +11,17 @@ module Killbill
8
11
 
9
12
  def parse!
10
13
  raise "#{@config_file} is not a valid file" unless @config_file.file?
11
- @config = YAML.load_file(@config_file.to_s)
14
+ @config = YAML.load(ERB.new(File.read(@config_file.to_s)).result)
12
15
  end
13
16
 
14
17
  def [](key)
15
18
  @config[key]
16
19
  end
20
+
21
+ def to_hash
22
+ @config.dup
23
+ end
17
24
  end
18
25
  end
19
26
  end
20
- end
27
+ end
@@ -5,10 +5,11 @@ module Killbill
5
5
  enable :sessions
6
6
 
7
7
  include ::ActionView::Helpers::FormTagHelper
8
+ include ::Killbill::Plugin::ActiveMerchant::ActiveRecordHelper
8
9
 
9
10
  helpers do
10
- def config
11
- ::Killbill::Plugin::ActiveMerchant.config
11
+ def config(kb_tenant_id=nil)
12
+ ::Killbill::Plugin::ActiveMerchant.config(kb_tenant_id)
12
13
  end
13
14
 
14
15
  def logger
@@ -22,12 +23,7 @@ module Killbill
22
23
 
23
24
  after do
24
25
  # return DB connections to the Pool if required
25
- pool = ::ActiveRecord::Base.connection_pool
26
- if pool.active_connection?
27
- connection = ::ActiveRecord::Base.connection
28
- pool.remove(connection)
29
- connection.disconnect!
30
- end
26
+ close_connection(logger)
31
27
  end
32
28
  end
33
29
  end
@@ -31,6 +31,33 @@ module Killbill
31
31
  Socket.ip_address_list.detect { |intf| intf.ipv4? and !intf.ipv4_loopback? and !intf.ipv4_multicast? and !intf.ipv4_private? }
32
32
  end
33
33
 
34
+ # Note: we assume options only contains keys as symbols
35
+ def self.normalized(options, key)
36
+ if !options.has_key?(key)
37
+ # Be friendly with Java-style conventions
38
+ camelized_key = key.to_s.camelize(false).to_sym
39
+ options.has_key?(camelized_key) ? normalize(options[camelized_key]) : nil
40
+ else
41
+ normalize(options[key])
42
+ end
43
+ end
44
+
45
+ def self.normalize(value)
46
+ case value.respond_to?(:strip) ? value.strip : value
47
+ when 'true' then true
48
+ when :true then true
49
+ when 'yes' then true
50
+ when :yes then true
51
+ when 'false' then false
52
+ when :false then false
53
+ when 'no' then false
54
+ when :no then false
55
+ when '' then nil
56
+ when 'null' then nil
57
+ else value
58
+ end
59
+ end
60
+
34
61
  class KBWiredumpDevice < IO
35
62
 
36
63
  # Required for compatibility, but unused
@@ -48,6 +75,23 @@ module Killbill
48
75
  end
49
76
  end
50
77
 
78
+ class LazyEvaluator
79
+
80
+ def initialize(&instantiator)
81
+ @instantiator = instantiator
82
+ end
83
+
84
+ def method_missing(method, *args)
85
+ __instance_object__.send(method, *args)
86
+ end
87
+
88
+ private
89
+
90
+ def __instance_object__
91
+ @__instance_object__ ||= @instantiator.call
92
+ end
93
+ end
94
+
51
95
  class BoundedLRUCache
52
96
  include ThreadSafe::Util::CheapLockable
53
97
 
@@ -0,0 +1,41 @@
1
+ module Killbill
2
+ module Plugin
3
+ module PropertiesHelper
4
+
5
+ def find_value_from_properties(properties, key = nil)
6
+ return nil if key.nil?
7
+ prop = (properties.find { |kv| kv.key.to_s == key.to_s })
8
+ prop.nil? ? nil : prop.value
9
+ end
10
+
11
+ def hash_to_properties(options = {})
12
+ merge_properties([], options)
13
+ end
14
+
15
+ def properties_to_hash(properties, options = {})
16
+ merged = {}
17
+ (properties || []).each do |p|
18
+ merged[p.key.to_sym] = p.value
19
+ end
20
+ merged.merge(options)
21
+ end
22
+
23
+ def merge_properties(properties, options = {})
24
+ merged = properties_to_hash(properties, options)
25
+
26
+ properties = []
27
+ merged.each do |k, v|
28
+ properties << build_property(k, v)
29
+ end
30
+ properties
31
+ end
32
+
33
+ def build_property(key, value = nil)
34
+ prop = ::Killbill::Plugin::Model::PluginProperty.new
35
+ prop.key = key
36
+ prop.value = value
37
+ prop
38
+ end
39
+ end
40
+ end
41
+ end
@@ -55,7 +55,14 @@ module Killbill
55
55
  headers[name] = servlet_request.get_headers(name).to_a
56
56
  end
57
57
 
58
- response_status, response_headers, response_body = rack_service(request_uri, method, query_string, input, scheme, server_name, server_port, content_type, content_length, headers)
58
+ # Pass original attributes (e.g. to get access to killbill_tenant)
59
+ attributes = {}
60
+ servlet_request.attribute_names.each do |name|
61
+ value = servlet_request.get_attribute(name)
62
+ attributes[name] = value
63
+ end
64
+
65
+ response_status, response_headers, response_body = rack_service(request_uri, method, query_string, input, scheme, server_name, server_port, content_type, content_length, headers, attributes)
59
66
 
60
67
  # Set status
61
68
  servlet_response.status = response_status
@@ -80,10 +87,10 @@ module Killbill
80
87
  response_body.close if response_body.respond_to? :close
81
88
  end
82
89
 
83
- def rack_service(request_uri = '/', method = 'GET', query_string = '', input = '', scheme = 'http', server_name = 'localhost', server_port = 4567, content_type = 'text/plain', content_length = 0, headers = {})
90
+ def rack_service(request_uri = '/', method = 'GET', query_string = '', input = '', scheme = 'http', server_name = 'localhost', server_port = 4567, content_type = 'text/plain', content_length = 0, headers = {}, attributes = {})
84
91
  return 503, {}, [] if @app.nil?
85
92
 
86
- rack_env = {
93
+ rack_env = attributes.merge({
87
94
  'rack.version' => Rack::VERSION,
88
95
  'rack.multithread' => true,
89
96
  'rack.multiprocess' => false,
@@ -99,7 +106,7 @@ module Killbill
99
106
  'QUERY_STRING' => (query_string || ""),
100
107
  'SERVER_NAME' => server_name,
101
108
  'SERVER_PORT' => server_port.to_s
102
- }
109
+ })
103
110
 
104
111
  rack_env['CONTENT_TYPE'] = content_type unless content_type.nil?
105
112
  rack_env['CONTENT_LENGTH'] = content_length unless content_length.nil?
@@ -8,10 +8,14 @@ module Killbill
8
8
  #
9
9
  class KillbillApi
10
10
 
11
- def initialize(plugin_name, java_service_map)
11
+ # @VisibleForTesting
12
+ attr_reader :proxied_services
13
+
14
+ def initialize(plugin_name, proxied_services)
12
15
  @plugin_name = plugin_name
16
+ @proxied_services = proxied_services
13
17
  @services = {}
14
- java_service_map.each do |k,v|
18
+ proxied_services.each do |k,v|
15
19
  @services[k.to_sym] = create_proxy_api(k, v)
16
20
  end
17
21
  end
@@ -24,7 +28,6 @@ module Killbill
24
28
  raise NoMethodError.new("undefined method `#{m}' for #{self}")
25
29
  end
26
30
 
27
-
28
31
  def create_context(tenant_id=nil, user_token=nil, reason_code=nil, comments=nil)
29
32
  context = Killbill::Plugin::Model::CallContext.new
30
33
  context.tenant_id= tenant_id
@@ -45,7 +48,6 @@ module Killbill
45
48
  proxy_class_name = "Killbill::Plugin::Api::#{api_name.to_s.split('_').map{|e| e.capitalize}.join}"
46
49
  proxy_class_name.to_class.new(java_api)
47
50
  end
48
-
49
51
  end
50
52
  end
51
53
  end
@@ -1,8 +1,8 @@
1
1
  require 'bundler'
2
2
  require 'logger'
3
3
  require 'pathname'
4
+ require 'tmpdir'
4
5
  require 'rake'
5
- require 'rake/packagetask'
6
6
  require 'rubygems/installer'
7
7
 
8
8
  module Killbill
@@ -11,12 +11,13 @@ module Killbill
11
11
 
12
12
  class << self
13
13
  def install_tasks(opts = {})
14
- new(opts[:base_name] || Dir.pwd, # Path to the plugin root directory (where the gempec and/or Gemfile should be)
15
- opts[:plugin_name], # Plugin name, e.g. 'klogger'
16
- opts[:gem_name], # Gem file name, e.g. 'klogger-1.0.0.gem'
17
- opts[:gemfile_name] || "Gemfile", # Gemfile name
18
- opts[:gemfile_lock_name] || "Gemfile.lock", # Gemfile.lock name
19
- opts[:verbose] || false)
14
+ gemfile_name = ENV['BUNDLE_GEMFILE'] || 'Gemfile'
15
+ new(opts[:base_name] || Dir.pwd, # Path to the plugin root directory (where the gempec and/or Gemfile should be)
16
+ opts[:plugin_name], # Plugin name, e.g. 'klogger'
17
+ opts[:gem_name], # Gem file name, e.g. 'klogger-1.0.0.gem'
18
+ opts[:gemfile_name] || gemfile_name, # Gemfile name
19
+ opts[:gemfile_lock_name] || "#{gemfile_name}.lock",
20
+ opts.key?(:verbose) ? opts[:verbose] : ENV['VERBOSE'] == 'true')
20
21
  .install
21
22
  end
22
23
  end
@@ -25,6 +26,14 @@ module Killbill
25
26
  @verbose = verbose
26
27
 
27
28
  @logger = Logger.new(STDOUT)
29
+ @logger.formatter = proc do |severity, datetime, _, msg|
30
+ date_format = datetime.strftime('%Y-%m-%d %H:%M:%S.%L')
31
+ if severity == "INFO" || severity == "WARN"
32
+ "KillBill [#{date_format}] #{severity} : #{msg}\n"
33
+ else
34
+ "KillBill [#{date_format}] #{severity} : #{msg}\n"
35
+ end
36
+ end
28
37
  @logger.level = @verbose ? Logger::DEBUG : Logger::INFO
29
38
 
30
39
  @base_name = base_name
@@ -37,27 +46,70 @@ module Killbill
37
46
  @base = Pathname.new(@base_name).expand_path
38
47
 
39
48
  # Find the gemspec to determine name and version
40
- @plugin_gemspec = find_plugin_gemspec
49
+ @plugin_gemspec = load_plugin_gemspec
41
50
 
51
+ @package_dir = Pathname.new('pkg').expand_path
52
+ FileUtils.mkdir_p @package_dir
42
53
  # Temporary build directory
43
- # Don't use 'pkg' as it is used by Rake::PackageTask already: it will
44
- # hard link all files from @package_dir to pkg to avoid tar'ing up symbolic links
45
- @package_dir = Pathname.new(name).expand_path
54
+ # will hard link all files from @package_tmp_dir to pkg to avoid tar'ing
55
+ # up symbolic links (similar to how Rake::PackageTask does prepare files)
56
+ @package_tmp_dir = Pathname.new(File.join('tmp', name)).expand_path
57
+
58
+ @root_dir_path = 'ROOT' # plugin's ROOT directory
59
+ @gems_dir_path = File.join(@root_dir_path, 'gems')
60
+ # Staging area to install the killbill.properties and config.ru files
61
+ @plugin_target_dir = @package_tmp_dir.join("#{version}").expand_path
62
+ @plugin_root_target_dir = @plugin_target_dir.join(@root_dir_path)
46
63
 
47
64
  # Staging area to install gem dependencies
48
65
  # Note the Killbill friendly structure (which we will keep in the tarball)
49
- @target_dir = @package_dir.join("#{version}/gems").expand_path
66
+ @plugin_gem_target_dir = @plugin_target_dir.join(@gems_dir_path)
67
+ end
50
68
 
51
- # Staging area to install the killbill.properties and config.ru files
52
- @plugin_root_target_dir = @package_dir.join("#{version}").expand_path
69
+ attr_reader :base
70
+
71
+ def name
72
+ @plugin_gemspec.name
73
+ end
74
+
75
+ def version
76
+ @plugin_gemspec.version
53
77
  end
54
78
 
55
79
  def specs
56
80
  # Rely on the Gemfile definition, if it exists, to get all dependencies
57
81
  # (we assume the Gemfile includes the plugin gemspec, as it should).
58
- # Otherwise, use only the plugin gemspec.
59
- # When using the Gemfile definition, don't include the :development group -- should this be configurable?
60
- @specs ||= @gemfile_definition ? @gemfile_definition.specs_for([:default]) : [@plugin_gemspec]
82
+ # Otherwise, use recursively retrieve the plugin gemspec' runtime dependencies
83
+ # ... resolves all gems as they are currently installed with RubyGems
84
+ @specs ||=
85
+ if @gemfile_definition
86
+ # don't include the :development group
87
+ @gemfile_definition.specs_for([:default])
88
+ else
89
+ get_dependencies = lambda do |spec|
90
+ deps = ( spec.runtime_dependencies || [] ).map(&:to_spec)
91
+ deps.dup.each { |spec| deps += get_dependencies.call(spec) }
92
+ deps.uniq
93
+ end
94
+ all_dependencies = get_dependencies.call(@plugin_gemspec)
95
+ all_dependencies.dup.each do |spec|
96
+ next unless all_dependencies.include?(spec) # removed previously
97
+
98
+ # duplicate gemspec of same name might get included since we did
99
+ # not really do through the gem activation hustle, lowest version
100
+ # should win since those tend to be matched by strict requirements
101
+ if other_spec = all_dependencies.find { |s| s.name == spec.name && s != spec }
102
+ if other_spec.version > spec.version
103
+ @logger.debug "Discarding matched gem '#{spec.name}' version #{other_spec.version} in favor of #{spec.version}"
104
+ all_dependencies.delete(other_spec)
105
+ else # other_spec.version < spec.version
106
+ @logger.debug "Discarding matched gem '#{spec.name}' version #{spec.version} in favor of #{other_spec.version}"
107
+ all_dependencies.delete(spec)
108
+ end
109
+ end
110
+ end
111
+ [ @plugin_gemspec ] + all_dependencies
112
+ end
61
113
  end
62
114
 
63
115
  def install
@@ -69,64 +121,175 @@ module Killbill
69
121
  validate
70
122
  end
71
123
 
72
- # Build the .tar.gz and .zip packages
73
- task :package, [:verbose] => :stage
74
- package_task = Rake::PackageTask.new(name, version) do |p|
75
- p.need_tar_gz = true
76
- p.need_zip = true
124
+ desc "Build all package files for #{name} plugin #{version}"
125
+ task :package, [:verbose] => :stage # builds .tar.gz & .zip packages
126
+
127
+ package_name = "#{name}-#{version}"
128
+ package_dir = @package_dir.realpath # pkg
129
+ package_dir_path = File.join(package_dir, package_name) # pkg/killbill-xxx-0.1.2
130
+
131
+ task :package => [ tar_gz_file = File.join(package_dir, "#{package_name}.tar.gz") ]
132
+ file tar_gz_file => [ package_dir_path, 'package:files' ] do
133
+ chdir(package_dir) do
134
+ tar_command = 'tar'
135
+ sh tar_command, "zcvf", tar_gz_file, package_name
136
+ end
137
+ end
138
+
139
+ task :package => [ zip_file = File.join(package_dir, "#{package_name}.zip") ]
140
+ file zip_file => [ package_dir_path, 'package:files' ] do
141
+ chdir(package_dir) do
142
+ zip_command = 'zip'
143
+ sh zip_command, "-r", zip_file, package_name
144
+ end
145
+ end
146
+
147
+ directory package_dir_path
148
+ task 'package:files' do
149
+ # move files from tmp directory to pkg (based on how rake does)
150
+ basename = Regexp.escape(@package_tmp_dir.basename.to_s)
151
+ tmp_path = @package_tmp_dir.realpath.to_s
152
+ realpath_tmp_prefix = tmp_path.sub(/\/#{basename}$/, '')
153
+ package_files = Rake::FileList.new("#{tmp_path}/**/*")
154
+
155
+ package_files.each do |fn|
156
+ f = File.join(package_dir_path, fn.sub(realpath_tmp_prefix, ''))
157
+ fdir = File.dirname(f)
158
+ mkdir_p(fdir) unless File.exist?(fdir)
159
+ if File.directory?(fn)
160
+ mkdir_p(f)
161
+ else
162
+ rm_f f
163
+ safe_ln(fn, f)
164
+ end
165
+ end
77
166
  end
78
167
 
79
- desc "Stage all dependencies"
80
- task :stage, [:verbose] => :validate do |t, args|
168
+ desc "Force a rebuild of package files for #{name} plugin #{version}"
169
+ task :repackage => [:clobber_package, :package]
170
+
171
+ task :clobber_tmp do
172
+ rm_f @package_tmp_dir rescue nil
173
+ end
174
+
175
+ desc "Remove package files from #{@package_dir}"
176
+ task :clobber_package do
177
+ rm_f tar_gz_file if File.exist?(tar_gz_file)
178
+ rm_f zip_file if File.exist?(zip_file)
179
+ rm_r package_dir_path rescue nil
180
+ end
181
+ task :clobber => [:clobber_tmp, :clobber_package]
182
+
183
+ task 'stage:init' do
184
+ # NOOP task for plugins to hook up if they need some sort of initialization
185
+ # (task will be run in the context of the Killbill::PluginHelper instance)
186
+ # NOTE: no need for post (stage:done) hook since it's easy using Rake :
187
+ # Rake::Task["killbill:package"].enhance { ... }
188
+ end
189
+
190
+ desc "Stage dependencies for #{name} plugin #{version}"
191
+ task :stage, [:verbose] => [ :validate, 'stage:init' ] do |t, args|
81
192
  set_verbosity(args)
82
193
 
194
+ mkdir_p @plugin_target_dir.to_s, :verbose => @verbose
195
+
83
196
  stage_dependencies
84
197
  stage_extra_files
85
-
86
- # Small hack! Update the list of files to package (Rake::FileList is evaluated too early above)
87
- package_task.package_files = Rake::FileList.new("#{@package_dir.basename}/**/*")
88
198
  end
89
199
 
90
- desc "Deploy the plugin to Kill Bill"
200
+ desc "Deploy #{name} plugin #{version} to KillBill server"
91
201
  task :deploy, [:force, :plugin_dir, :verbose] => :stage do |t, args|
92
- set_verbosity(args)
202
+ plugins_dir = prepare_deploy(t, args)
93
203
 
94
- plugins_dir = Pathname.new("#{args.plugin_dir || '/var/tmp/bundles/plugins/ruby'}").expand_path
95
- mkdir_p plugins_dir, :verbose => @verbose
204
+ cp_r @package_tmp_dir, plugins_dir, :verbose => @verbose
96
205
 
97
- plugin_path = Pathname.new("#{plugins_dir}/#{name}")
98
- if plugin_path.exist?
99
- if args.force == "true"
100
- @logger.info "Deleting previous plugin deployment #{plugin_path}"
101
- rm_rf plugin_path, :verbose => @verbose
102
- else
103
- raise "Cowardly not deleting previous plugin deployment #{plugin_path} - override with rake killbill:deploy[true]"
104
- end
105
- end
206
+ deploy_config_files plugin_path(plugins_dir) # .../[name]/[version]
207
+ end
208
+
209
+ desc "Deploy plugin in development mode (without staging)"
210
+ task 'deploy:dev', [:force, :plugin_dir, :verbose] => :validate do |t, args|
211
+ raise 'development deployment only works with Bundler' unless bundler?
212
+
213
+ plugins_dir = prepare_deploy(t, args)
106
214
 
107
- cp_r @package_dir, plugins_dir, :verbose => @verbose
215
+ # prepare "temporary" deployment at tmp/deploy:dev
216
+ package_tmp_dir = Pathname.new(File.join('tmp', 'deploy:dev')).expand_path
217
+ rm_r package_tmp_dir if File.exist?(package_tmp_dir)
218
+ mkdir_p package_tmp_dir.to_s, :verbose => @verbose
108
219
 
109
- Rake::FileList.new("#{@base}/*.yml").each do |config_file|
110
- config_file_path = Pathname.new("#{plugin_path}/#{version}/#{File.basename(config_file)}").expand_path
111
- @logger.info "Deploying #{config_file} to #{config_file_path}"
112
- cp config_file, config_file_path, :verbose => @verbose
220
+ mkdir target_dir = package_tmp_dir.join(version.to_s)
221
+
222
+ # NOTE: although same _boot.rb_ as with regular deploys might not work
223
+ stage_extra_files target_dir
224
+ if boot_rb_file.nil?
225
+ generate_dev_boot_rb target_dir
226
+ else
227
+ @logger.info "Make sure the suplied #{boot_rb_file} is removed/adjusted before doing a regular killbill:deploy (same boot.rb won't likely work)"
113
228
  end
229
+
230
+ # here we assume Gemfile declares gemspec and we link ROOT to base :
231
+ ln_s @base, target_dir.join(@root_dir_path), :verbose => @verbose
232
+
233
+ # ln -s /var/tmp/bundles/plugins/ruby/killbill-xxx -> tmp/deploy:dev
234
+ ln_s package_tmp_dir, plugins_dir.join(name), :verbose => @verbose
235
+
236
+ deploy_config_files target_dir
114
237
  end
115
238
 
116
239
  desc "List all dependencies"
117
- task :dependency => :validate do
240
+ task :dependencies => :validate do
118
241
  print_dependencies
119
242
  end
243
+ task :dependency => :dependencies
120
244
 
121
245
  desc "Delete #{@package_dir}"
122
- task :clean => :clobber_package do
123
- rm_rf @package_dir
246
+ task :clean => :clobber do
247
+ rm_r @package_dir if File.exist?(@package_dir)
124
248
  end
125
249
  end
126
250
  end
127
251
 
128
252
  private
129
253
 
254
+ def plugin_path(plugins_dir, versioned = true)
255
+ plugin_path = plugins_dir.join(name)
256
+ versioned ? plugin_path.join(version.to_s) : plugin_path
257
+ end
258
+
259
+ # (shared) deploy task(s) helper
260
+ # @return _plugins_ directory
261
+ def prepare_deploy(t, args)
262
+ set_verbosity(args)
263
+
264
+ plugins_dir = Pathname.new("#{args.plugin_dir || '/var/tmp/bundles/plugins/ruby'}").expand_path
265
+ mkdir_p plugins_dir, :verbose => @verbose
266
+
267
+ plugin_path = plugin_path(plugins_dir, false) # "#{plugins_dir}/#{name}"
268
+ if plugin_path.exist?
269
+ if args.force == "true"
270
+ safe_unlink plugin_path # if link (deploy:dev) just remove the link
271
+ if plugin_path.exist?
272
+ @logger.info "Deleting previous plugin deployment #{plugin_path}"
273
+ rm_rf plugin_path, :verbose => @verbose if plugin_path.exist?
274
+ else
275
+ @logger.info "Unlinked previous plugin deployment #{plugin_path}"
276
+ end
277
+ else
278
+ raise "Cowardly not deleting previous plugin deployment #{plugin_path} - override with rake #{t.name}[true]"
279
+ end
280
+ end
281
+ plugins_dir
282
+ end
283
+
284
+ # (shared) deploy task(s) helper
285
+ def deploy_config_files(path, config_files = Rake::FileList.new("#{@base}/*.yml"))
286
+ config_files.each do |config_file|
287
+ config_file_path = File.join(path, File.basename(config_file))
288
+ @logger.info "Deploying #{config_file} to #{config_file_path}"
289
+ cp config_file, config_file_path, :verbose => @verbose
290
+ end
291
+ end
292
+
130
293
  def set_verbosity(args)
131
294
  return unless args.verbose == 'true'
132
295
  @verbose = true
@@ -134,106 +297,257 @@ module Killbill
134
297
  end
135
298
 
136
299
  def validate
137
- @gemfile_definition = find_gemfile
300
+ if @gemfile_definition = build_gemfile
301
+ @gemfile_definition.resolve
302
+ end
303
+ true
138
304
  end
139
305
 
306
+ def bundler?; !! @gemfile_definition end
307
+
140
308
  def print_dependencies
141
- puts "Gems to be staged:"
309
+ # NOTE: can be improved to include :git info and warn on gem :path
142
310
  specs.each { |spec| puts " #{spec.name} (#{spec.version})" }
143
311
  end
144
312
 
145
- def name
146
- @plugin_gemspec.name
147
- end
148
-
149
- def version
150
- @plugin_gemspec.version
151
- end
152
-
153
313
  # Parse the <plugin_name>.gemspec file
154
- def find_plugin_gemspec
314
+ def load_plugin_gemspec
155
315
  gemspecs = @plugin_name ? [File.join(@base, "#{@plugin_name}.gemspec")] : Dir[File.join(@base, "{,*}.gemspec")]
156
- raise "Unable to find your plugin gemspec in #{@base}" unless gemspecs.size == 1
316
+ raise "Unable to find your plugin gemspec in #{@base}" if gemspecs.size == 0
317
+ raise "Found multiple plugin gemspec in #{@base} : #{gemspecs.inspect}" if gemspecs.size > 1
157
318
  spec_path = gemspecs.first
158
- @logger.debug "Parsing #{spec_path}"
159
- Bundler.load_gemspec(spec_path)
319
+ @logger.debug "Loading #{spec_path}"
320
+ Gem::Specification.load(spec_path)
160
321
  end
161
322
 
162
323
  def find_plugin_gem(spec)
163
- gem_name = spec.file_name
164
- # spec.loaded_from is the path to the gemspec file
165
- base = Pathname.new(File.dirname(spec.loaded_from)).expand_path
324
+ gem_name = spec.file_name
166
325
 
167
326
  # Try in the base directory first
168
327
  plugin_gem_file = Pathname.new(gem_name).expand_path
169
- plugin_gem_file = base.join(gem_name).expand_path unless plugin_gem_file.file?
328
+ unless plugin_gem_file.file? # `rake build` (./pkg)
329
+ plugin_gem_file = Pathname.new(File.join('pkg', gem_name))
330
+ end
170
331
 
171
- # Try in subdirectories next
172
332
  unless plugin_gem_file.file?
173
- plugin_gem_files = Dir[File.join(base, "**/#{spec.file_name}")]
174
- @logger.debug "Gem candidates found: #{plugin_gem_files}"
175
- # Take the first one, assume the other ones are from build directories (e.g. pkg)
176
- plugin_gem_file = Pathname.new(plugin_gem_files.first).expand_path unless plugin_gem_files.empty?
333
+ raise "Unable to find #{gem_name}. Did you build it? (`rake build')"
177
334
  end
178
335
 
179
- raise "Unable to find #{gem_name} in #{base}. Did you build it? (`rake build')" unless plugin_gem_file.file?
180
-
181
336
  @logger.debug "Found #{plugin_gem_file}"
182
- Pathname.new(plugin_gem_file).expand_path
337
+ plugin_gem_file.expand_path
338
+ end
339
+
340
+ def find_missing_gem(spec, silent = nil)
341
+ base = nil
342
+ if spec.loaded_from
343
+ # spec.loaded_from is (usually) the path to the gemspec file
344
+ base = Pathname.new(File.dirname(spec.loaded_from)).expand_path
345
+ if ! base.file?
346
+ base = nil unless base.directory?
347
+ end
348
+ end
349
+ unless base
350
+ base = spec.gems_dir if spec.respond_to?(:gems_dir)
351
+ base = spec.base_dir if spec.respond_to?(:base_dir)
352
+ end
353
+ # might end-up with a slightly incorrect resolution (due Bundler) :
354
+ # e.g. .../rvm/gems/jruby-1.7.19@global/gems/bundler-1.7.9/lib/bundler/gems
355
+ if base
356
+ parent = base
357
+ parent = parent.parent while ! parent.join('cache').directory?
358
+ base = parent if parent && parent.join('gems').directory? # RGs layout
359
+ end
360
+
361
+ gem_file = nil
362
+ gem_name = spec.file_name
363
+ gem_paths = Gem.paths.path.dup; gem_paths.unshift(base) if base
364
+ gem_paths.each do |gem_path| # e.g. /opt/rvm/gems/jruby-1.7.16@global
365
+ if File.directory? cache_dir = File.join(gem_path, 'cache')
366
+ if File.file? gem_file = File.join(cache_dir, gem_name)
367
+ gem_file = Pathname.new(gem_file); break
368
+ else
369
+ gem_file = nil
370
+ end
371
+ else
372
+ gem_files = Dir[File.join(gem_path, "**/#{gem_name}")]
373
+ unless gem_files.empty?
374
+ @logger.debug "Gem candidates found: #{gem_files.inspect}"
375
+ gem_file = Pathname.new(gem_files.first); break
376
+ end
377
+ end
378
+ end
379
+
380
+ if gem_file.nil? || ! gem_file.file?
381
+ return nil if silent
382
+ raise "Unable to find #{gem_name} under #{gem_paths.inspect}"
383
+ end
384
+
385
+ @logger.debug "Found #{gem_file}"
386
+ gem_file.expand_path
183
387
  end
184
388
 
185
389
  # Parse the existing Gemfile and Gemfile.lock files
186
- def find_gemfile
187
- gemfile = @base.join(@gemfile_name).expand_path
390
+ def build_gemfile
391
+ gemfile = gemfile_path
188
392
  # Don't make the Gemfile a requirement, a gemspec should be enough
189
393
  return nil unless gemfile.file?
190
394
 
191
395
  # Make sure the developer ran `bundle install' first. We could probably run
192
- # Bundler::Installer::install(@target_dir, @definition, {})
396
+ # Bundler::Installer::install(@plugin_gem_target_dir, @definition, {})
193
397
  # but it may be better to make sure all dependencies are resolved first,
194
398
  # before attempting to build the plugin
195
- gemfile_lock = @base.join(@gemfile_lock_name).expand_path
196
- raise "Unable to find the Gemfile.lock at #{gemfile_lock} for your plugin. Please run `bundle install' first" unless gemfile_lock.file?
399
+ gemfile_lock = gemfile_lock_path
400
+ raise "Unable to find the bundle .lock at #{gemfile_lock} for your plugin. Please run `bundle install' first" unless gemfile_lock.file?
197
401
 
198
402
  @logger.debug "Parsing #{gemfile} and #{gemfile_lock}"
199
403
  Bundler::Definition.build(gemfile, gemfile_lock, nil)
200
404
  end
201
405
 
406
+ def gemfile_path
407
+ @base.join(@gemfile_name).expand_path
408
+ end
409
+
410
+ def gemfile_lock_path
411
+ @base.join(@gemfile_lock_name).expand_path
412
+ end
413
+
202
414
  def stage_dependencies
203
415
  # Create the target directory
204
- mkdir_p @target_dir.to_s, :verbose => @verbose
416
+ mkdir_p @plugin_gem_target_dir.to_s, :verbose => @verbose
205
417
 
206
- @logger.debug "Installing all gem dependencies to #{@target_dir}"
418
+ @logger.debug "Installing all gem dependencies to #{@plugin_gem_target_dir}"
207
419
  # We can't simply use Bundler::Installer unfortunately, because we can't tell it to copy the gems for cached ones
208
420
  # (it will default to using Bundler::Source::Path references to the gemspecs on "install").
421
+
422
+ generate_boot_rb if boot_rb_file.nil?
423
+ # else user-suplied boot.rb will be copied into the package
424
+
425
+ # part of copying the dependencies is getting Gemfile/Gemfile.lock in
426
+ # otherwise :git => gem dependencies would need work-arounds to work
427
+ copy_gemfile if bundler? # plugin gem build might re-copy, that's fine!
428
+
209
429
  specs.each do |spec|
210
- plugin_gem_file = Pathname.new(spec.cache_file).expand_path
211
- if plugin_gem_file.file?
212
- @logger.debug "Staging #{spec.name} (#{spec.version}) from #{plugin_gem_file}"
430
+ if ! gem_path = valid_gem_path(spec)
431
+ if gem_path == false # spec.name == name
432
+ # Gemfile very likely declares gemspec ... so we need to get that in
433
+ # yet the current way (the plugin gem must be built first) we can
434
+ # not simply copy spec.loaded_from into the package's root directory
435
+ # (the actual [PLUGIN_ROOT]/killbill-plugin.gemspec) as that depends
436
+ # on `git' binary on PATH (to get the actual gem.files)
437
+ @logger.info "Building #{spec.name} gem from #{spec.loaded_from}"
438
+ Dir.mktmpdir do |dir|
439
+ plugin_gem = Gem::Package.new(File.join(dir, spec.file_name))
440
+ plugin_gem.spec = spec
441
+ plugin_gem.build(true) # skip_validation
442
+ gemspec_name = File.basename(spec.loaded_from)
443
+ puts_to_root plugin_gem.spec.to_ruby, gemspec_name
444
+ # NOTE: further the unpacked gemspec will be read by Bundler and assumes
445
+ # the unpacked gem structure to be found on the file-system, extract :
446
+ plugin_gem.extract_files @plugin_root_target_dir
447
+ end
448
+ next
449
+ end
450
+ if bundler?
451
+ # gem not under gem cache_dir (default gem or multiple gem paths)
452
+ gem_path = find_missing_gem(spec)
453
+ @logger.debug "Staging #{spec.name} (#{spec.version}) from #{gem_path}"
454
+ do_install_gem(gem_path, spec)
455
+ else # mostly Bunder-less backwards-compatibility
456
+ gem_path = find_missing_gem(spec, :silent) || find_plugin_gem(spec)
457
+ @logger.info "Staging #{spec.full_name} from #{gem_path}"
458
+ do_install_gem(gem_path, spec)
459
+ end
460
+ elsif gem_path.file?
461
+ @logger.debug "Staging #{spec.name} (#{spec.version}) from #{gem_path}"
462
+ do_install_gem(gem_path, spec)
463
+ elsif gem_path.directory?
464
+ @logger.debug "Staging #{spec.name} (#{spec.version}) from #{gem_path}"
465
+ do_install_bundler(gem_path, spec)
213
466
  else
214
- plugin_gem_file = find_plugin_gem(spec)
215
- @logger.info "Staging custom gem #{spec.full_name} from #{plugin_gem_file}"
467
+ raise "#{spec.name} gem path #{gem_path.inspect} does not exist"
216
468
  end
469
+ end
470
+ end
217
471
 
218
- do_install_gem(plugin_gem_file, spec.name, spec.version)
472
+ def valid_gem_path(spec)
473
+ cache_file = File.join(spec.cache_dir, "#{spec.full_name}.gem")
474
+ cache_path = Pathname.new(cache_file).expand_path
475
+ return cache_path if cache_path.file?
476
+ return false if spec.name == name # it's the plugin gem itself
477
+
478
+ if spec.source && bundler? && spec.source.is_a?(Bundler::Source)
479
+ # Path < Source and Git < Path :
480
+ case spec.source
481
+ when Bundler::Source::Git
482
+ # NOTE cache_path only works with `bundle cache --all`
483
+ # when bundle cached install path points to cache
484
+ # e.g. ./vendor/cache/killbill-plugin-framework-ruby-ce5e19f45bc9
485
+ # otherwise it's the path from under RG (as usual for Bundler)
486
+ # e.g. [RVM]/gems/jruby-1.7.19@kb/bundler/gems/killbill-plugin-framework-ruby-ce5e19f45bc9
487
+ return spec.source.install_path
488
+ when Bundler::Source::Path
489
+ @logger.warn "gem '#{spec.name}' declares :path => '#{spec.source.path}' packaging will only work locally (while the path exists) !"
490
+ return spec.source.path
491
+ end
219
492
  end
493
+ nil
494
+ end
495
+
496
+ def do_install_bundler(path, spec)
497
+ #full_gem_path = Pathname.new(spec.full_gem_path)
498
+
499
+ #gem_relative_path = full_gem_path.relative_path_from(Bundler.install_path)
500
+ #filenames = []; gem_relative_path.each_filename { |f| filenames << f }
501
+
502
+ #exclude_gems = true
503
+ #unless filenames.empty?
504
+ # full_gem_path = Pathname.new(Bundler.install_path) + filenames.first
505
+ # exclude_gems = false
506
+ #end
507
+
508
+ FileUtils.mkdir_p target_dir = File.join(@plugin_gem_target_dir, "bundler/gems")
509
+
510
+ if spec.groups.include?(:killbill_excluded)
511
+ Dir.glob("#{path}/**/#{spec.name}.gemspec").each do |file|
512
+ gem_target_file = File.join(target_dir, File.basename(file))
513
+ FileUtils.rm(gem_target_file) if File.exist?(gem_target_file)
514
+ FileUtils.cp(file, target_dir) # gemspec only to avert Bundler error
515
+ end
516
+ else
517
+ gem_target_dir = File.join(target_dir, File.basename(path))
518
+ FileUtils.rm_r(gem_target_dir) if File.exist?(gem_target_dir)
519
+ FileUtils.cp_r(path, target_dir)
520
+ # the copied .git directory is not needed (might be large) :
521
+ git_target_dir = File.join(gem_target_dir, '.git')
522
+ FileUtils.rm_r(git_target_dir) if File.exist?(git_target_dir)
523
+ end
524
+ rescue => e
525
+ @logger.warn "Unable to stage #{spec.name} from #{path}: #{e}"
526
+ raise e
220
527
  end
221
528
 
222
- def do_install_gem(path, name, version)
223
- gem_installer = Gem::Installer.new(path.to_s,
224
- {
225
- :force => true,
226
- :install_dir => @target_dir,
227
- # Should be redundant with the tweaks below
228
- :development => false,
229
- :wrappers => true
230
- })
529
+ def do_install_gem(path, spec)
530
+ name, version = spec.name, spec.version
531
+ options = {
532
+ :force => true,
533
+ :install_dir => @plugin_gem_target_dir,
534
+ #:only_install_dir => true,
535
+ # Should be redundant with the tweaks below
536
+ :development => false,
537
+ :wrappers => true
538
+ }
539
+ if Gem::Installer.respond_to?(:at)
540
+ gem_installer = Gem::Installer.at(path.to_s, options)
541
+ else # constructing an Installer object with a string is deprecated
542
+ gem_installer = Gem::Installer.new(path.to_s, options)
543
+ end
231
544
 
232
545
  # Tweak the spec file as there are a lot of things we don't care about
233
546
  gem_installer.spec.executables = nil
234
- gem_installer.spec.extensions = nil
235
547
  gem_installer.spec.extra_rdoc_files = nil
236
548
  gem_installer.spec.test_files = nil
549
+ # avoid the annoying post_install_message from money gem (and others)
550
+ gem_installer.spec.post_install_message = nil
237
551
 
238
552
  gem_installer.install
239
553
  rescue => e
@@ -241,17 +555,143 @@ module Killbill
241
555
  raise e
242
556
  end
243
557
 
244
- def stage_extra_files
558
+ def generate_boot_rb(target_dir = @plugin_target_dir)
559
+ @logger.debug "Generating boot.rb into #{target_dir}"
560
+ # NOTE: previously the same WD was used dependent on server startup
561
+ puts_to target_dir, <<-END, 'boot.rb'
562
+ Dir.chdir File.expand_path('#{@root_dir_path}', File.dirname(__FILE__))
563
+
564
+ ENV["GEM_HOME"] = File.join(File.dirname(__FILE__), '#{@gems_dir_path}')
565
+ ENV["GEM_PATH"] = ENV["GEM_HOME"]
566
+ # environment is set statically, as soon as Sinatra is loaded
567
+ ENV["RACK_ENV"] = 'production'
568
+ # prepare to boot using Bundler :
569
+ ENV["BUNDLE_WITHOUT"] = "#{ENV["BUNDLE_WITHOUT"] || 'development:test'}"
570
+ ENV["BUNDLE_GEMFILE"] = File.expand_path('Gemfile')
571
+ ENV["JBUNDLE_SKIP"] = 'true' # we only use JBundler for development/testing
572
+
573
+ require 'rubygems' unless defined? Gem
574
+ if File.exists?(ENV["BUNDLE_GEMFILE"])
575
+ require 'bundler'; Bundler.setup
576
+ else
577
+ #{adjust_plugin_load_path}
578
+ end
579
+
580
+ # try loading killbill (Bunler-less deploys or in case plugin forgot to require)
581
+ begin
582
+ require 'killbill'
583
+ rescue LoadError => e # not fatal for un-usual cases where plugins vendor the gem
584
+ warn "WARN: failed to load killbill gem: \#\{e.inspect\}"
585
+ end
586
+
587
+ #{plugin_require_line}
588
+
589
+ END
590
+ end
591
+
592
+ def generate_dev_boot_rb(target_dir, env = 'production')
593
+ @logger.debug "Generating (dev) boot.rb into #{target_dir}"
594
+ puts_to target_dir, <<-END, 'boot.rb'
595
+ Dir.chdir File.expand_path('#{@root_dir_path}', File.dirname(__FILE__))
596
+
597
+ ENV["GEM_HOME"] = '#{Gem.paths.home}'
598
+ ENV["GEM_PATH"] = '#{Gem.paths.path.join(':')}'
599
+ # environment is set statically, as soon as Sinatra is loaded
600
+ ENV["RACK_ENV"] = '#{env}'
601
+ # prepare to boot using Bundler :
602
+ ENV["BUNDLE_WITHOUT"] = "#{ENV["BUNDLE_WITHOUT"] || ''}"
603
+ ENV["BUNDLE_GEMFILE"] = File.expand_path('Gemfile')
604
+
605
+ require 'rubygems' unless defined? Gem
606
+ require 'bundler'; Bundler.setup
607
+
608
+ # require 'killbill'
609
+
610
+ #{plugin_require_line}
611
+
612
+ END
613
+ end
614
+
615
+ def plugin_require_line
616
+ files = @plugin_gemspec.files; require_path = "#{@plugin_gemspec.require_path}/"
617
+ files = files.select { |file| file.start_with?(require_path) && file[-3..-1] == '.rb' }
618
+ # [ "lib/stripe.rb", "lib/stripe/api.rb", "lib/stripe/application.rb", ... ]
619
+ files.map! { |file| file.sub(require_path, '') }
620
+ # [ "stripe.rb", "stripe/api.rb", "stripe/application.rb", ... ]
621
+
622
+ # 0. if killbill-stripe gem name has a killbill-stripe.rb use it
623
+ return "require '#{name}'" if files.include?("#{name}.rb")
624
+
625
+ # 1. RG convention: killbill-stripe -> require 'killbill/stripe'
626
+ filename = "#{name.sub('-', '/')}"
627
+ return "require '#{filename}'" if files.include?("#{filename}.rb")
628
+
629
+ # 2. killbill-paypal-express -> require 'paypal-express' (not used)
630
+ filename = "#{name.sub(/^killbill\-/, '')}"
631
+ return "require '#{filename}'" if files.include?("#{filename}.rb")
632
+
633
+ # 3. killbill-paypal-express -> require 'paypal_express'
634
+ filename = filename.sub('-', '_')
635
+ return "require '#{filename}'" if files.include?("#{filename}.rb")
636
+
637
+ # not likely to happen - fallback to root file (warn when multiple) :
638
+ files = files.select { |file| file.index('/').nil? }
639
+ return "require '#{files[0]}'" if files.size == 1
640
+
641
+ raise "could not resolve main require file from gemspec,\n please follow our naming convention for the bootstrap require e.g. \"#{name.sub('-', '/')}.rb\""
642
+ end
643
+
644
+ def adjust_plugin_load_path
645
+ # NOTE: assuming Dir.chdir [ROOT]
646
+ @plugin_gemspec.require_paths.map do |path|
647
+ "$LOAD_PATH << File.expand_path('#{path}')" # $LOAD_PATH << 'lib'
648
+ end.join('; ')
649
+ end
650
+
651
+ def copy_gemfile(target_dir = @plugin_root_target_dir)
652
+ copy_to target_dir, gemfile_path, 'Gemfile'
653
+ copy_to target_dir, gemfile_lock_path, 'Gemfile.lock'
654
+ end
655
+
656
+ def stage_extra_files(target_dir = @plugin_target_dir)
657
+ unless boot_rb_file.nil?
658
+ @logger.info "Staging (user-suplied) #{boot_rb_file}"
659
+ copy_to target_dir, boot_rb_file
660
+ end
245
661
  unless killbill_properties_file.nil?
246
- @logger.debug "Staging #{killbill_properties_file} to #{@plugin_root_target_dir}"
247
- cp killbill_properties_file, @plugin_root_target_dir, :verbose => @verbose
662
+ @logger.debug "Staging #{killbill_properties_file}"
663
+ copy_to target_dir, killbill_properties_file
248
664
  end
249
665
  unless config_ru_file.nil?
250
- @logger.debug "Staging #{config_ru_file} to #{@plugin_root_target_dir}"
251
- cp config_ru_file, @plugin_root_target_dir, :verbose => @verbose
666
+ @logger.debug "Staging #{config_ru_file}"
667
+ copy_to target_dir, config_ru_file
252
668
  end
253
669
  end
254
670
 
671
+ def copy_to(target_dir, file_path, base_name = File.basename(file_path))
672
+ target_file = File.join(target_dir, base_name)
673
+ cp file_path, target_file, :verbose => @verbose
674
+ end
675
+
676
+ def puts_to(target_dir, content, base_name)
677
+ target_file = File.join(target_dir, base_name)
678
+ File.open(target_file, 'w') { |file| file << content }
679
+ end
680
+
681
+ def puts_to_base(content, base_name)
682
+ target_file = File.join(@plugin_target_dir, base_name)
683
+ File.open(target_file, 'w') { |file| file << content }
684
+ end
685
+
686
+ def puts_to_root(content, base_name)
687
+ target_file = File.join(@plugin_root_target_dir, base_name)
688
+ File.open(target_file, 'w') { |file| file << content }
689
+ end
690
+
691
+ def boot_rb_file
692
+ path_to_string @base.join("boot.rb").expand_path
693
+ end
694
+
255
695
  def killbill_properties_file
256
696
  path_to_string @base.join("killbill.properties").expand_path
257
697
  end