spreadshit 0.1.0

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