samjones-calculator 0.0.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: a6261330f0e5c14d2a71ee7827a70d86bf5892a6
4
+ data.tar.gz: 5cfa35a3ee86e173f1a78511e19e146fc787c009
5
+ SHA512:
6
+ metadata.gz: aa1aa6f639d4bee4dd46575b968b5ecdff7803b97a7f364f8bda936653dd054f5bb0c71d3d39e84257aa1418d999e165f6966e10e8ab014f845dca523cef0622
7
+ data.tar.gz: 642f8096a939e3b13e4883704fffa73eff3897184450b2de1ddbb060a59e19f048f6352e740e2e09001911e5ce0db909a1a5603c367a3121b5c397611c3f8a72
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2015 Sam Jones
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,52 @@
1
+ # Calculator
2
+
3
+ Calculator written for the [String Calculator Kata](http://osherove.com/tdd-kata-1/.)
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'samjones-calculator'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install calculator
18
+
19
+ ## Usage
20
+
21
+ ### Instantiation
22
+ New Calculator with starting value of zero
23
+ `StringCalculator.new`
24
+
25
+ New Calculator setting starting value
26
+ `StringCalculator.new(10)`
27
+
28
+ ### Adding and Subtracting
29
+
30
+ Add value
31
+ `StringCalculator.new(22).add('20').value` will produce a result of 42
32
+
33
+ Subtract value
34
+ `StringCalculator.new(62).subtract('20').value` will produce a result of 42
35
+
36
+ Chaining calls
37
+ `StringCalculator.new(22).add('40').subtract('20').value` will produce a result of 42
38
+
39
+ ### Working with Delimiters
40
+
41
+ Add values with default delimiters `','`, `'\n'`
42
+ `StringCalculator.new.add('12,23\n7').value` will produce a result of 42
43
+
44
+ Add values by setting delimiter to `'~''`
45
+ `StringCalculator.new.add('//~\n21~21').value` will produce a result of 42
46
+
47
+ Add values by setting delimiters to `'~''`, `'!%'`
48
+ `StringCalculator.new.add('//[~][!%]\n20~20!%2').value` will produce a result of 42
49
+
50
+ ## Contributing
51
+
52
+ Contribution is closed.
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
@@ -0,0 +1,32 @@
1
+ class StringCalculator
2
+ def initialize(value = 0, tokenizer = Tokenizer.new)
3
+ @value = value
4
+ @tokenizer = tokenizer
5
+ end
6
+
7
+ def value
8
+ @value
9
+ end
10
+
11
+ def add(string)
12
+ @value += sum_ints(string)
13
+ self
14
+ end
15
+
16
+ def sum_ints(string)
17
+ ints = @tokenizer.ints(string)
18
+ @negs = ints.select { |i| i<0 }
19
+ ints.inject(:+)
20
+ end
21
+
22
+
23
+ def subtract(string)
24
+ @value -= sum_ints(string)
25
+ self
26
+ end
27
+
28
+ def check_negatives
29
+ raise "Negatives not allowed! #{@negs.join(', ')}" if @negs.any?
30
+ self
31
+ end
32
+ end
@@ -0,0 +1,37 @@
1
+ class Tokenizer
2
+ def initialize
3
+ @regex_or = '|'
4
+
5
+ @standard_delimiter = /,|\n/
6
+
7
+ @has_single_delimiter = /\/\/(.)\n(.*)/
8
+
9
+ @has_multi_delimiter = /\/\/(\[.+\])+\n.*/
10
+ @multi_delimiter = /\[|\]/
11
+ @multi_delimiter_replace = /\/\/(\[.+\])+\n/
12
+ end
13
+
14
+ def tokens(string)
15
+ case string
16
+ when @has_single_delimiter
17
+ delims = $1
18
+ string = $2
19
+ when @has_multi_delimiter
20
+ delims = Regexp.new($1.split(@multi_delimiter).reject(&:empty?).join(@regex_or))
21
+ string = string.sub(@multi_delimiter_replace, '')
22
+ else
23
+ delims = @standard_delimiter
24
+ string = string
25
+ end
26
+ string.split(delims)
27
+ end
28
+
29
+ def ints(string = '')
30
+ tokens(string).select { |s| is_i?(s) }.map { |s| s.to_i }
31
+ end
32
+
33
+ def is_i?(string)
34
+ string.to_i.to_s == string
35
+ end
36
+
37
+ end
@@ -0,0 +1,109 @@
1
+ require 'calculator/string_calculator'
2
+
3
+ def tokenizer(test)
4
+ tokenizer = instance_double('Tokenizer')
5
+ allow(tokenizer).to receive(:ints) { test[:ints] }
6
+ expect(tokenizer).to receive(:ints).with(test[:string])
7
+ tokenizer
8
+ end
9
+
10
+ describe 'StringCalculator' do
11
+
12
+ it 'should have a default value' do
13
+ expect(StringCalculator.new.value).to eq(0)
14
+ expect(StringCalculator.new(0).value).to eq(0)
15
+ expect(StringCalculator.new(10).value).to eq(10)
16
+ end
17
+
18
+ context 'add' do
19
+ it 'should add' do
20
+ [{
21
+ :initial_value => 0,
22
+ :string => '10',
23
+ :ints => [10],
24
+ :result => 10
25
+ },
26
+ {
27
+ :initial_value => 10,
28
+ :string => '10',
29
+ :ints => [10],
30
+ :result => 20
31
+ }
32
+ ].each { |test|
33
+ expect(StringCalculator.new(test[:initial_value], tokenizer(test)).add(test[:string]).value).to eq(test[:result])
34
+ }
35
+ end
36
+
37
+ it 'should add with no negatives when checking for negatives' do
38
+ [{
39
+ :initial_value => 0,
40
+ :string => '10',
41
+ :ints => [10],
42
+ :result => 10
43
+ }
44
+ ].each { |test|
45
+ expect(StringCalculator.new(test[:initial_value], tokenizer(test)).add(test[:string]).check_negatives.value).to eq(test[:result])
46
+ }
47
+ end
48
+
49
+ it 'should not add with negatives when checking for negatives' do
50
+ [{
51
+ :initial_value => 0,
52
+ :string => '-10',
53
+ :ints => [-10],
54
+ :result => 10
55
+ }
56
+ ].each { |test|
57
+ expect { StringCalculator.new(test[:initial_value], tokenizer(test)).add(test[:string]).check_negatives }.to raise_error('Negatives not allowed! -10')
58
+ }
59
+ end
60
+ end
61
+
62
+ context 'subtract' do
63
+ it 'should subtract' do
64
+ [{
65
+ :initial_value => 0,
66
+ :string => '10',
67
+ :ints => [10],
68
+ :result => -10
69
+ },
70
+ {
71
+ :initial_value => 10,
72
+ :string => '10',
73
+ :ints => [10],
74
+ :result => 0
75
+ }
76
+ ].each { |test|
77
+ expect(StringCalculator.new(test[:initial_value], tokenizer(test)).subtract(test[:string]).value).to eq(test[:result])
78
+ }
79
+ end
80
+
81
+ it 'should subtract with no negatives when checking for negatives' do
82
+ [{
83
+ :initial_value => 0,
84
+ :string => '10',
85
+ :ints => [10],
86
+ :result => -10
87
+ }
88
+ ].each { |test|
89
+ expect(StringCalculator.new(test[:initial_value], tokenizer(test)).subtract(test[:string]).check_negatives.value).to eq(test[:result])
90
+ }
91
+ end
92
+
93
+ it 'should not subtract with negatives when checking for negatives' do
94
+ [{
95
+ :initial_value => 0,
96
+ :string => '-10',
97
+ :ints => [-10],
98
+ :result => 10
99
+ }
100
+ ].each { |test|
101
+ expect { StringCalculator.new(test[:initial_value], tokenizer(test)).subtract(test[:string]).check_negatives }.to raise_error('Negatives not allowed! -10')
102
+ }
103
+ end
104
+ end
105
+
106
+ it 'should do both' do
107
+ expect(StringCalculator.new.add('10').subtract('5').add('15,10').subtract('5,2').value).to eq(23)
108
+ end
109
+ end
@@ -0,0 +1,96 @@
1
+ # This file was generated by the `rspec --init` command. Conventionally, all
2
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
3
+ # The generated `.rspec` file contains `--require spec_helper` which will cause
4
+ # this file to always be loaded, without a need to explicitly require it in any
5
+ # files.
6
+ #
7
+ # Given that it is always loaded, you are encouraged to keep this file as
8
+ # light-weight as possible. Requiring heavyweight dependencies from this file
9
+ # will add to the boot time of your test suite on EVERY test run, even for an
10
+ # individual file that may not need all of that loaded. Instead, consider making
11
+ # a separate helper file that requires the additional dependencies and performs
12
+ # the additional setup, and require it from the spec files that actually need
13
+ # it.
14
+ #
15
+ # The `.rspec` file also contains a few flags that are not defaults but that
16
+ # users commonly want.
17
+ #
18
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
19
+ RSpec.configure do |config|
20
+ # rspec-expectations config goes here. You can use an alternate
21
+ # assertion/expectation library such as wrong or the stdlib/minitest
22
+ # assertions if you prefer.
23
+ config.expect_with :rspec do |expectations|
24
+ # This option will default to `true` in RSpec 4. It makes the `description`
25
+ # and `failure_message` of custom matchers include text for helper methods
26
+ # defined using `chain`, e.g.:
27
+ # be_bigger_than(2).and_smaller_than(4).description
28
+ # # => "be bigger than 2 and smaller than 4"
29
+ # ...rather than:
30
+ # # => "be bigger than 2"
31
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
32
+ end
33
+
34
+ # rspec-mocks config goes here. You can use an alternate test double
35
+ # library (such as bogus or mocha) by changing the `mock_with` option here.
36
+ config.mock_with :rspec do |mocks|
37
+ # Prevents you from mocking or stubbing a method that does not exist on
38
+ # a real object. This is generally recommended, and will default to
39
+ # `true` in RSpec 4.
40
+ mocks.verify_partial_doubles = true
41
+ end
42
+
43
+ # The settings below are suggested to provide a good initial experience
44
+ # with RSpec, but feel free to customize to your heart's content.
45
+ =begin
46
+ # These two settings work together to allow you to limit a spec run
47
+ # to individual examples or groups you care about by tagging them with
48
+ # `:focus` metadata. When nothing is tagged with `:focus`, all examples
49
+ # get run.
50
+ config.filter_run :focus
51
+ config.run_all_when_everything_filtered = true
52
+
53
+ # Allows RSpec to persist some state between runs in order to support
54
+ # the `--only-failures` and `--next-failure` CLI options. We recommend
55
+ # you configure your source control system to ignore this file.
56
+ config.example_status_persistence_file_path = "spec/examples.txt"
57
+
58
+ # Limits the available syntax to the non-monkey patched syntax that is
59
+ # recommended. For more details, see:
60
+ # - http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax
61
+ # - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
62
+ # - http://myronmars.to/n/dev-blog/2014/05/notable-changes-in-rspec-3#new__config_option_to_disable_rspeccore_monkey_patching
63
+ config.disable_monkey_patching!
64
+
65
+ # This setting enables warnings. It's recommended, but in some cases may
66
+ # be too noisy due to issues in dependencies.
67
+ config.warnings = true
68
+
69
+ # Many RSpec users commonly either run the entire suite or an individual
70
+ # file, and it's useful to allow more verbose output when running an
71
+ # individual spec file.
72
+ if config.files_to_run.one?
73
+ # Use the documentation formatter for detailed output,
74
+ # unless a formatter has already been configured
75
+ # (e.g. via a command-line flag).
76
+ config.default_formatter = 'doc'
77
+ end
78
+
79
+ # Print the 10 slowest examples and example groups at the
80
+ # end of the spec run, to help surface which specs are running
81
+ # particularly slow.
82
+ config.profile_examples = 10
83
+
84
+ # Run specs in random order to surface order dependencies. If you find an
85
+ # order dependency and want to debug it, you can fix the order by providing
86
+ # the seed, which is printed after each run.
87
+ # --seed 1234
88
+ config.order = :random
89
+
90
+ # Seed global randomization in this process using the `--seed` CLI option.
91
+ # Setting this allows you to use `--seed` to deterministically reproduce
92
+ # test failures related to randomization by passing the same `--seed` value
93
+ # as the one that triggered the failure.
94
+ Kernel.srand config.seed
95
+ =end
96
+ end
@@ -0,0 +1,44 @@
1
+ require 'tokenizer/tokenizer.rb'
2
+
3
+ describe 'Tokenizer' do
4
+ it "should not blow up" do
5
+ expect(Tokenizer.new.ints).to eq([])
6
+ expect(Tokenizer.new.ints('')).to eq([])
7
+ end
8
+
9
+ it "should tokenize" do
10
+ expect(Tokenizer.new.tokens('abc')).to eq(['abc'])
11
+ expect(Tokenizer.new.tokens('abc,123')).to eq(['abc', '123'])
12
+ end
13
+
14
+ it "should handle multiple delimiters" do
15
+ expect(Tokenizer.new.tokens("1,2\n3")).to eq(['1', '2', '3'])
16
+ end
17
+
18
+ it "should handle setting delimiter" do
19
+ expect(Tokenizer.new.tokens("//'\n1'2")).to eq(['1', '2'])
20
+ end
21
+
22
+ it "should handle setting multiple delimiters" do
23
+ expect(Tokenizer.new.tokens("//['][#]\n1'2#3")).to eq(['1', '2', '3'])
24
+ end
25
+
26
+ it "should handle complex delimiters" do
27
+ expect(Tokenizer.new.tokens("//['#]\n1'#2")).to eq(['1', '2'])
28
+ end
29
+
30
+ it "should test for int" do
31
+ expect(Tokenizer.new.is_i?("123")).to eq(true)
32
+ expect(Tokenizer.new.is_i?("abc")).to eq(false)
33
+ end
34
+
35
+ it "should return ints" do
36
+ expect(Tokenizer.new.ints('1')).to eq([1])
37
+ expect(Tokenizer.new.ints('1,2')).to eq([1, 2])
38
+ end
39
+
40
+ it "should filter non ints" do
41
+ expect(Tokenizer.new.ints('abc,123')).to eq([123])
42
+ end
43
+
44
+ end
metadata ADDED
@@ -0,0 +1,80 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: samjones-calculator
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Sam Jones
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-09-11 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rake
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rspec
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 2.0.0
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: 2.0.0
41
+ description: String Calculator written for kata http://osherove.com/tdd-kata-1/
42
+ email:
43
+ - sam@samljones.com
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - LICENSE.txt
49
+ - README.md
50
+ - Rakefile
51
+ - lib/calculator/string_calculator.rb
52
+ - lib/tokenizer/tokenizer.rb
53
+ - spec/calculator/calculator_spec.rb
54
+ - spec/spec_helper.rb
55
+ - spec/tokenizer/tokenizer_spec.rb
56
+ homepage: https://github.com/xDD-CLE/katas/tree/master/adder_new_lang/samjones/calculator
57
+ licenses:
58
+ - MIT
59
+ metadata: {}
60
+ post_install_message:
61
+ rdoc_options: []
62
+ require_paths:
63
+ - lib
64
+ required_ruby_version: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ required_rubygems_version: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ version: '0'
74
+ requirements: []
75
+ rubyforge_project:
76
+ rubygems_version: 2.4.6
77
+ signing_key:
78
+ specification_version: 4
79
+ summary: String Calculator
80
+ test_files: []