melissadata 0.0.1 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +2 -0
- data/Gemfile.lock +46 -16
- data/README.md +50 -2
- data/Rakefile +58 -23
- data/Vagrantfile +12 -12
- data/bin/md_tcp_server +5 -0
- data/bin/md_unix_server +5 -0
- data/chef/cookbooks/apt/README.md +109 -0
- data/chef/cookbooks/apt/files/default/apt-cacher +9 -0
- data/chef/cookbooks/apt/files/default/apt-cacher.conf +144 -0
- data/chef/cookbooks/apt/files/default/apt-proxy-v2.conf +50 -0
- data/chef/cookbooks/apt/metadata.json +34 -0
- data/chef/cookbooks/apt/metadata.rb +13 -0
- data/chef/cookbooks/apt/providers/repository.rb +72 -0
- data/chef/cookbooks/apt/recipes/cacher-client.rb +37 -0
- data/chef/cookbooks/apt/recipes/cacher.rb +45 -0
- data/chef/cookbooks/apt/recipes/default.rb +33 -0
- data/chef/cookbooks/apt/resources/repository.rb +30 -0
- data/chef/cookbooks/build-essential/README.md +24 -0
- data/chef/cookbooks/build-essential/metadata.json +35 -0
- data/chef/cookbooks/build-essential/metadata.rb +10 -0
- data/chef/cookbooks/build-essential/recipes/default.rb +45 -0
- data/chef/cookbooks/melissadata/attributes/default.rb +5 -0
- data/chef/cookbooks/melissadata/metadata.rb +8 -0
- data/chef/cookbooks/melissadata/recipes/default.rb +37 -0
- data/chef/cookbooks/melissadata/templates/default/Makefile.erb +39 -0
- data/chef/cookbooks/melissadata/templates/default/md_tcp_server.init.erb +82 -0
- data/chef/cookbooks/melissadata/templates/default/md_unix_server.init.erb +81 -0
- data/chef/cookbooks/rvm/recipes/default.rb +1 -0
- data/chef/cookbooks/rvm/recipes/r187.rb +6 -0
- data/chef/cookbooks/rvm/recipes/r192.rb +6 -0
- data/chef/cookbooks/rvm/recipes/system.rb +28 -0
- data/chef/cookbooks/ubuntu/attributes/default.rb +24 -0
- data/chef/cookbooks/ubuntu/metadata.json +36 -0
- data/chef/cookbooks/ubuntu/metadata.rb +8 -0
- data/chef/cookbooks/ubuntu/recipes/default.rb +27 -0
- data/chef/cookbooks/ubuntu/templates/default/sources.list.erb +15 -0
- data/lib/melissadata.rb +1 -2
- data/lib/melissadata/client.rb +8 -0
- data/lib/melissadata/client/base.rb +56 -0
- data/lib/melissadata/client/tcp.rb +28 -0
- data/lib/melissadata/client/unix.rb +22 -0
- data/lib/melissadata/client/vagrant.rb +9 -0
- data/lib/melissadata/command.rb +1 -0
- data/lib/melissadata/command/base.rb +0 -1
- data/lib/melissadata/command/compile.rb +37 -0
- data/lib/melissadata/command/package.rb +32 -73
- data/lib/melissadata/constants.rb +6 -5
- data/lib/melissadata/melissadata.rb +26 -91
- data/lib/melissadata/native_object.rb +0 -8
- data/lib/melissadata/native_object/address.rb +2 -0
- data/lib/melissadata/native_object/base.rb +18 -5
- data/lib/melissadata/native_object/email.rb +2 -0
- data/lib/melissadata/native_object/geo.rb +2 -0
- data/lib/melissadata/native_object/ip_locator.rb +2 -0
- data/lib/melissadata/native_object/name.rb +2 -0
- data/lib/melissadata/native_object/phone.rb +2 -0
- data/lib/melissadata/server.rb +10 -0
- data/lib/melissadata/server/base.rb +145 -0
- data/lib/melissadata/server/tcp.rb +22 -0
- data/lib/melissadata/server/unix.rb +24 -0
- data/lib/melissadata/vagrant_plugin.rb +23 -0
- data/lib/melissadata/vagrant_plugin/actions.rb +9 -0
- data/lib/melissadata/vagrant_plugin/actions/base.rb +60 -0
- data/lib/melissadata/vagrant_plugin/actions/install.rb +78 -0
- data/lib/melissadata/vagrant_plugin/actions/update.rb +22 -0
- data/lib/melissadata/vagrant_plugin/command.rb +21 -0
- data/lib/melissadata/vagrant_plugin/config.rb +18 -0
- data/lib/melissadata/version.rb +1 -1
- data/locales/en.yml +16 -0
- data/melissadata.gemspec +11 -12
- data/pkg/melissadata-0.0.1.gem +0 -0
- data/spec/helpers/vagrant_helpers.rb +17 -0
- data/spec/melissadata/command/package_spec.rb +1 -1
- data/spec/melissadata/vagrant_plugin/actions/install_spec.rb +40 -0
- data/spec/melissadata/vagrant_plugin/config_spec.rb +17 -0
- data/spec/spec_helper.rb +12 -7
- metadata +201 -228
- data/bin/md +0 -4
- data/bin/md-rpc +0 -9
- data/bin/md-server +0 -18
- data/bin/melissadata +0 -4
- data/lib/melissadata/native_object/client.rb +0 -33
- data/lib/melissadata/prev/command_old.rb +0 -80
- data/lib/melissadata/prev/shared_objects.rb +0 -54
- data/lib/melissadata/rpc.rb +0 -39
- data/pkg/melissa-data-0.0.1.gem +0 -0
- data/pkg/melissa-data-0.0.2.gem +0 -0
- data/pkg/melissa-data-0.0.3.gem +0 -0
- data/templates/config.rb +0 -20
- data/templates/locales/en.yml +0 -4
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'msgpack'
|
2
|
+
require 'msgpack/rpc'
|
3
|
+
|
4
|
+
module MelissaData::Client
|
5
|
+
|
6
|
+
class Base
|
7
|
+
attr_reader :rpc
|
8
|
+
|
9
|
+
def self.available?
|
10
|
+
raise 'I should have been subclassed'
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize(*args)
|
14
|
+
raise 'I should have been subclassed'
|
15
|
+
end
|
16
|
+
|
17
|
+
def process(opts={})
|
18
|
+
rpc.call :process, opts
|
19
|
+
end
|
20
|
+
|
21
|
+
def process_address(address=nil, zip=nil, city=nil, state=nil)
|
22
|
+
rpc.call :process_address, address, zip, city, state
|
23
|
+
end
|
24
|
+
alias_method :address, :process_address
|
25
|
+
|
26
|
+
def process_email(email=nil)
|
27
|
+
rpc.call :process_email, email
|
28
|
+
end
|
29
|
+
alias_method :email, :process_email
|
30
|
+
|
31
|
+
def process_geo(zip=nil, plus4=nil, dpc=nil)
|
32
|
+
rpc.call :process_geo, zip, plus4, dpc
|
33
|
+
end
|
34
|
+
alias_method :geo, :process_geo
|
35
|
+
|
36
|
+
def process_name(first_name=nil, last_name=nil)
|
37
|
+
rpc.call :process_name, first_name, last_name
|
38
|
+
end
|
39
|
+
alias_method :name, :process_name
|
40
|
+
|
41
|
+
def process_phone(phone=nil, zip=nil)
|
42
|
+
rpc.call :process_phone, phone, zip
|
43
|
+
end
|
44
|
+
alias_method :phone, :process_phone
|
45
|
+
|
46
|
+
def process_ip(ip)
|
47
|
+
rpc.call :process_ip, ip
|
48
|
+
end
|
49
|
+
alias_method :ip, :process_ip
|
50
|
+
|
51
|
+
def version
|
52
|
+
rpc.call :version
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module MelissaData::Client
|
2
|
+
|
3
|
+
class TCP < Base
|
4
|
+
class << self
|
5
|
+
def available?
|
6
|
+
TCPSocket.new(host, port)
|
7
|
+
true
|
8
|
+
rescue Errno::ECONNREFUSED
|
9
|
+
false
|
10
|
+
end
|
11
|
+
|
12
|
+
def host
|
13
|
+
'127.0.0.1'
|
14
|
+
end
|
15
|
+
|
16
|
+
def port
|
17
|
+
MelissaData::DEFAULT_TCP_PORT
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def initialize(host=self.class.host, port=self.class.port)
|
22
|
+
@host = host
|
23
|
+
@port = port
|
24
|
+
@rpc = MessagePack::RPC::Client.new(@host, @port)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'rev'
|
2
|
+
require 'msgpack/rpc/transport/base'
|
3
|
+
require 'msgpack/rpc/transport/unix'
|
4
|
+
|
5
|
+
module MelissaData::Client
|
6
|
+
|
7
|
+
class Unix < Base
|
8
|
+
def self.available?
|
9
|
+
File.exists? socket_file
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.socket_file
|
13
|
+
MelissaData::DEFAULT_SOCKET_FILE
|
14
|
+
end
|
15
|
+
|
16
|
+
def initialize
|
17
|
+
transport = MessagePack::RPC::UNIXTransport.new
|
18
|
+
@rpc = MessagePack::RPC::Client.new transport, self.class.socket_file
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
data/lib/melissadata/command.rb
CHANGED
@@ -0,0 +1,37 @@
|
|
1
|
+
module MelissaData
|
2
|
+
module Command
|
3
|
+
class CompileCommand < Base
|
4
|
+
register "compile", "Compile the MelissaData shared objects"
|
5
|
+
|
6
|
+
def self.source_root
|
7
|
+
Dir['/Volumes/*'].detect{ |path| path =~ /DVD-\d{4}-\d{2}$/ }
|
8
|
+
end
|
9
|
+
|
10
|
+
def execute
|
11
|
+
source_dir = Dir['/Volumes/*'].detect{ |path| path =~ /DVD-\d{4}-\d{2}$/ }
|
12
|
+
raise Errors::DiscNotFound if source_dir.blank?
|
13
|
+
|
14
|
+
dest_dir = "/tmp"
|
15
|
+
|
16
|
+
possible_objects = %w[ address email name phone ]
|
17
|
+
# objects = Dir.entries(source_dir).select{ |path| possible_objects.include?(path) }
|
18
|
+
objects = %w[ email name ]
|
19
|
+
|
20
|
+
objects.each do |obj|
|
21
|
+
Dir["#{source_dir}/#{obj}/linux/gcc34_64bit/*.h"].each do |filename|
|
22
|
+
copy_file filename, "/opt/melissadata/src/#{File.basename(filename)}"
|
23
|
+
end
|
24
|
+
|
25
|
+
filename = "#{source_dir}/#{obj}/linux/gcc34_64bit/libmd#{obj.capitalize}.so"
|
26
|
+
copy_file filename, "/opt/melissadata/lib/#{File.basename(filename)}"
|
27
|
+
|
28
|
+
filename = "#{source_dir}/#{obj}/linux/interfaces/ruby/md#{obj.capitalize}RubyWrapper.cpp"
|
29
|
+
copy_file filename, "/opt/melissadata/lib/#{File.basename(filename)}"
|
30
|
+
end
|
31
|
+
|
32
|
+
template "Makefile", "/opt/melissadata/src/Makefile"
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -1,83 +1,42 @@
|
|
1
1
|
module MelissaData
|
2
2
|
module Command
|
3
|
+
|
3
4
|
class PackageCommand < Base
|
4
|
-
# class_option :base, :type => :string, :default => nil
|
5
|
-
# class_option :output, :type => :string, :default => nil
|
6
|
-
# class_option :include, :type => :array, :default => nil
|
7
|
-
# class_option :melissadatafile, :type => :string, :default => nil
|
8
5
|
register "package", "Package up the MelissaData databases"
|
9
6
|
|
10
7
|
def execute
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
# # "#{source_dir}/#{obj}/linux/gcc34_64bit/*.h" => /opt/melissadata/src/
|
42
|
-
# # "#{source_dir}/#{obj}/linux/gcc34_64bit/libmdEmail.so" => /opt/melissadata/lib/
|
43
|
-
# # "#{source_dir}/#{obj}/linux/interfaces/ruby/mdEmailRubyWrapper.cpp" => /opt/melissadata/lib/
|
44
|
-
|
45
|
-
# # inside "#{source_dir}/#{obj}/linux/gcc34_64bit" do
|
46
|
-
# # run "tar czf #{data_file} data"
|
47
|
-
# # end
|
48
|
-
# end
|
49
|
-
# end
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
# return package_base if options[:base]
|
54
|
-
# package_target
|
8
|
+
source_dir = Dir['/Volumes/*'].detect{ |path| path =~ /DVD-\d{4}-\d{2}$/ }
|
9
|
+
raise Errors::DiscNotFound if source_dir.blank?
|
10
|
+
|
11
|
+
# dest_dir = "/tmp"
|
12
|
+
|
13
|
+
# possible_objects = %w[ address email name phone ]
|
14
|
+
# # objects = Dir.entries(source_dir).select{ |path| possible_objects.include?(path) }
|
15
|
+
# objects = %w[ email name ]
|
16
|
+
|
17
|
+
# objects.each do |obj|
|
18
|
+
# data_file = "#{dest_dir}/#{obj}.tgz"
|
19
|
+
# inside "#{source_dir}/#{obj}" do
|
20
|
+
# run "tar czf #{data_file} data"
|
21
|
+
# end
|
22
|
+
|
23
|
+
# say_status :upload, "#{obj}.tgz to #{options[:bucket_name]}"
|
24
|
+
# cf = CloudFiles::Connection.new(:username => options[:username], :api_key => options[:api_key])
|
25
|
+
# cont = cf.container options[:bucket_name]
|
26
|
+
# remote_object = cont.create_object "#{obj}.tgz", false
|
27
|
+
# remote_object.load_from_filename data_file
|
28
|
+
|
29
|
+
# # "#{source_dir}/#{obj}/linux/gcc34_64bit/*.h" => /opt/melissadata/src/
|
30
|
+
# # "#{source_dir}/#{obj}/linux/gcc34_64bit/libmdEmail.so" => /opt/melissadata/lib/
|
31
|
+
# # "#{source_dir}/#{obj}/linux/interfaces/ruby/mdEmailRubyWrapper.cpp" => /opt/melissadata/lib/
|
32
|
+
|
33
|
+
# # inside "#{source_dir}/#{obj}/linux/gcc34_64bit" do
|
34
|
+
# # run "tar czf #{data_file} data"
|
35
|
+
# # end
|
36
|
+
# end
|
37
|
+
# end
|
55
38
|
end
|
56
|
-
|
57
|
-
protected
|
58
|
-
|
59
|
-
# def package_base
|
60
|
-
# vm = VM.find(options[:base], env)
|
61
|
-
# raise Errors::BaseVMNotFound, :name => options[:base] if !vm.created?
|
62
|
-
# package_vm(vm)
|
63
|
-
# end
|
64
|
-
|
65
|
-
# def package_target
|
66
|
-
# raise Errors::MultiVMTargetRequired, :command => "package" if target_vms.length > 1
|
67
|
-
# vm = target_vms.first
|
68
|
-
# raise Errors::VMNotCreatedError if !vm.created?
|
69
|
-
# package_vm(vm)
|
70
|
-
# end
|
71
|
-
|
72
|
-
# def package_vm(vm)
|
73
|
-
# opts = options.inject({}) do |acc, data|
|
74
|
-
# k,v = data
|
75
|
-
# acc["package.#{k}"] = v
|
76
|
-
# acc
|
77
|
-
# end
|
78
|
-
|
79
|
-
# vm.package(opts)
|
80
|
-
# end
|
81
39
|
end
|
40
|
+
|
82
41
|
end
|
83
42
|
end
|
@@ -1,12 +1,13 @@
|
|
1
1
|
module MelissaData
|
2
|
-
|
2
|
+
|
3
3
|
module Constants
|
4
|
-
|
5
|
-
|
4
|
+
DEFAULT_TCP_ADDRESS = '0.0.0.0'
|
5
|
+
DEFAULT_TCP_PORT = 1404
|
6
|
+
DEFAULT_SOCKET_FILE = '/tmp/melissadata.sock'
|
6
7
|
|
7
|
-
|
8
|
-
DEFAULT_PORT = 6000
|
8
|
+
DEFAULT_VAGRANT_TCP_PORT = 14004
|
9
9
|
end
|
10
|
+
|
10
11
|
end
|
11
12
|
|
12
13
|
module MelissaData
|
@@ -1,18 +1,37 @@
|
|
1
1
|
$LOAD_PATH.unshift File.expand_path('..', File.dirname(__FILE__)) unless $LOAD_PATH.include?(File.expand_path('..', File.dirname(__FILE__)))
|
2
|
+
require 'rubygems'
|
2
3
|
require 'pathname'
|
3
4
|
require 'i18n'
|
4
5
|
require 'thor'
|
5
6
|
require 'thor/group'
|
6
7
|
require 'thor/actions'
|
7
|
-
require 'active_support/core_ext'
|
8
8
|
|
9
9
|
module MelissaData
|
10
10
|
module_function
|
11
11
|
|
12
|
-
autoload :Env,
|
13
|
-
autoload :CLI,
|
14
|
-
autoload :Errors,
|
15
|
-
autoload :Constants,
|
12
|
+
autoload :Env, 'melissadata/env'
|
13
|
+
autoload :CLI, 'melissadata/cli'
|
14
|
+
autoload :Errors, 'melissadata/errors'
|
15
|
+
autoload :Constants, 'melissadata/constants'
|
16
|
+
autoload :NativeObject, 'melissadata/native_object'
|
17
|
+
autoload :VagrantPlugin, 'melissadata/vagrant_plugin'
|
18
|
+
autoload :Server, 'melissadata/server'
|
19
|
+
autoload :Client, 'melissadata/client'
|
20
|
+
|
21
|
+
def client
|
22
|
+
if MelissaData::Client::Unix.available?
|
23
|
+
MelissaData::Client::Unix.new
|
24
|
+
|
25
|
+
elsif MelissaData::Client::TCP.available?
|
26
|
+
MelissaData::Client::TCP.new
|
27
|
+
|
28
|
+
elsif MelissaData::Client::Vagrant.available?
|
29
|
+
MelissaData::Client::Vagrant.new
|
30
|
+
|
31
|
+
else
|
32
|
+
raise 'No client connection available'
|
33
|
+
end
|
34
|
+
end
|
16
35
|
|
17
36
|
ENVIRONMENTS = [:development, :production, :test, :staging]
|
18
37
|
|
@@ -73,92 +92,8 @@ module MelissaData
|
|
73
92
|
end
|
74
93
|
end
|
75
94
|
|
76
|
-
I18n.load_path << File.expand_path("
|
95
|
+
I18n.load_path << File.expand_path("locales/en.yml", MelissaData.gem_root)
|
77
96
|
|
97
|
+
require 'melissadata/constants'
|
78
98
|
require 'melissadata/command'
|
79
99
|
require 'melissadata/version'
|
80
|
-
|
81
|
-
|
82
|
-
# require 'rubygems'
|
83
|
-
# require 'active_support/core_ext'
|
84
|
-
# require 'melissadata/constants'
|
85
|
-
# require 'melissadata/commands'
|
86
|
-
# require 'melissadata/exceptions'
|
87
|
-
|
88
|
-
# module MelissaData
|
89
|
-
# module_function
|
90
|
-
|
91
|
-
# ENVIRONMENTS = [:development, :production, :vagrant, :test, :staging]
|
92
|
-
|
93
|
-
# # Retrieves the current MelissaData environment
|
94
|
-
# #
|
95
|
-
# # @return [String] the current environment
|
96
|
-
# def env
|
97
|
-
# @env or fail "environment has not been set"
|
98
|
-
# end
|
99
|
-
|
100
|
-
# # Sets the current MelissaData environment
|
101
|
-
# #
|
102
|
-
# # @param [String|Symbol] env the environment symbol of [dev | development | prod | production | test]
|
103
|
-
# def env=(e)
|
104
|
-
# es = case(e.to_sym)
|
105
|
-
# when :dev then :development
|
106
|
-
# when :prod then :production
|
107
|
-
# else e.to_sym
|
108
|
-
# end
|
109
|
-
|
110
|
-
# if ENVIRONMENTS.include?(es)
|
111
|
-
# @env = es
|
112
|
-
# else
|
113
|
-
# fail "did not recognize environment: #{e}, expected one of: #{ENVIRONMENTS.join(', ')}"
|
114
|
-
# end
|
115
|
-
# end
|
116
|
-
|
117
|
-
# def client=(c)
|
118
|
-
# @client = c
|
119
|
-
# end
|
120
|
-
|
121
|
-
# def client
|
122
|
-
# raise "Client has not yet been setup" if @client.nil?
|
123
|
-
# @client
|
124
|
-
# end
|
125
|
-
|
126
|
-
# def lib_path
|
127
|
-
# @lib_path ||= File.dirname(__FILE__)
|
128
|
-
# end
|
129
|
-
|
130
|
-
# # Determines if we are in the production environment
|
131
|
-
# #
|
132
|
-
# # @return [Boolean] true if current environment is production, false otherwise
|
133
|
-
# def prod?
|
134
|
-
# env == :production
|
135
|
-
# end
|
136
|
-
|
137
|
-
# # Determines if we are in the development environment
|
138
|
-
# #
|
139
|
-
# # @return [Boolean] true if current environment is development, false otherwise
|
140
|
-
# def dev?
|
141
|
-
# env == :development
|
142
|
-
# end
|
143
|
-
|
144
|
-
# # Determines if we are in the test environment
|
145
|
-
# #
|
146
|
-
# # @return [Boolean] true if current environment is test, false otherwise
|
147
|
-
# def test?
|
148
|
-
# env == :test
|
149
|
-
# end
|
150
|
-
|
151
|
-
# # Determines if we are in the staging environment
|
152
|
-
# #
|
153
|
-
# # @return [Boolean] true if current environment is staging.
|
154
|
-
# def staging?
|
155
|
-
# env == :staging
|
156
|
-
# end
|
157
|
-
|
158
|
-
# # Determines if we are in the vagrant environment
|
159
|
-
# #
|
160
|
-
# # @return [Boolean] true if current environment is vagrant.
|
161
|
-
# def vagrant?
|
162
|
-
# env == :vagrant
|
163
|
-
# end
|
164
|
-
# end
|
@@ -1,8 +1,6 @@
|
|
1
1
|
module MelissaData
|
2
2
|
module NativeObject
|
3
3
|
autoload :Base, 'melissadata/native_object/base'
|
4
|
-
autoload :Client, 'melissadata/native_object/client'
|
5
|
-
|
6
4
|
autoload :Address, 'melissadata/native_object/address'
|
7
5
|
autoload :Email, 'melissadata/native_object/email'
|
8
6
|
autoload :Geo, 'melissadata/native_object/geo'
|
@@ -11,9 +9,3 @@ module MelissaData
|
|
11
9
|
autoload :Phone, 'melissadata/native_object/phone'
|
12
10
|
end
|
13
11
|
end
|
14
|
-
|
15
|
-
%w[
|
16
|
-
mdAddrRubyWrapper mdEmailRubyWrapper mdGeoRubyWrapper mdNameRubyWrapper mdPhoneRubyWrapper mdIpLocatorRubyWrapper
|
17
|
-
].each do |obj|
|
18
|
-
require File.join('/opt/melissadata/lib', obj)
|
19
|
-
end
|