killbill 3.2.4 → 4.0.0

Sign up to get free protection for your applications and to get access to all the features.
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