spreadshit 0.1.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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 8458cafa69246fc6038f2ac8a6823569e39de820
4
+ data.tar.gz: 2982c754d56740b6234255cf2c90c50b141f78cd
5
+ SHA512:
6
+ metadata.gz: 0cc85771b717af20087bc7ee1a64da872a26143f6e40d2682acf8afe093eb850193e2737ab2676f5ff1a657b9c3f2523592028583d0516a5c30c2497aab575a7
7
+ data.tar.gz: 497cb849983596238a0a49b742517c126ddc4473abe2a730ba4540a03a1faee053a0eb74be2fedb0cdc05a538d875424e32fb84c7e9f8db58fac5919c2ab2a0b
data/.gitignore ADDED
@@ -0,0 +1 @@
1
+ Gemfile.lock
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
4
+
5
+ gem "pry"
data/README.md ADDED
@@ -0,0 +1,25 @@
1
+ # Spreadsheet
2
+
3
+ Esse projeto é um exercício sugerido pelo [Casssiano](https://github.com/cassiano), que consiste na implementação em uma planilha de cálculos.
4
+
5
+ Por ora, o projeto está em fase alpha, e não trata quase nenhum caso de excessão.
6
+
7
+ [![Obligatory gif](https://asciinema.org/a/4677dtu9ippkmzgttr85vnj22.png)](https://asciinema.org/a/4677dtu9ippkmzgttr85vnj22)
8
+
9
+ ## Implementação
10
+
11
+ Foram utilizados vários conceitos descritos no paper [Deprecating the Observer Pattern](http://infoscience.epfl.ch/record/148043/files/DeprecatingObserversTR2010.pdf) para implementação, já que cada célula representa um `Signal` descrito no paper.
12
+
13
+ Para o parsing das fórmulas da planilha, foi desenvolvido um simples parser com o [Treetop](http://treetop.rubyforge.org/).
14
+
15
+ A UI (ainda bastante simplista) foi produzida com a ajuda da biblioteca [Curses](https://en.wikipedia.org/wiki/Curses_(programming_library)).
16
+
17
+ ## Demo
18
+
19
+ Para rodar o demo, primeiro instale as dependências com:
20
+
21
+ $ bundle install
22
+
23
+ E depois execute
24
+
25
+ $ ./demo.rb
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/bin/console ADDED
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "spreadshit"
5
+ require "pry"
6
+
7
+ @sheet = Spreadshit.new
8
+ @sheet[:A1] = 10
9
+ @sheet[:A2] = "0"
10
+ @sheet[:A3] = "=A1 + A2"
11
+
12
+ @sheet[:B1] = 0
13
+ @sheet[:B2] = 1
14
+ 3.upto(300).each do |n|
15
+ @sheet["B#{n}"] = "=B#{n - 1} + B#{n - 2}"
16
+ end
17
+
18
+ Pry.start
data/bin/demo ADDED
@@ -0,0 +1,34 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "spreadshit"
5
+ require "spreadshit/window"
6
+
7
+ sheet = Spreadshit.new
8
+ [
9
+ "Sum",
10
+ 10,
11
+ 20,
12
+ 30,
13
+ "",
14
+ "=A2 + A3 + A4",
15
+ "=SUM(A2; A3; A4)",
16
+ "=SUM(A2:A4)"
17
+ ].each_with_index do |cel, index|
18
+ sheet["A#{index + 1}"] = cel
19
+ end
20
+
21
+ # Fibonacci
22
+ sheet[:B1] = "Fibonacci"
23
+ sheet[:B2] = 0
24
+ sheet[:B3] = 1
25
+ 4.upto(30).each do |n|
26
+ sheet["B#{n}"] = "=B#{n - 1} + B#{n - 2}"
27
+ end
28
+
29
+ window = Spreadshit::Window.new do |delegate|
30
+ delegate.cell_updated { |address, value| sheet[address] = value }
31
+ delegate.cell_value { |address| sheet[address] }
32
+ delegate.cell_content { |address| sheet.cell_at(address).raw }
33
+ end
34
+ window.start
data/bin/setup ADDED
@@ -0,0 +1,6 @@
1
+ #!/bin/bash
2
+
3
+ set -euo pipefail
4
+ IFS=$'\n\t'
5
+
6
+ bundle install
data/lib/spreadshit.rb ADDED
@@ -0,0 +1,112 @@
1
+ class Spreadshit
2
+ require "spreadshit/formula"
3
+ require "spreadshit/functions"
4
+
5
+ def initialize(parser = Formula.new, functions = Functions.new)
6
+ @cells = Hash.new { |hash, key| hash[key] = Cell.new }
7
+ @parser = parser
8
+ @functions = functions
9
+ end
10
+
11
+ def [](address)
12
+ @cells[address.to_sym].value
13
+ end
14
+
15
+ def []=(address, value)
16
+ @cells[address.to_sym].update(value) { parse(value) }
17
+ end
18
+
19
+ def cell_at(address)
20
+ @cells[address.to_sym]
21
+ end
22
+
23
+ private
24
+
25
+ def parse(value)
26
+ if value.to_s.start_with? "="
27
+ eval @parser.parse(value[1..-1])
28
+ else
29
+ value
30
+ end
31
+ end
32
+
33
+ def eval(expression, refs = Set.new)
34
+ case expression
35
+ when Formula::Literal
36
+ expression.content
37
+ when Formula::Addition
38
+ @functions.add(eval(expression.left, refs), eval(expression.right, refs))
39
+ when Formula::Subtraction
40
+ @functions.minus(eval(expression.left, refs), eval(expression.right, refs))
41
+ when Formula::Multiplication
42
+ @functions.multiply(eval(expression.left, refs), eval(expression.right, refs))
43
+ when Formula::Division
44
+ @functions.divide(eval(expression.left, refs), eval(expression.right, refs))
45
+ when Formula::Function
46
+ @functions.send(expression.name.downcase, *expression.arguments.map { |arg| eval arg, refs })
47
+ when Formula::Reference
48
+ if refs.include? expression.address
49
+ "CYCLIC"
50
+ else
51
+ refs << expression.address
52
+ self[expression.address]
53
+ end
54
+ when Formula::Range
55
+ expand_range(expression.top, expression.bottom).map { |ref| eval ref, refs }
56
+ end
57
+ end
58
+
59
+ def expand_range(top, bottom)
60
+ cols = top.col..bottom.col
61
+ rows = top.row..bottom.row
62
+ cols.flat_map do |col|
63
+ rows.map { |row| Formula::Reference.new(col, row) }
64
+ end
65
+ end
66
+ end
67
+
68
+ class Cell
69
+ attr_reader :raw
70
+
71
+ def initialize(raw = "", &expression)
72
+ @raw = raw
73
+ @observers = Set.new
74
+ @observed = []
75
+ update(&expression) if block_given?
76
+ end
77
+
78
+ def value
79
+ if @@caller
80
+ @observers << @@caller
81
+ @@caller.observed << self
82
+ end
83
+ @value
84
+ end
85
+
86
+ def update(value = raw || "", &expression)
87
+ @raw = value
88
+ @expression = expression
89
+ compute
90
+ @value
91
+ end
92
+
93
+ protected
94
+
95
+ attr_reader :observers, :observed
96
+
97
+ def compute
98
+ @observed.each { |observed| observed.observers.delete(self) }
99
+ @observed = []
100
+
101
+ @@caller = self
102
+ new_value = @expression.call
103
+ @@caller = nil
104
+
105
+ if new_value != @value
106
+ @value = new_value
107
+ observers = @observers
108
+ @observers = Set.new
109
+ observers.each { |observer| observer.compute }
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,113 @@
1
+ require "treetop"
2
+
3
+ class Spreadshit::Formula
4
+ module Nodes
5
+ class Node < Treetop::Runtime::SyntaxNode; end
6
+ class NumberNode < Node; end
7
+ class StringNode < Node; end
8
+ class GroupNode < Node; end
9
+ class FunctionNode < Node; end
10
+ class ArgumentListNode < Node; end
11
+ class AdditiveNode < Node; end
12
+ class MultiplicativeNode < Node; end
13
+ class RangeNode < Node; end
14
+ class ReferenceNode < Node; end
15
+ end
16
+
17
+ class Literal < Struct.new(:content); end
18
+ class String < Literal; end
19
+ class Number < Literal; end
20
+ class Integer < Number; end
21
+ class Decimal < Number; end
22
+
23
+ class BinaryOperation < Struct.new(:left, :right); end
24
+ class Addition < BinaryOperation; end
25
+ class Subtraction < BinaryOperation; end
26
+ class Multiplication < BinaryOperation; end
27
+ class Division < BinaryOperation; end
28
+ class Function < Struct.new(:name, :arguments); end
29
+
30
+ class Reference < Struct.new(:col, :row)
31
+ def address
32
+ [col, row].join
33
+ end
34
+
35
+ def to_s
36
+ address
37
+ end
38
+ end
39
+
40
+ class Range < Struct.new(:top, :bottom)
41
+ def to_s
42
+ [top.address, bottom.address].join(":")
43
+ end
44
+ end
45
+
46
+ @@parser = Treetop.load(File.join(File.dirname(__FILE__), "formula.treetop")).new
47
+
48
+ def parse(formula)
49
+ process @@parser.parse(formula)
50
+ end
51
+
52
+ private
53
+
54
+ def process(node)
55
+ case node
56
+ when Nodes::NumberNode
57
+ node.text_value.include?(".") ?
58
+ Decimal.new(node.text_value.to_f) :
59
+ Integer.new(node.text_value.to_i)
60
+
61
+ when Nodes::StringNode
62
+ String.new(node.chars.text_value)
63
+
64
+ when Nodes::GroupNode
65
+ process node.content
66
+
67
+ when Nodes::FunctionNode
68
+ Function.new(
69
+ node.name.text_value.upcase,
70
+ process(node.arguments) || []
71
+ )
72
+
73
+ when Nodes::ArgumentListNode
74
+ if node.tail.elements.empty?
75
+ [process(node.head)]
76
+ else
77
+ [process(node.head)] + process(node.tail.elements[0].arguments)
78
+ end
79
+
80
+ when Nodes::RangeNode
81
+ Range.new(process(node.top), process(node.bottom))
82
+
83
+ when Nodes::ReferenceNode
84
+ Reference.new(node.col.text_value.upcase, node.row.text_value.to_i)
85
+
86
+ when Nodes::AdditiveNode
87
+ if node.tail.elements.empty?
88
+ process node.head
89
+ else
90
+ node.tail.elements.reduce(process(node.head)) do |left, node|
91
+ right = process(node.operand)
92
+ case node.operator.text_value
93
+ when "+" then Addition.new(left, right)
94
+ when "-" then Subtraction.new(left, right)
95
+ end
96
+ end
97
+ end
98
+
99
+ when Nodes::MultiplicativeNode
100
+ if node.tail.elements.empty?
101
+ process node.head
102
+ else
103
+ node.tail.elements.reduce(process(node.head)) do |left, node|
104
+ right = process(node.operand)
105
+ case node.operator.text_value
106
+ when "*" then Multiplication.new(left, right)
107
+ when "/" then Division.new(left, right)
108
+ end
109
+ end
110
+ end
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,61 @@
1
+ grammar FormulaGrammar
2
+ rule additive
3
+ head:multiplicative tail:(space* operator:[+-] space* operand:multiplicative)* <Spreadshit::Formula::Nodes::AdditiveNode>
4
+ end
5
+
6
+ rule multiplicative
7
+ head:operand tail:(space* operator:[*/] space* operand:operand)* <Spreadshit::Formula::Nodes::MultiplicativeNode>
8
+ end
9
+
10
+ rule operand
11
+ reference / signed_number / string / group / function
12
+ end
13
+
14
+ rule group
15
+ '(' space* content:additive space* ')' <Spreadshit::Formula::Nodes::GroupNode>
16
+ end
17
+
18
+ rule function
19
+ name:[a-zA-Z]+ '(' space* arguments:arguments_list? space* ')' <Spreadshit::Formula::Nodes::FunctionNode>
20
+ end
21
+
22
+ rule arguments_list
23
+ head:argument tail:(space* ';' space* arguments:arguments_list)* <Spreadshit::Formula::Nodes::ArgumentListNode>
24
+ end
25
+
26
+ rule argument
27
+ range / additive / operand
28
+ end
29
+
30
+ rule range
31
+ top:reference ':' bottom:reference <Spreadshit::Formula::Nodes::RangeNode>
32
+ end
33
+
34
+ rule reference
35
+ col:[a-zA-Z]+ row:([1-9] [0-9]+ / [1-9]) <Spreadshit::Formula::Nodes::ReferenceNode>
36
+ end
37
+
38
+ rule signed_number
39
+ sign? number <Spreadshit::Formula::Nodes::NumberNode>
40
+ end
41
+
42
+ rule number
43
+ ([0-9]+ '.')? [0-9]+
44
+ end
45
+
46
+ rule sign
47
+ [+-]
48
+ end
49
+
50
+ rule string
51
+ quote chars:(!quote .)* quote <Spreadshit::Formula::Nodes::StringNode>
52
+ end
53
+
54
+ rule quote
55
+ '"'
56
+ end
57
+
58
+ rule space
59
+ ' '
60
+ end
61
+ end
@@ -0,0 +1,68 @@
1
+ class Spreadshit::Functions
2
+ [[:+, :add], [:-, :minus], [:*, :multiply]].each do |operator, name|
3
+ define_method(name) { |left, right| to_number(left).send(operator, to_number(right)) }
4
+ end
5
+
6
+ def divide(left, right)
7
+ right = to_number(right)
8
+ return Float::NAN if right.zero?
9
+ to_number(to_number(left) / right.to_f)
10
+ end
11
+
12
+ def sum(*args)
13
+ to_number args.flatten.map { |arg| to_number(arg) }.reduce(:+)
14
+ end
15
+
16
+ def average(*args)
17
+ to_number(sum(*args) / args.size.to_f)
18
+ end
19
+
20
+ def sqrt(number)
21
+ Math.sqrt to_number(number)
22
+ end
23
+
24
+ def ln(number)
25
+ Math.log to_number(number)
26
+ end
27
+
28
+ def var(*args)
29
+ average = average(*args)
30
+
31
+ args.flatten.map { |arg| to_number(arg) }.reduce(0) do |variance, value|
32
+ variance + (value - average) ** 2
33
+ end
34
+ end
35
+
36
+ def stdev(*args)
37
+ sqrt(var(*args))
38
+ end
39
+
40
+ def date(year, month, date)
41
+ Date.new(year, month, date)
42
+ end
43
+
44
+ private
45
+
46
+ def to_number(value)
47
+ case value
48
+ when Float::INFINITY
49
+ value
50
+ when nil
51
+ 0
52
+ when Numeric
53
+ if !value.to_f.nan? && value.to_f - value.to_i == 0
54
+ value.to_i
55
+ else
56
+ value.to_f
57
+ end
58
+ when Date
59
+ value
60
+ when -> string { string.to_s =~ /\A[-+]?([0-9]+\.)?[0-9]+\z/ }
61
+ to_number(value.to_f)
62
+ when String && -> string { string.strip.empty? }
63
+ 0
64
+ else
65
+ Float::NAN
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,3 @@
1
+ class Spreadshit
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,263 @@
1
+ require "curses"
2
+
3
+ class Spreadshit::Window
4
+ class Address < Struct.new(:col, :row)
5
+ def to_s
6
+ [col, row].join
7
+ end
8
+
9
+ def to_sym
10
+ to_s.to_sym
11
+ end
12
+ end
13
+
14
+ class SpreadsheetDelegate
15
+ def initialize
16
+ @cell_updated, @cell_value, @cell_content = Proc.new {}
17
+ end
18
+
19
+ def cell_updated(&block)
20
+ if block_given?
21
+ @cell_updated = block
22
+ else
23
+ @cell_updated
24
+ end
25
+ end
26
+
27
+ def cell_value(&block)
28
+ if block_given?
29
+ @cell_value = block
30
+ else
31
+ @cell_value
32
+ end
33
+ end
34
+
35
+ def cell_content(&block)
36
+ if block_given?
37
+ @cell_content = block
38
+ else
39
+ @cell_content
40
+ end
41
+ end
42
+ end
43
+
44
+ include Curses
45
+
46
+ def initialize
47
+ @mode = :navigation
48
+ @x, @y = 0, 0
49
+ @sx, @sy = 0, 0
50
+ @col_width = 13
51
+ @letters = ("A".."ZZZ").to_a
52
+ @spreadsheet_delegate = SpreadsheetDelegate.new
53
+ yield @spreadsheet_delegate
54
+ end
55
+
56
+ def start
57
+ init_screen
58
+ start_color
59
+ init_pair(COLOR_WHITE, COLOR_BLACK, COLOR_WHITE)
60
+ init_pair(COLOR_BLUE, COLOR_BLACK, COLOR_BLUE)
61
+ init_pair(COLOR_GREEN, COLOR_BLACK, COLOR_GREEN)
62
+ init_pair(COLOR_RED, COLOR_BLACK, COLOR_MAGENTA)
63
+ use_default_colors
64
+ redraw
65
+
66
+ loop { capture_input }
67
+ end
68
+
69
+ private
70
+
71
+ def max_cols
72
+ cols / @col_width
73
+ end
74
+
75
+ def max_rows
76
+ lines - 3
77
+ end
78
+
79
+ def address
80
+ Address.new(@letters[@x], @y + 1)
81
+ end
82
+
83
+ def current_cell_value
84
+ cell_value_at(address)
85
+ end
86
+
87
+ def cell_value_at(address)
88
+ @spreadsheet_delegate.cell_value.call(address)
89
+ end
90
+
91
+ def current_cell_content
92
+ @spreadsheet_delegate.cell_content.call(address)
93
+ end
94
+
95
+ def current_cell_content=(value)
96
+ @spreadsheet_delegate.cell_updated.call(address, value)
97
+ end
98
+
99
+ def capture_input
100
+ case @mode
101
+ when :navigation
102
+ navigate
103
+ when :edit
104
+ read_cell_definition
105
+ end
106
+ end
107
+
108
+ def navigate
109
+ cbreak
110
+ noecho
111
+ stdscr.keypad = true
112
+ case getch
113
+ when KEY_UP
114
+ @y -= 1
115
+ @y = 0 if @y < 0
116
+ @sy -= 1 if @y < @sy
117
+ @sy = 0 if @sy < 0
118
+ when KEY_DOWN
119
+ @y += 1
120
+ @sy += 1 if @y >= max_rows
121
+ when KEY_LEFT
122
+ @x -= 1
123
+ @x = 0 if @x < 0
124
+ @sx -= 1 if @x < @sx
125
+ @sx = 0 if @sx < 0
126
+ when KEY_RIGHT
127
+ @x += 1
128
+ @sx += 1 if @x >= max_cols
129
+ when 10
130
+ @mode = :edit
131
+ when 27
132
+ exit 0
133
+ else
134
+ return
135
+ end
136
+
137
+ redraw
138
+ end
139
+
140
+ def read_cell_definition
141
+ echo
142
+ self.current_cell_content = getstr
143
+ @mode = :navigation
144
+ redraw
145
+ end
146
+
147
+ def redraw
148
+ draw_cells
149
+ draw_letters_header
150
+ draw_numbers_header
151
+ draw_text_field
152
+ cursor_to_input_line
153
+ refresh
154
+ end
155
+
156
+ def draw_text_field
157
+ setpos(divider_line, 0)
158
+
159
+ case @mode
160
+ when :navigation
161
+ draw_divider(
162
+ color: color_pair(COLOR_RED) | A_NORMAL,
163
+ left_text: current_cell_value.to_s,
164
+ center_text: "Press ENTER to edit #{address}",
165
+ )
166
+ cursor_to_input_line
167
+ addstr(current_cell_content.to_s)
168
+ clrtoeol
169
+ when :edit
170
+ draw_divider(
171
+ color: color_pair(COLOR_GREEN) | A_NORMAL,
172
+ left_text: current_cell_content.to_s,
173
+ center_text: "Editing #{address}"
174
+ )
175
+ cursor_to_input_line
176
+ clrtoeol
177
+ end
178
+ end
179
+
180
+ def draw_divider(color: color_pair(COLOR_GREEN) | A_NORMAL, left_text: "", right_text: "", center_text: "")
181
+ attron color do
182
+ setpos(divider_line, 0)
183
+ addstr(" " * cols)
184
+
185
+ setpos(divider_line, 2)
186
+ addstr(left_text.ljust(cols / 3))
187
+
188
+ setpos(divider_line, cols / 3)
189
+ addstr(center_text.center(cols / 3))
190
+
191
+ setpos(divider_line, cols - right_text.size - 2)
192
+ addstr(right_text)
193
+ end
194
+ end
195
+
196
+ def visible_letters
197
+ (@sx...max_cols + @sx).map { |col| @letters[col] }
198
+ end
199
+
200
+ def selected?(row, col)
201
+ address.col == col && address.row == (@sy + row)
202
+ end
203
+
204
+ def draw_cells(padding: 4)
205
+ 1.upto(max_rows).each do |row|
206
+ visible_letters.each.with_index do |col, index|
207
+ setpos(row, padding + index * @col_width)
208
+
209
+ if selected? row, col
210
+ attron(color_pair(@mode == :edit ? COLOR_GREEN : COLOR_WHITE) | A_TOP) do
211
+ draw_cell @sy + row, col
212
+ end
213
+ else
214
+ draw_cell @sy + row, col
215
+ end
216
+ end
217
+ end
218
+ end
219
+
220
+ def draw_letters_header(padding: 4, color: COLOR_BLUE)
221
+ visible_letters.each.with_index do |letter, index|
222
+ setpos(0, padding + index * @col_width)
223
+
224
+ attron(color_pair(color) | A_TOP) do
225
+ addstr(letter.center(@col_width))
226
+ end
227
+ end
228
+ end
229
+
230
+ def draw_numbers_header(padding: 4, color: COLOR_BLUE)
231
+ 1.upto(max_rows).each.with_index do |row, index|
232
+ setpos(row, 0)
233
+
234
+ attron(color_pair(COLOR_BLUE) | A_TOP) do
235
+ addstr (@sy + row).to_s.rjust(padding)
236
+ end
237
+ end
238
+ end
239
+
240
+ def draw_cell(row, col)
241
+ value = cell_value_at(Address.new(col, row)).to_s
242
+
243
+ if value == Float::NAN.to_s
244
+ addstr("#VALUE!".center(@col_width))
245
+ elsif value.size >= @col_width
246
+ addstr(value.chars.last(@col_width).join)
247
+ else
248
+ addstr(value.rjust(@col_width))
249
+ end
250
+ end
251
+
252
+ def input_line
253
+ lines - 1
254
+ end
255
+
256
+ def divider_line
257
+ lines - 2
258
+ end
259
+
260
+ def cursor_to_input_line
261
+ setpos(input_line, 2)
262
+ end
263
+ end
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "spreadshit/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "spreadshit"
8
+ spec.version = Spreadshit::VERSION
9
+ spec.authors = ["Rodrigo Navarro"]
10
+ spec.email = ["rnavarro@rnavarro.com.br"]
11
+
12
+ spec.summary = %q{Simple spreadsheet implementation}
13
+ spec.description = %q{Simple spreadsheet implementation}
14
+ spec.homepage = "https://github.com/reu/spreadshit"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
17
+ spec.bindir = "exe"
18
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency "treetop", "~> 1.6"
22
+ spec.add_dependency "curses"
23
+
24
+ spec.add_development_dependency "bundler", "~> 1.9"
25
+ spec.add_development_dependency "rake", "~> 10.0"
26
+ end
metadata ADDED
@@ -0,0 +1,113 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: spreadshit
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Rodrigo Navarro
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2016-01-19 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: treetop
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.6'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.6'
27
+ - !ruby/object:Gem::Dependency
28
+ name: curses
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.9'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.9'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '10.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '10.0'
69
+ description: Simple spreadsheet implementation
70
+ email:
71
+ - rnavarro@rnavarro.com.br
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - ".gitignore"
77
+ - Gemfile
78
+ - README.md
79
+ - Rakefile
80
+ - bin/console
81
+ - bin/demo
82
+ - bin/setup
83
+ - lib/spreadshit.rb
84
+ - lib/spreadshit/formula.rb
85
+ - lib/spreadshit/formula.treetop
86
+ - lib/spreadshit/functions.rb
87
+ - lib/spreadshit/version.rb
88
+ - lib/spreadshit/window.rb
89
+ - spreadshit.gemspec
90
+ homepage: https://github.com/reu/spreadshit
91
+ licenses: []
92
+ metadata: {}
93
+ post_install_message:
94
+ rdoc_options: []
95
+ require_paths:
96
+ - lib
97
+ required_ruby_version: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - ">="
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ required_rubygems_version: !ruby/object:Gem::Requirement
103
+ requirements:
104
+ - - ">="
105
+ - !ruby/object:Gem::Version
106
+ version: '0'
107
+ requirements: []
108
+ rubyforge_project:
109
+ rubygems_version: 2.4.5
110
+ signing_key:
111
+ specification_version: 4
112
+ summary: Simple spreadsheet implementation
113
+ test_files: []