mylookup 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 6d2aec87813030f47044c072473c3ee422ee1852
4
+ data.tar.gz: d3e9aeec030a2fe15ae538151f6f3d73736fa754
5
+ SHA512:
6
+ metadata.gz: a868b087ef7d1014bf70053f05f9b0452010af16c43cf2c00dc5c4dd8a38bbeea3a1909be33eb941f2c6c18f7e9190c1ba52b644f46071090c24cd72e3ef375f
7
+ data.tar.gz: 4682498bb757620f6004a963e3cef126ae39cd33df0b701239015a0d3880220e617e7372ba15a5c465c87dae81277c75b3b170e7a0ae215c186ce266b03d3fe2
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "vlookup"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
@@ -0,0 +1,30 @@
1
+ require 'mylookup'
2
+ require 'optparse'
3
+
4
+ options = {}
5
+ options_parser = OptionParser.new do |opts|
6
+ opts.on("-l", "--left LEFT", "Path to the Left Table") do |left|
7
+ options[:left] = left
8
+ end
9
+ opts.on("-r", "--right RIGHT", "Path to the Right Table") do |right|
10
+ options[:right] = right
11
+ end
12
+ opts.on("--lefton LEFTON", "Left Table's matching column") do |lefton|
13
+ options[:lefton] = lefton
14
+ end
15
+ opts.on("--righton RIGHTON", "Right Table's matching column") do |righton|
16
+ options[:righton] = righton
17
+ end
18
+ opts.on("--leftsheet LEFTSHEET", "Left Table's sheet name") do |leftsheet|
19
+ options[:leftsheet] = leftsheet
20
+ end
21
+ opts.on("--rightsheet RIGHTSHEET", "Right Table's sheet name") do |rightsheet|
22
+ options[:rightsheet] = rightsheet
23
+ end
24
+ opts.on("v", "--[no-]verbose", "Run verbosely") do |v|
25
+ options[:verbose] = v
26
+ end
27
+ end
28
+
29
+ options_parser.parse!
30
+ Mylookup.run(options)
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,156 @@
1
+ require "mylookup/version"
2
+ require "mylookup/validator"
3
+ require "mylookup/processor"
4
+
5
+ #= Entry point to startup the application
6
+ module Mylookup
7
+
8
+ # Entry point method that starts the whole app
9
+ def self.run(opts)
10
+ validate_options opts
11
+ l_src = source_type opts[:left], opts[:leftsheet], :left
12
+ r_src = source_type opts[:right], opts[:rightsheet], :right
13
+ options = {
14
+ :left => opts[:left],
15
+ :right => opts[:right],
16
+ :lefton => opts[:lefton],
17
+ :righton => opts[:righton],
18
+ :leftsheet => opts[:leftsheet],
19
+ :rightsheet => opts[:rightsheet],
20
+ :verbose => opts[:verbose],
21
+ :l_src => l_src,
22
+ :r_src => r_src,
23
+ }
24
+ validate_sources l_src, r_src, options
25
+ processor = Processor.new(options)
26
+ processor.process
27
+ end
28
+
29
+ # Health check up on options entered
30
+ def self.validate_options opts
31
+ unless opts[:left]
32
+ puts "[Error]: Left database/file name must be defined"
33
+ exit
34
+ else
35
+ unless opts[:leftsheet]
36
+ puts "[Warning] : Left table name must be defined in case of source being MongoDB"
37
+ puts "[Warning--cont'd]: First/Default sheet is assumed to be Left Table/Sheet "
38
+ else
39
+ unless opts[:lefton]
40
+ puts "[Error]: Left table/sheet matching column must be defined"
41
+ exit
42
+ end
43
+ end
44
+ end
45
+ unless opts[:right]
46
+ puts "[Error]: Right Database/File name must be defined"
47
+ exit
48
+ else
49
+ unless opts[:rightsheet]
50
+ puts "[Warning] : Right Collection name must be defined in case of source being MongoDB"
51
+ puts "[Warning--cont'd]: First/Default Sheet is assumed to be Right Table/Sheet "
52
+ else
53
+ unless opts[:righton]
54
+ puts "[Error]: Right table/sheet matching column must be defined"
55
+ exit
56
+ end
57
+ end
58
+ end
59
+ return true
60
+ end
61
+
62
+ # Validates the existence of fields/sheets of the left and right tables
63
+ def self.validate_sources l_src, r_src, ops
64
+ l_path, l_tbl, l_col = ops[:left], ops[:leftsheet], ops[:lefton]
65
+ r_path, r_tbl, r_col = ops[:right], ops[:rightsheet], ops[:righton]
66
+ if l_src == :excel
67
+ sht_comment, col_comment = validate_excel_attribs(l_path, l_tbl, l_col, :left)
68
+ puts "[Info]: Left Table => #{sht_comment} | #{col_comment}" if ops[:verbose]
69
+ elsif l_src == :mongo
70
+ db = File.split(l_path)[1]
71
+ valid, comment = validate_mongo_field_existence l_tbl, db, l_col
72
+ puts "[Info]: Left Table => #{comment}" if ops[:verbose]
73
+ end
74
+ if r_src == :excel
75
+ sht_comment, col_comment = validate_excel_attribs(r_path, r_tbl, r_col, :right)
76
+ puts "[Info]: Right Table => #{sht_comment} | #{col_comment}" if ops[:verbose]
77
+ elsif r_src == :mongo
78
+ db = File.split(r_path)[1]
79
+ valid, comment = validate_mongo_field_existence r_tbl, db, r_col
80
+ puts "[Info]: Right Table => #{comment}" if ops[:verbose]
81
+ end
82
+ end
83
+
84
+ # Validates the existence of sheet and matching column in an excel file
85
+ def self.validate_excel_attribs path, sht_name, col_name, src_type
86
+ valid, sht_comment = validate_excel_sheet_existence sht_name, path
87
+ valid, col_comment = validate_excel_column_existence col_name, sht_name, path, src_type
88
+ return sht_comment, col_comment
89
+ end
90
+
91
+ # Validates and gives the type of the file whether it is an excel file or
92
+ # Mongo DB collection
93
+ def self.source_type file_name, sht, what_tbl
94
+ unless file_name
95
+ puts "[Error]: #{what_tbl.to_s.capitalize} Table's path must be given"
96
+ exit
97
+ else
98
+ valid, comment = validate_file_existence file_name, sht
99
+ comment =~ /mongodb/i ? :mongo : :excel
100
+ end
101
+ end
102
+
103
+ # Validates whether the given file does exist or not
104
+ def self.validate_file_existence path, sht
105
+ valid, comment = Validator::FileValidator.new(path, coll: sht).validate
106
+ unless valid
107
+ puts "[Error]: #{comment}"
108
+ exit
109
+ else
110
+ return valid, comment
111
+ end
112
+ end
113
+
114
+ # Validates whether the given sheet does exist or not
115
+ def self.validate_excel_sheet_existence sht, path
116
+ sht_name = sht
117
+ sht_name = 0 unless sht_name
118
+ validator = Validator::ExcelAttribValidator.new(path, sht_name)
119
+ valid, comment = validator.validate_sheet
120
+ unless valid
121
+ puts "[Error]: #{comment}"
122
+ exit
123
+ else
124
+ return valid, comment
125
+ end
126
+ end
127
+
128
+ # Validates whether the given column does exist or not
129
+ def self.validate_excel_column_existence col, sht, path, what_tbl
130
+ unless col
131
+ puts "[Error]: #{what_tbl.to_s.capitalize} Table's matching column must be given"
132
+ exit
133
+ end
134
+ validator = Validator::ExcelAttribValidator.new(path, sht)
135
+ valid, comment = validator.validate_column col
136
+ unless valid
137
+ puts "[Error]: #{comment}"
138
+ exit
139
+ else
140
+ return valid, comment
141
+ end
142
+ end
143
+
144
+ # Validates whether the given column does exist or not
145
+ def self.validate_mongo_field_existence coll_name, db_name, field
146
+ validator = Validator::MongoAttribValidator.new(coll_name, db_name, field)
147
+ valid, comment = validator.validate_field
148
+ unless valid
149
+ puts "[Error]: #{comment}"
150
+ exit
151
+ else
152
+ return valid, comment
153
+ end
154
+ end
155
+
156
+ end
@@ -0,0 +1,66 @@
1
+ #= Module containing connectors to databases
2
+
3
+ module Connector
4
+
5
+ #== Defines MongoDB connection properties
6
+ class MongoConnector
7
+ require 'mongo'
8
+
9
+ attr_accessor :client, :coll, :coll_name, :db_name, :host, :port
10
+
11
+ def initialize(coll_name, db_name: 'ccsdm', host: 'localhost', port: '27017')
12
+ @coll_name, @db_name, @host, @port = coll_name, db_name, host, port
13
+ Mongo::Logger.logger.level = Logger::WARN
14
+ @conn_str = @host + ':' + @port
15
+ @client = Mongo::Client.new([@conn_str], :database => @db_name)
16
+ @coll = @client[@coll_name]
17
+ end
18
+
19
+ # Queries and returns number of documents in the collection
20
+ def recs qry
21
+ @coll.find(qry).count
22
+ end
23
+
24
+ def collection_exists?
25
+ recs({}) > 0
26
+ end
27
+
28
+ def field_exists? field
29
+ @coll.find({}).projection({ '_id' => 0 }).limit(1).collect { |doc| doc }[0].keys.include? field
30
+ end
31
+
32
+ public :recs, :collection_exists?
33
+ end
34
+
35
+ class ExcelReadConnector
36
+ require 'roo'
37
+
38
+ def initialize(path)
39
+ @path = path
40
+ @wb = Roo::Excelx.new(@path)
41
+ @sheets = @wb.sheets
42
+ end
43
+
44
+ def sheet_exists? sht_name
45
+ @sheets.include? sht_name
46
+ end
47
+
48
+ def column_exists? sht, col_name
49
+ @wb.sheet(sht).row(1).include? col_name
50
+ end
51
+ end
52
+
53
+ class ExcelWriteConnector
54
+ require 'rubyXL'
55
+
56
+ attr_accessor :wb, :ws
57
+
58
+ def initialize(path)
59
+ @path = path
60
+ @wb = RubyXL::Workbook.new
61
+ @ws = @wb.worksheets[0]
62
+ end
63
+
64
+ end
65
+
66
+ end
@@ -0,0 +1,61 @@
1
+ require 'mylookup/reader'
2
+ require 'mylookup/writer'
3
+
4
+ #= class containing the functionalities of handling the whole show
5
+ class Processor
6
+
7
+ def initialize(opts)
8
+ @l_db, @l_tbl, @l_on = opts[:left], opts[:leftsheet], opts[:lefton]
9
+ @r_db, @r_tbl, @r_on = opts[:right], opts[:rightsheet], opts[:righton]
10
+ @verbose = opts[:verbose]
11
+ @l_src, @r_src = opts[:l_src], opts[:r_src]
12
+ @l_reader, @r_reader = nil, nil
13
+ @l_data, @r_data, @matched, @unmatched = nil, nil, nil, nil
14
+ if @l_src == :excel
15
+ @l_reader = FileReader::Excel.new(@l_db, @l_tbl, @l_on)
16
+ else
17
+ @l_reader = FileReader::MongoDB.new(@l_on, @l_tbl, db_name: @l_db)
18
+ end
19
+ if @r_src == :excel
20
+ @r_reader = FileReader::Excel.new(@r_db, @r_tbl, @r_on)
21
+ else
22
+ @r_reader = FileReader::MongoDB.new(@r_on, @r_tbl, db_name: @r_db)
23
+ end
24
+ end
25
+
26
+ def process
27
+ puts "Processing initiating..."
28
+ read_data
29
+ mylookup
30
+ write_unmatched unless @unmatched.empty?
31
+ end
32
+
33
+ def mylookup
34
+ puts "Executing mylookup..."
35
+ @unmatched = @l_data - @r_data
36
+ @matched = @l_data - @unmatched
37
+ puts "[Info]: Left Table size: #{@l_data.size} row(s)"
38
+ puts "Matched: #{@matched.size} row(s) Unmatched: #{@unmatched.size} row(s)"
39
+ puts "Matched: #{(@matched.size.to_f*100/@l_data.size.to_f).round(2)}%"
40
+ end
41
+
42
+ def write_unmatched
43
+ writer = FileWriter::Excel.new('unmatched.xlsx', @unmatched, @l_on)
44
+ writer.write
45
+ end
46
+
47
+ def read_data
48
+ puts "Reading Left Table data..."
49
+ l_comment = @l_reader.read
50
+ @l_data = @l_reader.data
51
+ puts "Reading Right Table data..."
52
+ r_comment = @r_reader.read
53
+ @r_data = @r_reader.data
54
+ puts "[Info]: LEFT =>#{l_comment}"
55
+ puts "[Info]: RIGHT=>#{r_comment}"
56
+ end
57
+
58
+ private :read_data, :mylookup, :write_unmatched
59
+ public :process
60
+
61
+ end
@@ -0,0 +1,61 @@
1
+ require 'mylookup/connector'
2
+
3
+
4
+ #= Contains funtionalities of reading data
5
+ module FileReader
6
+
7
+ #== Contains the functionalities of reading Excel data
8
+ class Excel < Connector::ExcelReadConnector
9
+ attr_reader :data
10
+
11
+ def initialize(path, sht_name, col_name)
12
+ super(path)
13
+ @sht = @wb.sheet(sht_name)
14
+ @col = col_name
15
+ @data = nil
16
+ end
17
+
18
+ def read(match: {}, hide: {}, q_meth: :find)
19
+ aoa = @sht.parse(@col.to_sym => @col)
20
+ @data = aoa.collect { |item| item[@col.to_sym].to_s.downcase }
21
+ @data = @data.uniq
22
+ return "Excel Data contains #{@data.size} row(s)\nExcel First Record => #{@data[0]}"
23
+ end
24
+
25
+ public :read
26
+
27
+ end
28
+
29
+ #== Contains the reading functionalities of MongoDB data
30
+ class MongoDB < Connector::MongoConnector
31
+ attr_reader :data
32
+
33
+ def initialize(col_name, coll_name, db_name: 'ccsdm', host: 'localhost', port: '27017')
34
+ super(coll_name, db_name: db_name, host: host, port: port)
35
+ @col = col_name
36
+ @data = []
37
+ end
38
+
39
+ def read(hide: { '_id' => 0 }, q_meth: :agg, match: {})
40
+ if q_meth == :find
41
+ @coll.find(match).projection(hide).each do |doc|
42
+ @data = @data + [doc[@col].to_s.downcase]
43
+ end
44
+ @data = @data.uniq
45
+ elsif q_meth == :agg
46
+ qry = [
47
+ { '$match' => match },
48
+ { '$group' => { '_id' => { @col => '$' + @col } } },
49
+ { '$project' => { '_id' => 0, @col => '$_id.' + @col } }
50
+ ]
51
+ @coll.aggregate(qry).each do |doc|
52
+ @data = @data + [doc[@col].to_s.downcase]
53
+ end
54
+ end
55
+ return "Mongo Data contains #{@data.size} row(s)\nMongo First Record => #{@data[0]}"
56
+ end
57
+
58
+ public :read
59
+ end
60
+
61
+ end
@@ -0,0 +1,97 @@
1
+ #= Validation module to checks all the base rules in input options
2
+
3
+ require 'mylookup/connector'
4
+
5
+ module Validator
6
+
7
+ #== Excel file attributes and properties validator
8
+ class ExcelAttribValidator < Connector::ExcelReadConnector
9
+
10
+ def initialize(path, sht)
11
+ super(path)
12
+ @sht = sht
13
+ end
14
+
15
+ def validate_sheet
16
+ if sheet_exists? @sht
17
+ [true, "#{@sht} sheet in #{@path} exists"]
18
+ else
19
+ [false, "#{@sht} sheet in #{@path} DOES NOT exist!"]
20
+ end
21
+ end
22
+
23
+ def validate_column col_name
24
+ if column_exists? @sht, col_name
25
+ [true, "#{col_name} column in #{@sht} sheet of #{@path} exists"]
26
+ else
27
+ [false, "#{col_name} column in #{@sht} sheet of #{@path} DOES NOT exist!"]
28
+ end
29
+ end
30
+
31
+ public :validate_sheet, :validate_column
32
+
33
+ end
34
+
35
+ #== MongoDB collection attributes and properties validator
36
+ class MongoAttribValidator < Connector::MongoConnector
37
+
38
+ def initialize(coll_name, db, field)
39
+ super(coll_name, db_name: db)
40
+ @field = field
41
+ end
42
+
43
+ def validate_field
44
+ if field_exists? @field
45
+ [true, "'#{@field}' field exists in '#{@coll_name}' collection"]
46
+ else
47
+ [false, "'#{@field}' field DOES NOT exist in '#{@coll_name}' collection"]
48
+ end
49
+ end
50
+
51
+ public :validate_field
52
+
53
+ end
54
+
55
+ #== File Validator to validate the input paths of a file
56
+ class FileValidator
57
+
58
+ def initialize(path, coll: '')
59
+ @path = path
60
+ @coll = coll
61
+ end
62
+
63
+ def validate
64
+ if is_excel_file
65
+ if excel_file_exists
66
+ [true, "#{@path} does exist"]
67
+ else
68
+ return [false, "#{@path} does not exist"]
69
+ end
70
+ else
71
+ if @path =~ /\./i
72
+ return false, "#{@path} is neither an excel file nor a MongoDB collection"
73
+ else
74
+ db = File.split(@path)[1]
75
+ mongo_conn = Connector::MongoConnector.new(@coll, db_name: db)
76
+ unless mongo_conn.collection_exists?
77
+ return [false, "'#{@path}' collection in MongoDB does not exist"]
78
+ else
79
+ return true, "'#{@path}' is a MongoDB collection"
80
+ end
81
+ end
82
+ end
83
+ end
84
+
85
+ def excel_file_exists
86
+ File.file?(@path)
87
+ end
88
+
89
+ def is_excel_file
90
+ @path[-5, 5] == '.xlsx'
91
+ end
92
+
93
+ private :is_excel_file, :excel_file_exists
94
+
95
+ end
96
+
97
+ end
@@ -0,0 +1,3 @@
1
+ module Mylookup
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,43 @@
1
+ require 'mylookup/connector'
2
+
3
+
4
+ #=Contains functionalities of writing data
5
+ module FileWriter
6
+
7
+ #== Contains the functionalities of writing data in Excel
8
+ class Excel < Connector::ExcelWriteConnector
9
+
10
+ def initialize(path, data, header)
11
+ super(path)
12
+ @data = data
13
+ @header = header
14
+ end
15
+
16
+ def write
17
+ puts "Writing Data in Excel"
18
+ begin
19
+ write_header
20
+ write_data
21
+ rescue StandardError => err
22
+ puts "[Error]: Error occured while writing in Excel!!!"
23
+ puts err
24
+ end
25
+ end
26
+
27
+ def write_header
28
+ @ws.add_cell(0, 0, @header)
29
+ end
30
+
31
+ def write_data
32
+ @data.each_with_index do |d, i|
33
+ @ws.add_cell(i+1, 0, d.to_s.upcase)
34
+ end
35
+ @wb.write(@path)
36
+ end
37
+
38
+ private :write_header, :write_data
39
+ public :write
40
+ end
41
+
42
+ end
43
+
metadata ADDED
@@ -0,0 +1,159 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mylookup
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - jeyaraj
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-08-18 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.15'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.15'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: minitest
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '5.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '5.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rubyXL
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '3.3'
62
+ - - ">="
63
+ - !ruby/object:Gem::Version
64
+ version: 3.3.26
65
+ type: :development
66
+ prerelease: false
67
+ version_requirements: !ruby/object:Gem::Requirement
68
+ requirements:
69
+ - - "~>"
70
+ - !ruby/object:Gem::Version
71
+ version: '3.3'
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: 3.3.26
75
+ - !ruby/object:Gem::Dependency
76
+ name: roo
77
+ requirement: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - "~>"
80
+ - !ruby/object:Gem::Version
81
+ version: '2.7'
82
+ - - ">="
83
+ - !ruby/object:Gem::Version
84
+ version: 2.7.0
85
+ type: :development
86
+ prerelease: false
87
+ version_requirements: !ruby/object:Gem::Requirement
88
+ requirements:
89
+ - - "~>"
90
+ - !ruby/object:Gem::Version
91
+ version: '2.7'
92
+ - - ">="
93
+ - !ruby/object:Gem::Version
94
+ version: 2.7.0
95
+ - !ruby/object:Gem::Dependency
96
+ name: mongo
97
+ requirement: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - "~>"
100
+ - !ruby/object:Gem::Version
101
+ version: '2.4'
102
+ - - ">="
103
+ - !ruby/object:Gem::Version
104
+ version: 2.4.1
105
+ type: :development
106
+ prerelease: false
107
+ version_requirements: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - "~>"
110
+ - !ruby/object:Gem::Version
111
+ version: '2.4'
112
+ - - ">="
113
+ - !ruby/object:Gem::Version
114
+ version: 2.4.1
115
+ description: Does mylookup on Excel file or MongoDB collections
116
+ email:
117
+ - jeyaraj.durairaj@gmail.com
118
+ executables:
119
+ - console
120
+ - mylookup
121
+ - setup
122
+ extensions: []
123
+ extra_rdoc_files: []
124
+ files:
125
+ - bin/console
126
+ - bin/mylookup
127
+ - bin/setup
128
+ - lib/mylookup.rb
129
+ - lib/mylookup/connector.rb
130
+ - lib/mylookup/processor.rb
131
+ - lib/mylookup/reader.rb
132
+ - lib/mylookup/validator.rb
133
+ - lib/mylookup/version.rb
134
+ - lib/mylookup/writer.rb
135
+ homepage: https://github.com/jeydurai/mylookup
136
+ licenses:
137
+ - MIT
138
+ metadata: {}
139
+ post_install_message:
140
+ rdoc_options: []
141
+ require_paths:
142
+ - lib
143
+ required_ruby_version: !ruby/object:Gem::Requirement
144
+ requirements:
145
+ - - ">="
146
+ - !ruby/object:Gem::Version
147
+ version: '0'
148
+ required_rubygems_version: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
153
+ requirements: []
154
+ rubyforge_project:
155
+ rubygems_version: 2.6.12
156
+ signing_key:
157
+ specification_version: 4
158
+ summary: Simulates Excel's Mylookup function
159
+ test_files: []