total_recall 0.2.0 → 0.4.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 +7 -0
- data/.travis.yml +3 -0
- data/Gemfile +0 -2
- data/README.md +86 -24
- data/lib/total_recall.rb +111 -272
- data/lib/total_recall/templates/sample.yml.tt +79 -0
- data/lib/total_recall/templates/simple.yml.tt +35 -0
- data/lib/total_recall/version.rb +1 -1
- data/spec/spec_helper.rb +1 -9
- data/spec/total_recall/total_recall_spec.rb +176 -0
- data/total_recall.gemspec +21 -16
- metadata +97 -63
- data/spec/fixtures/.keep +0 -0
- data/spec/fixtures/abn.csv +0 -2
- data/spec/fixtures/abncc.csv +0 -3
- data/spec/fixtures/ing.csv +0 -3
- data/spec/total_recall/bank_parser_spec.rb +0 -57
    
        checksums.yaml
    ADDED
    
    | @@ -0,0 +1,7 @@ | |
| 1 | 
            +
            ---
         | 
| 2 | 
            +
            SHA1:
         | 
| 3 | 
            +
              metadata.gz: 162939627043b1aea142c3c8a6dedc9c0557801a
         | 
| 4 | 
            +
              data.tar.gz: fd307f5b53406336a46225d36b9138d11d228bfd
         | 
| 5 | 
            +
            SHA512:
         | 
| 6 | 
            +
              metadata.gz: a78dcb6fa9e0d5d533dcc8bef4d34769ced4f8809c8f074158c4a3c3de2cd45a53cacd2364d0e0b941330cc7fccf35c8f443106eada6f6dfa7c2b48cc555db3f
         | 
| 7 | 
            +
              data.tar.gz: 1ac9db0b80f7c7e08be72ccadb399d1eb209b56bbfa40bafcc0fa3c7037c691e3e329c6a9e4147bcda57b6893058a8b2f8a1b10459ebb31ca7fb428ec701a80f
         | 
    
        data/.travis.yml
    ADDED
    
    
    
        data/Gemfile
    CHANGED
    
    
    
        data/README.md
    CHANGED
    
    | @@ -1,39 +1,100 @@ | |
| 1 | 
            -
            TotalRecall
         | 
| 2 | 
            -
            ============
         | 
| 1 | 
            +
            # TotalRecall [](https://secure.travis-ci.org/#!/eval/total_recall)
         | 
| 3 2 |  | 
| 4 | 
            -
            Turn  | 
| 3 | 
            +
            Turn **any** csv-file into a [Ledger](http://ledger-cli.org/) journal.
         | 
| 5 4 |  | 
| 6 | 
            -
             | 
| 7 | 
            -
            ------------
         | 
| 5 | 
            +
            ## Quickstart
         | 
| 8 6 |  | 
| 9 | 
            -
             | 
| 10 | 
            -
                Tasks:
         | 
| 11 | 
            -
                  total_recall help [TASK]                                   # Describe available tasks or one specific task
         | 
| 12 | 
            -
                  total_recall ledger -i, --input=INPUT -p, --parser=PARSER  # Convert input to ledger-transactions
         | 
| 13 | 
            -
                  total_recall parse -i, --input=INPUT -p, --parser=PARSER   # Parses input-file and prints it
         | 
| 7 | 
            +
            ### Generate a sample config
         | 
| 14 8 |  | 
| 15 | 
            -
             | 
| 16 | 
            -
             | 
| 9 | 
            +
            ```bash
         | 
| 10 | 
            +
            total_recall sample
         | 
| 11 | 
            +
            # An annotated config 'sample.yml' will be written to CWD.
         | 
| 12 | 
            +
            ```
         | 
| 17 13 |  | 
| 18 | 
            -
             | 
| 19 | 
            -
             | 
| 20 | 
            -
             | 
| 21 | 
            -
             | 
| 22 | 
            -
             | 
| 23 | 
            -
             | 
| 24 | 
            -
             | 
| 14 | 
            +
            ### Generate a ledger
         | 
| 15 | 
            +
             | 
| 16 | 
            +
            The sample config contains 2 transactions.  
         | 
| 17 | 
            +
            Let's get 'em in a journal:
         | 
| 18 | 
            +
             | 
| 19 | 
            +
            ```bash
         | 
| 20 | 
            +
            total_recall ledger -c sample.yml > sample.dat
         | 
| 21 | 
            +
            # What account provides these transactions?  |Assets:Checking|
         | 
| 22 | 
            +
            #
         | 
| 23 | 
            +
            # +------------+-----+----------+
         | 
| 24 | 
            +
            # | 03.11.2013 | Foo | 1.638,00 |
         | 
| 25 | 
            +
            # +------------+-----+----------+
         | 
| 26 | 
            +
            # To what account did the money go?
         | 
| 27 | 
            +
            # Expenses:Foo
         | 
| 28 | 
            +
            # +------------+-----+---------+
         | 
| 29 | 
            +
            # | 03.11.2013 | Bar | -492,93 |
         | 
| 30 | 
            +
            # +------------+-----+---------+
         | 
| 31 | 
            +
            # To what account did the money go?
         | 
| 32 | 
            +
            # Expenses:Bar
         | 
| 33 | 
            +
            ```
         | 
| 34 | 
            +
            Now verify the ledger file:
         | 
| 35 | 
            +
             | 
| 36 | 
            +
            ```bash
         | 
| 37 | 
            +
            ledger -f sample.dat balance
         | 
| 38 | 
            +
             | 
| 39 | 
            +
            #         $ -1.145,07  Assets:Checking
         | 
| 40 | 
            +
            #          $ 1.145,07  Expenses
         | 
| 41 | 
            +
            #           $ -492,93    Bar
         | 
| 42 | 
            +
            #          $ 1.638,00    Foo
         | 
| 43 | 
            +
            #--------------------
         | 
| 44 | 
            +
            #                   0
         | 
| 45 | 
            +
            ```
         | 
| 46 | 
            +
             | 
| 47 | 
            +
            ### Now what?
         | 
| 48 | 
            +
             | 
| 49 | 
            +
            May I suggest:
         | 
| 50 | 
            +
            * read `sample.yml`
         | 
| 51 | 
            +
             | 
| 52 | 
            +
              It explains what options are available.
         | 
| 53 | 
            +
            * put your own csv in `sample.yml` and adjust the context
         | 
| 54 | 
            +
             | 
| 55 | 
            +
            ## Usage
         | 
| 25 56 |  | 
| 26 | 
            -
             | 
| 27 | 
            -
             | 
| 57 | 
            +
            ```bash
         | 
| 58 | 
            +
            total_recall
         | 
| 59 | 
            +
             | 
| 60 | 
            +
            # Commands:
         | 
| 61 | 
            +
            #  total_recall help [COMMAND]              # Describe available commands or one specific command
         | 
| 62 | 
            +
            #  total_recall init NAME                   # Generate a minimal config NAME.yml
         | 
| 63 | 
            +
            #  total_recall ledger -c, --config=CONFIG  # Convert the config to a ledger
         | 
| 64 | 
            +
            #  total_recall sample                      # Generate an annotated config
         | 
| 65 | 
            +
             | 
| 66 | 
            +
            # typically you would do:
         | 
| 67 | 
            +
            total_recall init my-bank
         | 
| 68 | 
            +
             | 
| 69 | 
            +
            # fiddle with the settings in 'my-bank.yml' and test-run it:
         | 
| 70 | 
            +
            total_recal ledger -c my-bank.yml
         | 
| 71 | 
            +
            # to skip prompts just provide dummy-data:
         | 
| 72 | 
            +
            yes 'Dummy' | total_recal ledger -c my-bank.yml
         | 
| 73 | 
            +
             | 
| 74 | 
            +
            # export it to a journal:
         | 
| 75 | 
            +
            total_recall ledger -c my-bank.yml > my-bank.dat
         | 
| 76 | 
            +
             | 
| 77 | 
            +
            # verify correctness with ledger:
         | 
| 78 | 
            +
            ledger -f my-bank.dat bal
         | 
| 79 | 
            +
            ```
         | 
| 80 | 
            +
             | 
| 81 | 
            +
            ## Develop
         | 
| 82 | 
            +
                
         | 
| 83 | 
            +
            ```bash
         | 
| 84 | 
            +
            git clone git://github.com/eval/total_recall.git
         | 
| 85 | 
            +
            cd total_recall
         | 
| 86 | 
            +
            bundle
         | 
| 87 | 
            +
            bundle exec rake spec
         | 
| 88 | 
            +
            ```
         | 
| 89 | 
            +
            ## Author
         | 
| 28 90 |  | 
| 29 91 | 
             
            Gert Goet (eval) :: gert@thinkcreate.nl :: @gertgoet
         | 
| 30 92 |  | 
| 31 | 
            -
            License
         | 
| 32 | 
            -
            ------
         | 
| 93 | 
            +
            ## License
         | 
| 33 94 |  | 
| 34 95 | 
             
            (The MIT license)
         | 
| 35 96 |  | 
| 36 | 
            -
            Copyright (c)  | 
| 97 | 
            +
            Copyright (c) 2014 Gert Goet, ThinkCreate
         | 
| 37 98 |  | 
| 38 99 | 
             
            Permission is hereby granted, free of charge, to any person obtaining
         | 
| 39 100 | 
             
            a copy of this software and associated documentation files (the
         | 
| @@ -53,3 +114,4 @@ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |
| 53 114 | 
             
            LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
         | 
| 54 115 | 
             
            OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
         | 
| 55 116 | 
             
            WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
         | 
| 117 | 
            +
             | 
    
        data/lib/total_recall.rb
    CHANGED
    
    | @@ -1,323 +1,162 @@ | |
| 1 | 
            -
            require  | 
| 1 | 
            +
            require 'yaml'
         | 
| 2 2 | 
             
            require "thor"
         | 
| 3 3 | 
             
            require "mustache"
         | 
| 4 | 
            +
            require 'csv'
         | 
| 4 5 |  | 
| 5 6 | 
             
            module TotalRecall
         | 
| 6 | 
            -
               | 
| 7 | 
            -
                 | 
| 8 | 
            -
             | 
| 9 | 
            -
             | 
| 10 | 
            -
                  # Expected: Hash with:
         | 
| 11 | 
            -
                  #:amount => Float,
         | 
| 12 | 
            -
                  #:currency => String,
         | 
| 13 | 
            -
                  #:description => String,
         | 
| 14 | 
            -
                  #:date => Date
         | 
| 15 | 
            -
                  def parse_row(row)
         | 
| 16 | 
            -
                    amount = row[6].sub(/,/,'.').to_f
         | 
| 17 | 
            -
                    {
         | 
| 18 | 
            -
                      :amount => (row[5] == 'Bij' ? amount : -amount),
         | 
| 19 | 
            -
                      :date => Date.parse(row[0]),
         | 
| 20 | 
            -
                      :description => [row[1], row[8]].map{|i| i.strip.gsub(/\s+/, ' ')}.join(' '),
         | 
| 21 | 
            -
                      :currency => 'EUR'
         | 
| 22 | 
            -
                    }
         | 
| 23 | 
            -
                  end
         | 
| 24 | 
            -
             | 
| 25 | 
            -
             | 
| 26 | 
            -
                  def self.options
         | 
| 27 | 
            -
                    {:col_sep => ",", :headers => true}
         | 
| 28 | 
            -
                  end
         | 
| 29 | 
            -
             | 
| 30 | 
            -
                  def options
         | 
| 31 | 
            -
                    self.class.options
         | 
| 32 | 
            -
                  end
         | 
| 33 | 
            -
                end # /Ing
         | 
| 34 | 
            -
             | 
| 35 | 
            -
                class Abn
         | 
| 36 | 
            -
                  require 'time'
         | 
| 37 | 
            -
             | 
| 38 | 
            -
                  # Expected: Hash with:
         | 
| 39 | 
            -
                  #:amount => Float,
         | 
| 40 | 
            -
                  #:currency => String,
         | 
| 41 | 
            -
                  #:description => String,
         | 
| 42 | 
            -
                  #:date => Date
         | 
| 43 | 
            -
                  def parse_row(row)
         | 
| 44 | 
            -
                    {
         | 
| 45 | 
            -
                      :amount => row[6].sub(/,/,'.').to_f,
         | 
| 46 | 
            -
                      :date => Date.parse(row[2]),
         | 
| 47 | 
            -
                      :description => row[7].strip.gsub(/\s+/,' '),
         | 
| 48 | 
            -
                      :currency => row[1]
         | 
| 49 | 
            -
                    }
         | 
| 50 | 
            -
                  end
         | 
| 51 | 
            -
             | 
| 52 | 
            -
                  def self.options
         | 
| 53 | 
            -
                    {:col_sep => "\t"}
         | 
| 54 | 
            -
                  end
         | 
| 55 | 
            -
             | 
| 56 | 
            -
                  def options
         | 
| 57 | 
            -
                    self.class.options
         | 
| 58 | 
            -
                  end
         | 
| 59 | 
            -
                end # /Abn
         | 
| 60 | 
            -
             | 
| 61 | 
            -
                class AbnCC
         | 
| 62 | 
            -
                  require 'time'
         | 
| 7 | 
            +
              class Helper
         | 
| 8 | 
            +
                require 'highline/import'
         | 
| 9 | 
            +
                require "terminal-table"
         | 
| 63 10 |  | 
| 64 | 
            -
             | 
| 65 | 
            -
             | 
| 66 | 
            -
                  #:currency => String,
         | 
| 67 | 
            -
                  #:description => String,
         | 
| 68 | 
            -
                  #:date => Date
         | 
| 69 | 
            -
                  def parse_row(row)
         | 
| 70 | 
            -
                    amount = row[2].sub(/,/,'.').to_f
         | 
| 71 | 
            -
                    {
         | 
| 72 | 
            -
                      :amount => (row[8] == 'D' ? -amount : amount),
         | 
| 73 | 
            -
                      :date => Date.parse(row[0]),
         | 
| 74 | 
            -
                      :description => row[3],
         | 
| 75 | 
            -
                      :currency => 'EUR'
         | 
| 76 | 
            -
                    }
         | 
| 77 | 
            -
                  end
         | 
| 11 | 
            +
                attr_reader :config
         | 
| 12 | 
            +
                attr_accessor :row
         | 
| 78 13 |  | 
| 79 | 
            -
             | 
| 80 | 
            -
             | 
| 81 | 
            -
             | 
| 14 | 
            +
                def initialize(config = {})
         | 
| 15 | 
            +
                  @config = config
         | 
| 16 | 
            +
                end
         | 
| 82 17 |  | 
| 83 | 
            -
             | 
| 84 | 
            -
             | 
| 85 | 
            -
                   | 
| 86 | 
            -
                 | 
| 87 | 
            -
             | 
| 18 | 
            +
                def with_row(row, &block)
         | 
| 19 | 
            +
                  @row = row
         | 
| 20 | 
            +
                  instance_eval(&block)
         | 
| 21 | 
            +
                ensure
         | 
| 22 | 
            +
                  @row = nil
         | 
| 23 | 
            +
                end
         | 
| 88 24 |  | 
| 89 | 
            -
             | 
| 90 | 
            -
             | 
| 25 | 
            +
                def highline
         | 
| 26 | 
            +
                  @highline ||= HighLine.new($stdin, $stderr)
         | 
| 27 | 
            +
                end
         | 
| 91 28 |  | 
| 92 | 
            -
                def  | 
| 93 | 
            -
                   | 
| 94 | 
            -
                  @default_account = default_account
         | 
| 95 | 
            -
                  @_transactions = transactions
         | 
| 29 | 
            +
                def ask(question, &block)
         | 
| 30 | 
            +
                  highline.ask(question, &block)
         | 
| 96 31 | 
             
                end
         | 
| 97 32 |  | 
| 98 | 
            -
                 | 
| 99 | 
            -
             | 
| 100 | 
            -
             | 
| 101 | 
            -
             | 
| 102 | 
            -
             | 
| 103 | 
            -
             | 
| 104 | 
            -
             | 
| 105 | 
            -
             | 
| 106 | 
            -
             | 
| 107 | 
            -
             | 
| 108 | 
            -
             | 
| 109 | 
            -
             | 
| 110 | 
            -
             | 
| 111 | 
            -
             | 
| 112 | 
            -
             | 
| 33 | 
            +
                # Prompts the user for an account-name.
         | 
| 34 | 
            +
                #
         | 
| 35 | 
            +
                # @param question [String] the question
         | 
| 36 | 
            +
                # @param options [Hash]
         | 
| 37 | 
            +
                # @option options [String] :default (nil) account-name that will be used
         | 
| 38 | 
            +
                #   if no input is provided.
         | 
| 39 | 
            +
                #
         | 
| 40 | 
            +
                # @example
         | 
| 41 | 
            +
                #   ask_account("What account did this money come from?",
         | 
| 42 | 
            +
                #               default: 'Expenses:Various')
         | 
| 43 | 
            +
                #   What account did this money come from?  |Expenses:Various|
         | 
| 44 | 
            +
                #   _
         | 
| 45 | 
            +
                #
         | 
| 46 | 
            +
                # @return [String] the account-name
         | 
| 47 | 
            +
                def ask_account(question, options = {})
         | 
| 48 | 
            +
                  options = { default: nil }.merge(options)
         | 
| 49 | 
            +
                  highline.ask(question) do |q|
         | 
| 50 | 
            +
                    q.default = options[:default] if options[:default]
         | 
| 113 51 | 
             
                  end
         | 
| 114 52 | 
             
                end
         | 
| 115 53 |  | 
| 116 | 
            -
             | 
| 117 | 
            -
             | 
| 118 | 
            -
            { | 
| 119 | 
            -
             | 
| 120 | 
            -
              {{to}}  {{spacer}}{{currency}} {{amount}}{{to_tags}}
         | 
| 121 | 
            -
              {{from}}{{from_tags}}
         | 
| 122 | 
            -
             | 
| 123 | 
            -
            {{/ transactions}}
         | 
| 124 | 
            -
            TEMPLATE
         | 
| 54 | 
            +
                def render_row(options = {})
         | 
| 55 | 
            +
                  options = { columns: [] }.merge(options)
         | 
| 56 | 
            +
                  _row = options[:columns].map{|i| row[i] }
         | 
| 57 | 
            +
                  $stderr.puts Terminal::Table.new(rows: [ _row ])
         | 
| 125 58 | 
             
                end
         | 
| 126 | 
            -
             | 
| 127 | 
            -
                protected
         | 
| 128 | 
            -
                  def fmt_amount(amount)
         | 
| 129 | 
            -
                    ("%10.2f" % amount).split('.') * options[:decimal_mark]
         | 
| 130 | 
            -
                  end
         | 
| 131 59 | 
             
              end
         | 
| 132 60 |  | 
| 133 | 
            -
              class  | 
| 134 | 
            -
                 | 
| 61 | 
            +
              class Config
         | 
| 62 | 
            +
                YAML::add_builtin_type('proc') {|_, val| eval("proc{ #{val} }") }
         | 
| 135 63 |  | 
| 136 | 
            -
                def initialize
         | 
| 137 | 
            -
                   | 
| 138 | 
            -
                  @ | 
| 64 | 
            +
                def initialize(options = {})
         | 
| 65 | 
            +
                  options = {file: 'total_recall.yml'}.merge(options)
         | 
| 66 | 
            +
                  @config_file = File.expand_path(options[:file])
         | 
| 139 67 | 
             
                end
         | 
| 140 68 |  | 
| 141 | 
            -
                 | 
| 142 | 
            -
             | 
| 143 | 
            -
             | 
| 144 | 
            -
             | 
| 145 | 
            -
                  search_vector = []
         | 
| 146 | 
            -
                  account_vectors = {}
         | 
| 147 | 
            -
             | 
| 148 | 
            -
                  query_tokens.each do |token|
         | 
| 149 | 
            -
                    idf = Math.log((accounts.keys.length + 1) / ((tokens[token] || {}).keys.length.to_f + 1))
         | 
| 150 | 
            -
                    tf = 1.0 / query_tokens.length.to_f
         | 
| 151 | 
            -
                    search_vector << tf*idf
         | 
| 69 | 
            +
                def config
         | 
| 70 | 
            +
                  @config ||= YAML.load_file(@config_file)
         | 
| 71 | 
            +
                end
         | 
| 152 72 |  | 
| 153 | 
            -
             | 
| 154 | 
            -
             | 
| 155 | 
            -
             | 
| 156 | 
            -
             | 
| 157 | 
            -
                    end
         | 
| 158 | 
            -
                  end
         | 
| 73 | 
            +
                def csv_file
         | 
| 74 | 
            +
                  config[:csv][:file] &&
         | 
| 75 | 
            +
                    File.expand_path(config[:csv][:file], File.dirname(@config_file))
         | 
| 76 | 
            +
                end
         | 
| 159 77 |  | 
| 160 | 
            -
             | 
| 161 | 
            -
                   | 
| 162 | 
            -
                     | 
| 163 | 
            -
             | 
| 78 | 
            +
                def csv
         | 
| 79 | 
            +
                  @csv ||= begin
         | 
| 80 | 
            +
                    csv_raw = csv_file ? File.read(csv_file) : config[:csv][:raw]
         | 
| 81 | 
            +
                    CSV.parse(csv_raw, config[:csv][:options] || {})
         | 
| 164 82 | 
             
                  end
         | 
| 165 | 
            -
             | 
| 166 | 
            -
                  account_vectors.sort! {|a, b| b[:cosine] <=> a[:cosine] }
         | 
| 167 | 
            -
                  account_vectors.first && account_vectors.first[:account]
         | 
| 168 83 | 
             
                end
         | 
| 169 84 |  | 
| 170 | 
            -
                 | 
| 171 | 
            -
             | 
| 172 | 
            -
             | 
| 173 | 
            -
                  tokenize(data).each do |token|
         | 
| 174 | 
            -
                    tokens[token] ||= {}
         | 
| 175 | 
            -
                    tokens[token][account] ||= 0
         | 
| 176 | 
            -
                    tokens[token][account] += 1
         | 
| 177 | 
            -
                    accounts[account] += 1
         | 
| 178 | 
            -
                  end
         | 
| 85 | 
            +
                def template_file
         | 
| 86 | 
            +
                  config[:template][:file] &&
         | 
| 87 | 
            +
                    File.expand_path(config[:template][:file], File.dirname(@config_file))
         | 
| 179 88 | 
             
                end
         | 
| 180 89 |  | 
| 181 | 
            -
                 | 
| 182 | 
            -
                   | 
| 183 | 
            -
                     | 
| 90 | 
            +
                def template
         | 
| 91 | 
            +
                  @template ||= begin
         | 
| 92 | 
            +
                    template_file ? File.read(template_file) : config[:template][:raw]
         | 
| 184 93 | 
             
                  end
         | 
| 185 | 
            -
              end
         | 
| 186 | 
            -
             | 
| 187 | 
            -
              class BankParser
         | 
| 188 | 
            -
                require 'csv'
         | 
| 189 | 
            -
                attr_reader :strategy
         | 
| 190 | 
            -
             | 
| 191 | 
            -
                def initialize(options={})
         | 
| 192 | 
            -
                  @strategy = options[:strategy]
         | 
| 193 94 | 
             
                end
         | 
| 194 95 |  | 
| 195 | 
            -
                 | 
| 196 | 
            -
             | 
| 197 | 
            -
                # @example
         | 
| 198 | 
            -
                #   parser = TotalRecall::BankParser.new(:strategy => TotalRecall::ParseStrategy::Some.new)
         | 
| 199 | 
            -
                #   parser.parse("12.1\n1.99") #=> [{:amount => 12.1}, {:amount => 1.99}]
         | 
| 200 | 
            -
                #
         | 
| 201 | 
            -
                # @param [String] str content of csv to parse.
         | 
| 202 | 
            -
                # @return [Array<Hash>]
         | 
| 203 | 
            -
                def parse(str, options={})
         | 
| 204 | 
            -
                  options = strategy.options.merge(options)
         | 
| 205 | 
            -
             | 
| 206 | 
            -
                  result = []
         | 
| 207 | 
            -
                  CSV.parse(str, options){|row| result << strategy.parse_row(row)}
         | 
| 208 | 
            -
                  result
         | 
| 96 | 
            +
                def session
         | 
| 97 | 
            +
                  @session ||= Helper.new(config)
         | 
| 209 98 | 
             
                end
         | 
| 210 | 
            -
              end #/BankParser
         | 
| 211 | 
            -
             | 
| 212 99 |  | 
| 213 | 
            -
             | 
| 214 | 
            -
             | 
| 215 | 
            -
                 | 
| 216 | 
            -
                require "bayes_motel"
         | 
| 217 | 
            -
             | 
| 218 | 
            -
                no_tasks do
         | 
| 219 | 
            -
                  def self.strategies
         | 
| 220 | 
            -
                    {
         | 
| 221 | 
            -
                      'abn' => TotalRecall::ParseStrategy::Abn,
         | 
| 222 | 
            -
                      'ing' => TotalRecall::ParseStrategy::Ing,
         | 
| 223 | 
            -
                      'abncc' => TotalRecall::ParseStrategy::AbnCC
         | 
| 224 | 
            -
                    }
         | 
| 225 | 
            -
                  end
         | 
| 100 | 
            +
                def context
         | 
| 101 | 
            +
                  @context ||= config[:context].merge(transactions: transactions)
         | 
| 102 | 
            +
                end
         | 
| 226 103 |  | 
| 227 | 
            -
             | 
| 228 | 
            -
             | 
| 104 | 
            +
                def transactions
         | 
| 105 | 
            +
                  @transactions ||= begin
         | 
| 106 | 
            +
                    csv.each_with_object([]) do |row, result|
         | 
| 107 | 
            +
                      result << transaction_defaults.merge(transactions_config).each_with_object({}) do |(k,v), cfg|
         | 
| 108 | 
            +
                        next if k[/^__/]
         | 
| 109 | 
            +
                        cfg[k] = v.respond_to?(:call) ? session.with_row(row, &v) : v
         | 
| 110 | 
            +
                      end
         | 
| 111 | 
            +
                    end
         | 
| 229 112 | 
             
                  end
         | 
| 113 | 
            +
                end
         | 
| 230 114 |  | 
| 231 | 
            -
             | 
| 232 | 
            -
             | 
| 115 | 
            +
                def transaction_defaults
         | 
| 116 | 
            +
                  @transaction_defaults ||= begin
         | 
| 117 | 
            +
                    defaults = transactions_config[:__defaults__] || {}
         | 
| 118 | 
            +
                    defaults.each_with_object({}) do |(k,v), result|
         | 
| 119 | 
            +
                      result[k] = v.respond_to?(:call) ? session.with_row(nil, &v) : v
         | 
| 120 | 
            +
                    end
         | 
| 233 121 | 
             
                  end
         | 
| 122 | 
            +
                end
         | 
| 234 123 |  | 
| 235 | 
            -
             | 
| 236 | 
            -
             | 
| 237 | 
            -
                  end
         | 
| 124 | 
            +
                def transactions_config
         | 
| 125 | 
            +
                  config[:context][:transactions]
         | 
| 238 126 | 
             
                end
         | 
| 239 127 |  | 
| 240 | 
            -
                desc "ledger", "Convert input to ledger-transactions"
         | 
| 241 | 
            -
                method_option :input, :aliases => "-i", :desc => "CSV file to use for input", :required => true
         | 
| 242 | 
            -
                method_option :parser, :aliases => "-p", :desc => "Parser to use (one of #{strategies.keys.inspect})", :required => true
         | 
| 243 128 | 
             
                def ledger
         | 
| 244 | 
            -
                   | 
| 245 | 
            -
             | 
| 246 | 
            -
             | 
| 247 | 
            -
             | 
| 248 | 
            -
                  guesser = AccountGuesser.new
         | 
| 249 | 
            -
             | 
| 250 | 
            -
                  default_account = highline.ask("What is the account name of this bank account in Ledger?\n> ")
         | 
| 251 | 
            -
                  transactions = [] # [{<row>, :account => 'Expenses:Car'}, ...]
         | 
| 252 | 
            -
                  tags = []
         | 
| 253 | 
            -
                  start_from = 0
         | 
| 254 | 
            -
                  begin
         | 
| 255 | 
            -
                    rows.each_with_index do |row, ix|
         | 
| 256 | 
            -
                      if start_from && start_from != ix
         | 
| 257 | 
            -
                        next
         | 
| 258 | 
            -
                      elsif start_from
         | 
| 259 | 
            -
                        start_from = nil
         | 
| 260 | 
            -
                      end
         | 
| 261 | 
            -
             | 
| 262 | 
            -
                      guessed = guesser.guess(row[:description])
         | 
| 263 | 
            -
                      question = row[:amount] > 0 ? "What account provided this income?" : "To which account did this money go?"
         | 
| 129 | 
            +
                  Mustache.render(template, context)
         | 
| 130 | 
            +
                end
         | 
| 131 | 
            +
              end
         | 
| 264 132 |  | 
| 265 | 
            -
             | 
| 266 | 
            -
             | 
| 267 | 
            -
                      account = account.empty? ? guessed : account # defaults not working
         | 
| 268 | 
            -
                      case account
         | 
| 269 | 
            -
                      when 'd'
         | 
| 270 | 
            -
                        break
         | 
| 271 | 
            -
                      when 's'
         | 
| 272 | 
            -
                        next
         | 
| 273 | 
            -
                      when 'p'
         | 
| 274 | 
            -
                        transactions.pop
         | 
| 275 | 
            -
                        start_from = [0, (ix - 1)].max
         | 
| 276 | 
            -
                        raise
         | 
| 277 | 
            -
                      end
         | 
| 133 | 
            +
              class Cli < Thor
         | 
| 134 | 
            +
                require 'total_recall/version'
         | 
| 278 135 |  | 
| 279 | 
            -
             | 
| 280 | 
            -
             | 
| 281 | 
            -
                        tag = highline.choose do |menu|
         | 
| 282 | 
            -
                          menu.shell = true
         | 
| 283 | 
            -
                          menu.prompt = "Add tags? "
         | 
| 284 | 
            -
                          menu.prompt << "(#{transaction_tags.join(',')})" if transaction_tags.any?
         | 
| 136 | 
            +
                include Thor::Actions
         | 
| 137 | 
            +
                source_root File.expand_path('../total_recall/templates', __FILE__)
         | 
| 285 138 |  | 
| 286 | 
            -
             | 
| 287 | 
            -
             | 
| 288 | 
            -
             | 
| 289 | 
            -
             | 
| 290 | 
            -
             | 
| 291 | 
            -
                        transaction_tags << tag
         | 
| 292 | 
            -
                      end
         | 
| 139 | 
            +
                desc "ledger", "Convert the config to a ledger"
         | 
| 140 | 
            +
                method_option :config, :aliases => "-c", :desc => "Config file", :required => true
         | 
| 141 | 
            +
                def ledger
         | 
| 142 | 
            +
                  puts TotalRecall::Config.new(file: File.expand_path(options[:config])).ledger
         | 
| 143 | 
            +
                end
         | 
| 293 144 |  | 
| 294 | 
            -
             | 
| 295 | 
            -
             | 
| 296 | 
            -
             | 
| 297 | 
            -
                   | 
| 298 | 
            -
                    $stderr.puts "Let's do that again"
         | 
| 299 | 
            -
                    retry
         | 
| 300 | 
            -
                  end
         | 
| 145 | 
            +
                desc "sample", "Generate an annotated config"
         | 
| 146 | 
            +
                def sample
         | 
| 147 | 
            +
                  @version = TotalRecall::VERSION
         | 
| 148 | 
            +
                  template("sample.yml.tt")
         | 
| 301 149 |  | 
| 302 | 
            -
                   | 
| 150 | 
            +
                  say "Now run '#{$0} ledger -c sample.yml' to generate the ledger"
         | 
| 303 151 | 
             
                end
         | 
| 304 152 |  | 
| 305 | 
            -
                desc " | 
| 306 | 
            -
                 | 
| 307 | 
            -
             | 
| 308 | 
            -
                def parse
         | 
| 309 | 
            -
                  strategy = strategies[options[:parser]]
         | 
| 310 | 
            -
                  file_contents = File.read(options[:input])
         | 
| 311 | 
            -
                  data = parser(strategy.new).parse(file_contents)
         | 
| 153 | 
            +
                desc "init NAME", "Generate a minimal config NAME.yml"
         | 
| 154 | 
            +
                def init(name = "total_recall")
         | 
| 155 | 
            +
                  destination = name + ".yml"
         | 
| 312 156 |  | 
| 313 | 
            -
                   | 
| 314 | 
            -
             | 
| 315 | 
            -
             | 
| 316 | 
            -
                      t << [ row[:date], row[:amount], row[:description] ]
         | 
| 317 | 
            -
                    end
         | 
| 318 | 
            -
                  end
         | 
| 319 | 
            -
                  puts table
         | 
| 157 | 
            +
                  @version = TotalRecall::VERSION
         | 
| 158 | 
            +
                  @name = name
         | 
| 159 | 
            +
                  template("simple.yml.tt", destination)
         | 
| 320 160 | 
             
                end
         | 
| 321 | 
            -
             | 
| 322 161 | 
             
              end
         | 
| 323 162 | 
             
            end
         | 
| @@ -0,0 +1,79 @@ | |
| 1 | 
            +
            ---
         | 
| 2 | 
            +
            :total_recall:
         | 
| 3 | 
            +
              # The version of total_recall that generated this file
         | 
| 4 | 
            +
              :version: <%= @version %>
         | 
| 5 | 
            +
            :template:
         | 
| 6 | 
            +
              # 1). Provide the [mustache-template](https://github.com/defunkt/mustache)
         | 
| 7 | 
            +
              #   for your ledger.
         | 
| 8 | 
            +
              #
         | 
| 9 | 
            +
              # Either assign a template to :raw: or point to a file via :file:.
         | 
| 10 | 
            +
              # The location of :file: is relative to this file.
         | 
| 11 | 
            +
              # :file: takes precedence over :raw:
         | 
| 12 | 
            +
              #
         | 
| 13 | 
            +
              # The template MUST contain a section `transactions`.
         | 
| 14 | 
            +
              #
         | 
| 15 | 
            +
              #:file: ledger.mustache
         | 
| 16 | 
            +
              :raw: |-
         | 
| 17 | 
            +
                ; -*- ledger -*-¬
         | 
| 18 | 
            +
                ; Generated by total_recall at {{generated_at}}
         | 
| 19 | 
            +
                --decimal-comma
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                {{# transactions}}
         | 
| 22 | 
            +
                {{date}} {{{description}}}
         | 
| 23 | 
            +
                  {{to}}    {{currency}} {{amount}}
         | 
| 24 | 
            +
                  {{from}}
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                {{/ transactions}}
         | 
| 27 | 
            +
             | 
| 28 | 
            +
            :csv:
         | 
| 29 | 
            +
              # 2). Provide the csv
         | 
| 30 | 
            +
              #
         | 
| 31 | 
            +
              # Either assign csv to :raw: or point to a csv-file via :file:.
         | 
| 32 | 
            +
              # :raw: is ideal for experimenting with the csv-format.
         | 
| 33 | 
            +
              #
         | 
| 34 | 
            +
              # The location of :file: is relative to this file.
         | 
| 35 | 
            +
              # :file: takes precedence over :raw:
         | 
| 36 | 
            +
              #
         | 
| 37 | 
            +
              #:file: sample.csv
         | 
| 38 | 
            +
              :raw: |-
         | 
| 39 | 
            +
                "03.11.2013";"Foo";"04.11.2013";"1.638,00";""
         | 
| 40 | 
            +
                "03.11.2013";"Bar";"04.11.2013";"-492,93";""
         | 
| 41 | 
            +
              :options:
         | 
| 42 | 
            +
                # Any option accepted by CSV#new (http://www.ruby-doc.org/stdlib-2.1.1/libdoc/csv/rdoc/CSV.html#method-c-new).
         | 
| 43 | 
            +
                :col_sep: ";"
         | 
| 44 | 
            +
                :headers: false
         | 
| 45 | 
            +
             | 
| 46 | 
            +
            :context:
         | 
| 47 | 
            +
              # 3). Define the context
         | 
| 48 | 
            +
              #
         | 
| 49 | 
            +
              # The context is in essence the dictionary that will be applied to the template.
         | 
| 50 | 
            +
              # The value for transactions will be expanded to an array, one for every line in the csv.
         | 
| 51 | 
            +
              :transactions:
         | 
| 52 | 
            +
                # transactions have defaults...
         | 
| 53 | 
            +
                :__defaults__:
         | 
| 54 | 
            +
                  :from: !!proc |
         | 
| 55 | 
            +
                    ask_account("What account provides these transactions?",
         | 
| 56 | 
            +
                                  default: 'Assets:Checking')
         | 
| 57 | 
            +
                  :currency: $
         | 
| 58 | 
            +
                # ...and fields that vary per transaction.
         | 
| 59 | 
            +
                # Helper method exist such as `row`, `ask_account` (see `TotalRecall::Helper` for more).
         | 
| 60 | 
            +
                :date: !!proc |
         | 
| 61 | 
            +
                  # For weird date formats consider using Date#strptime (http://www.ruby-doc.org/stdlib-2.1.1/libdoc/date/rdoc/Date.html#method-c-strptime)
         | 
| 62 | 
            +
                  Date.parse(row[0])
         | 
| 63 | 
            +
                :description: !!proc row[1]
         | 
| 64 | 
            +
                :amount: !!proc row[3]
         | 
| 65 | 
            +
                :to: !!proc |
         | 
| 66 | 
            +
                  # show the row and let the user decide
         | 
| 67 | 
            +
                  render_row(columns: [0, 1, 3])
         | 
| 68 | 
            +
                  ask_account("To what account did the money go?")
         | 
| 69 | 
            +
              :generated_at: !!proc Time.now
         | 
| 70 | 
            +
             | 
| 71 | 
            +
            # You can add any other data needed:
         | 
| 72 | 
            +
            # :ticker_sybols:
         | 
| 73 | 
            +
            #   "Apple": AAPL
         | 
| 74 | 
            +
            #   "Google Inc.": GOOG
         | 
| 75 | 
            +
            #
         | 
| 76 | 
            +
            # Example usage:
         | 
| 77 | 
            +
            # :context:
         | 
| 78 | 
            +
            #   :transactions:
         | 
| 79 | 
            +
            #     :symbol: !!proc config[:ticker_symbols][row[1]]
         | 
| @@ -0,0 +1,35 @@ | |
| 1 | 
            +
            ---
         | 
| 2 | 
            +
            :total_recall:
         | 
| 3 | 
            +
              :version: <%= @version %>
         | 
| 4 | 
            +
            :template:
         | 
| 5 | 
            +
              :raw: |-
         | 
| 6 | 
            +
                ; -*- ledger -*-¬
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                {{# transactions}}
         | 
| 9 | 
            +
                {{date}} {{{description}}}
         | 
| 10 | 
            +
                  {{to}}    {{currency}} {{amount}}
         | 
| 11 | 
            +
                  {{from}}
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                {{/ transactions}}
         | 
| 14 | 
            +
             | 
| 15 | 
            +
            :csv:
         | 
| 16 | 
            +
              #:file: <%= @name %>.csv
         | 
| 17 | 
            +
              :raw: |-
         | 
| 18 | 
            +
                "03.11.2013";"Foo";"04.11.2013";"1.638,00";""
         | 
| 19 | 
            +
                "03.11.2013";"Bar";"04.11.2013";"-492,93";""
         | 
| 20 | 
            +
              :options:
         | 
| 21 | 
            +
                :col_sep: ";"
         | 
| 22 | 
            +
                :headers: false
         | 
| 23 | 
            +
             | 
| 24 | 
            +
            :context:
         | 
| 25 | 
            +
              :transactions:
         | 
| 26 | 
            +
                :__defaults__:
         | 
| 27 | 
            +
                  :from: !!proc |
         | 
| 28 | 
            +
                    ask_account("What account provides these transactions?", default: 'Assets:Checking')
         | 
| 29 | 
            +
                  :currency: $
         | 
| 30 | 
            +
                :date: !!proc Date.parse(row[0])
         | 
| 31 | 
            +
                :description: !!proc row[1]
         | 
| 32 | 
            +
                :amount: !!proc row[3]
         | 
| 33 | 
            +
                :to: !!proc |
         | 
| 34 | 
            +
                  render_row(columns: [0, 1, 3, 4])
         | 
| 35 | 
            +
                  ask_account("To what account did the money go?")
         | 
    
        data/lib/total_recall/version.rb
    CHANGED
    
    
    
        data/spec/spec_helper.rb
    CHANGED
    
    | @@ -4,12 +4,4 @@ Bundler.setup | |
| 4 4 |  | 
| 5 5 | 
             
            $:.unshift File.expand_path("../../lib", __FILE__)
         | 
| 6 6 | 
             
            require "total_recall"
         | 
| 7 | 
            -
             | 
| 8 | 
            -
            # fullpath to csv-file
         | 
| 9 | 
            -
            def fixture_pathname(str)
         | 
| 10 | 
            -
              Pathname(%W(spec fixtures #{str}.csv).join(File::SEPARATOR)).realpath
         | 
| 11 | 
            -
            end
         | 
| 12 | 
            -
             | 
| 13 | 
            -
            def fixture_contents(str)
         | 
| 14 | 
            -
              File.read(fixture_pathname(str))
         | 
| 15 | 
            -
            end
         | 
| 7 | 
            +
            require 'fakefs/spec_helpers'
         | 
| @@ -0,0 +1,176 @@ | |
| 1 | 
            +
            require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            describe TotalRecall::Config do
         | 
| 4 | 
            +
              include FakeFS::SpecHelpers
         | 
| 5 | 
            +
             | 
| 6 | 
            +
              def stubbed_file(path, content)
         | 
| 7 | 
            +
                # SOURCE http://edgeapi.rubyonrails.org/classes/String.html#method-i-strip_heredoc
         | 
| 8 | 
            +
                indent = content.scan(/^[ \t]*(?=\S)/).min.size rescue 0
         | 
| 9 | 
            +
                content = content.gsub(/^[ \t]{#{indent}}/, '')
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                FakeFS do
         | 
| 12 | 
            +
                  File.open(path, 'w'){|f| f.print content }
         | 
| 13 | 
            +
                end
         | 
| 14 | 
            +
              end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
              def instance_with_config(config, options = {})
         | 
| 17 | 
            +
                options = {file: 'config.yml'}.merge(options)
         | 
| 18 | 
            +
                stubbed_file(options[:file], config)
         | 
| 19 | 
            +
                described_class.new(options)
         | 
| 20 | 
            +
              end
         | 
| 21 | 
            +
             | 
| 22 | 
            +
              describe '#config' do
         | 
| 23 | 
            +
                it 'yields the config as hash' do
         | 
| 24 | 
            +
                  instance = instance_with_config(<<-CONFIG)
         | 
| 25 | 
            +
                  :csv:
         | 
| 26 | 
            +
                    :raw: Some csv
         | 
| 27 | 
            +
                  :a: 1
         | 
| 28 | 
            +
                  CONFIG
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                  expect(instance.config).to eql({csv: { raw: 'Some csv'}, a: 1})
         | 
| 31 | 
            +
                end
         | 
| 32 | 
            +
              end
         | 
| 33 | 
            +
             | 
| 34 | 
            +
              describe '#csv' do
         | 
| 35 | 
            +
                it 'yields csv assigned to :raw' do
         | 
| 36 | 
            +
                  instance = instance_with_config(<<-CONFIG)
         | 
| 37 | 
            +
                  :csv:
         | 
| 38 | 
            +
                    :raw: Some csv
         | 
| 39 | 
            +
                  CONFIG
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                  expect(instance.csv).to eql(CSV.parse('Some csv'))
         | 
| 42 | 
            +
                end
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                it 'yields csv from file :file' do
         | 
| 45 | 
            +
                  csv_file = stubbed_file('some.csv', 'Some csv')
         | 
| 46 | 
            +
                  instance = instance_with_config(<<-CONFIG)
         | 
| 47 | 
            +
                  :csv:
         | 
| 48 | 
            +
                    :file: some.csv
         | 
| 49 | 
            +
                  CONFIG
         | 
| 50 | 
            +
             | 
| 51 | 
            +
                  expect(instance.csv).to eql(CSV.parse('Some csv'))
         | 
| 52 | 
            +
                end
         | 
| 53 | 
            +
             | 
| 54 | 
            +
                it 'yields csv from :file when both :raw and :file are configured' do
         | 
| 55 | 
            +
                  csv_file = stubbed_file('some.csv', 'Some csv')
         | 
| 56 | 
            +
                  instance = instance_with_config(<<-CONFIG)
         | 
| 57 | 
            +
                  :csv:
         | 
| 58 | 
            +
                    :file: some.csv
         | 
| 59 | 
            +
                    :raw: Some raw csv
         | 
| 60 | 
            +
                  CONFIG
         | 
| 61 | 
            +
             | 
| 62 | 
            +
                  expect(instance.csv).to eql(CSV.parse('Some csv'))
         | 
| 63 | 
            +
                end
         | 
| 64 | 
            +
             | 
| 65 | 
            +
                specify 'csv-options are passed on to CSV#read' do
         | 
| 66 | 
            +
                  instance = instance_with_config(<<-CONFIG)
         | 
| 67 | 
            +
                  :csv:
         | 
| 68 | 
            +
                    :options:
         | 
| 69 | 
            +
                      :option1: true
         | 
| 70 | 
            +
                  CONFIG
         | 
| 71 | 
            +
             | 
| 72 | 
            +
                  expect(CSV).to receive(:parse).with(anything(), { option1: true })
         | 
| 73 | 
            +
                  instance.csv
         | 
| 74 | 
            +
                end
         | 
| 75 | 
            +
              end
         | 
| 76 | 
            +
             | 
| 77 | 
            +
              describe '#template' do
         | 
| 78 | 
            +
                it 'yields template assigned to :raw' do
         | 
| 79 | 
            +
                  instance = instance_with_config(<<-CONFIG)
         | 
| 80 | 
            +
                  :template:
         | 
| 81 | 
            +
                    :raw: |-
         | 
| 82 | 
            +
                      Raw template
         | 
| 83 | 
            +
                      here
         | 
| 84 | 
            +
                  CONFIG
         | 
| 85 | 
            +
             | 
| 86 | 
            +
                  expect(instance.template).to eql("Raw template\nhere")
         | 
| 87 | 
            +
                end
         | 
| 88 | 
            +
             | 
| 89 | 
            +
                it 'yields template from file :file' do
         | 
| 90 | 
            +
                  template_file = stubbed_file('template.mustache', 'File template')
         | 
| 91 | 
            +
                  instance = instance_with_config(<<-CONFIG)
         | 
| 92 | 
            +
                  :template:
         | 
| 93 | 
            +
                    :file: template.mustache
         | 
| 94 | 
            +
                  CONFIG
         | 
| 95 | 
            +
             | 
| 96 | 
            +
                  expect(instance.template).to eql('File template')
         | 
| 97 | 
            +
                end
         | 
| 98 | 
            +
             | 
| 99 | 
            +
                it 'yields template from :file when both :raw and :file are configured' do
         | 
| 100 | 
            +
                  template_file = stubbed_file('template.mustache', 'File template')
         | 
| 101 | 
            +
                  instance = instance_with_config(<<-CONFIG)
         | 
| 102 | 
            +
                  :template:
         | 
| 103 | 
            +
                    :file: template.mustache
         | 
| 104 | 
            +
                    :raw: Raw template
         | 
| 105 | 
            +
                  CONFIG
         | 
| 106 | 
            +
             | 
| 107 | 
            +
                  expect(instance.template).to eql('File template')
         | 
| 108 | 
            +
                end
         | 
| 109 | 
            +
              end
         | 
| 110 | 
            +
             | 
| 111 | 
            +
              describe 'YAML types' do
         | 
| 112 | 
            +
                it 'allows proc-types' do
         | 
| 113 | 
            +
                  instance = instance_with_config(<<-CONFIG)
         | 
| 114 | 
            +
                  :a: !!proc 1 + 1
         | 
| 115 | 
            +
                  :b: !!proc |
         | 
| 116 | 
            +
                    1 + 1
         | 
| 117 | 
            +
                  CONFIG
         | 
| 118 | 
            +
             | 
| 119 | 
            +
                  expect(instance.config[:a].call).to eq 2
         | 
| 120 | 
            +
                  expect(instance.config[:b].call).to eq 2
         | 
| 121 | 
            +
                end
         | 
| 122 | 
            +
              end
         | 
| 123 | 
            +
             | 
| 124 | 
            +
              describe '#context' do
         | 
| 125 | 
            +
                it 'has a transaction per line of csv' do
         | 
| 126 | 
            +
                  instance = instance_with_config(<<-CONFIG)
         | 
| 127 | 
            +
                  :csv:
         | 
| 128 | 
            +
                    :raw: |-
         | 
| 129 | 
            +
                      1
         | 
| 130 | 
            +
                      1
         | 
| 131 | 
            +
                  :context:
         | 
| 132 | 
            +
                    :transactions:
         | 
| 133 | 
            +
                      :from: From
         | 
| 134 | 
            +
                      :to: !!proc 1 + 1
         | 
| 135 | 
            +
                      :amount: !!proc row[0]
         | 
| 136 | 
            +
                  CONFIG
         | 
| 137 | 
            +
             | 
| 138 | 
            +
                  transactions = instance.context[:transactions]
         | 
| 139 | 
            +
                  expect(transactions.size).to eq 2
         | 
| 140 | 
            +
             | 
| 141 | 
            +
                  transaction = transactions.first
         | 
| 142 | 
            +
                  expect(transaction).to match({from: 'From', to: 2, amount: "1"})
         | 
| 143 | 
            +
                end
         | 
| 144 | 
            +
             | 
| 145 | 
            +
                it 'adds defaults to every transaction' do
         | 
| 146 | 
            +
                  instance = instance_with_config(<<-CONFIG)
         | 
| 147 | 
            +
                  :csv:
         | 
| 148 | 
            +
                    :raw: |-
         | 
| 149 | 
            +
                      line 1
         | 
| 150 | 
            +
                      line 2
         | 
| 151 | 
            +
                  :context:
         | 
| 152 | 
            +
                    :transactions:
         | 
| 153 | 
            +
                      :__defaults__:
         | 
| 154 | 
            +
                        :default: !!proc 1
         | 
| 155 | 
            +
                      :from: From
         | 
| 156 | 
            +
                  CONFIG
         | 
| 157 | 
            +
             | 
| 158 | 
            +
                  transaction = instance.context[:transactions].first
         | 
| 159 | 
            +
             | 
| 160 | 
            +
                  expect(transaction).to match({from: 'From', default: 1})
         | 
| 161 | 
            +
                end
         | 
| 162 | 
            +
             | 
| 163 | 
            +
                it 'may contain any other settings' do
         | 
| 164 | 
            +
                  instance = instance_with_config(<<-CONFIG)
         | 
| 165 | 
            +
                  :csv:
         | 
| 166 | 
            +
                    :raw: some csv
         | 
| 167 | 
            +
                  :context:
         | 
| 168 | 
            +
                    :transactions:
         | 
| 169 | 
            +
                      :from: From
         | 
| 170 | 
            +
                    :a: 1
         | 
| 171 | 
            +
                  CONFIG
         | 
| 172 | 
            +
             | 
| 173 | 
            +
                  expect(instance.context).to match(transactions: [{from: 'From'}], a: 1)
         | 
| 174 | 
            +
                end
         | 
| 175 | 
            +
              end
         | 
| 176 | 
            +
            end
         | 
    
        data/total_recall.gemspec
    CHANGED
    
    | @@ -1,24 +1,29 @@ | |
| 1 | 
            -
            #  | 
| 2 | 
            -
             | 
| 1 | 
            +
            # coding: utf-8
         | 
| 2 | 
            +
            lib = File.expand_path('../lib', __FILE__)
         | 
| 3 | 
            +
            $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
         | 
| 4 | 
            +
            require 'total_recall/version'
         | 
| 3 5 |  | 
| 4 6 | 
             
            Gem::Specification.new do |gem|
         | 
| 7 | 
            +
              gem.name          = "total_recall"
         | 
| 8 | 
            +
              gem.version       = TotalRecall::VERSION
         | 
| 5 9 | 
             
              gem.authors       = ["Gert Goet"]
         | 
| 6 10 | 
             
              gem.email         = ["gert@thinkcreate.nl"]
         | 
| 7 | 
            -
              gem.description   = %q{Turn  | 
| 8 | 
            -
              gem.summary       = %q{Turn  | 
| 9 | 
            -
              gem.homepage      = ""
         | 
| 11 | 
            +
              gem.description   = %q{Turn any csv into a Ledger journal}
         | 
| 12 | 
            +
              gem.summary       = %q{Turn any csv into a Ledger journal}
         | 
| 13 | 
            +
              gem.homepage      = "https://github.com/eval/total_recall"
         | 
| 14 | 
            +
              gem.license       = "MIT"
         | 
| 10 15 |  | 
| 11 | 
            -
              gem. | 
| 12 | 
            -
              gem. | 
| 13 | 
            -
              gem.test_files    =  | 
| 14 | 
            -
              gem.name          = "total_recall"
         | 
| 16 | 
            +
              gem.files         = `git ls-files -z`.split("\x0")
         | 
| 17 | 
            +
              gem.executables   = gem.files.grep(%r{^bin/}) { |f| File.basename(f) }
         | 
| 18 | 
            +
              gem.test_files    = gem.files.grep(%r{^(test|spec|features)/})
         | 
| 15 19 | 
             
              gem.require_paths = ["lib"]
         | 
| 16 | 
            -
              gem.version       = TotalRecall::VERSION
         | 
| 17 20 |  | 
| 18 | 
            -
              gem.add_dependency | 
| 19 | 
            -
              gem.add_dependency | 
| 20 | 
            -
              gem.add_dependency | 
| 21 | 
            -
              gem.add_dependency | 
| 22 | 
            -
              gem. | 
| 23 | 
            -
              gem.add_development_dependency | 
| 21 | 
            +
              gem.add_dependency 'thor', '~> 0.19'
         | 
| 22 | 
            +
              gem.add_dependency 'terminal-table', '~> 1.4'
         | 
| 23 | 
            +
              gem.add_dependency 'highline', '~> 1.6'
         | 
| 24 | 
            +
              gem.add_dependency 'mustache', '~> 0.99'
         | 
| 25 | 
            +
              gem.add_development_dependency "bundler", "~> 1.6"
         | 
| 26 | 
            +
              gem.add_development_dependency "rake"
         | 
| 27 | 
            +
              gem.add_development_dependency 'rspec', '~> 3.0.0'
         | 
| 28 | 
            +
              gem.add_development_dependency 'fakefs'
         | 
| 24 29 | 
             
            end
         | 
    
        metadata
    CHANGED
    
    | @@ -1,83 +1,128 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: total_recall
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 0. | 
| 5 | 
            -
              prerelease: 
         | 
| 4 | 
            +
              version: 0.4.0
         | 
| 6 5 | 
             
            platform: ruby
         | 
| 7 6 | 
             
            authors:
         | 
| 8 7 | 
             
            - Gert Goet
         | 
| 9 8 | 
             
            autorequire: 
         | 
| 10 9 | 
             
            bindir: bin
         | 
| 11 10 | 
             
            cert_chain: []
         | 
| 12 | 
            -
            date:  | 
| 11 | 
            +
            date: 2014-06-04 00:00:00.000000000 Z
         | 
| 13 12 | 
             
            dependencies:
         | 
| 14 13 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 15 14 | 
             
              name: thor
         | 
| 16 | 
            -
              requirement:  | 
| 17 | 
            -
                none: false
         | 
| 15 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 18 16 | 
             
                requirements:
         | 
| 19 | 
            -
                - - ~>
         | 
| 17 | 
            +
                - - "~>"
         | 
| 20 18 | 
             
                  - !ruby/object:Gem::Version
         | 
| 21 | 
            -
                    version: 0. | 
| 19 | 
            +
                    version: '0.19'
         | 
| 22 20 | 
             
              type: :runtime
         | 
| 23 21 | 
             
              prerelease: false
         | 
| 24 | 
            -
              version_requirements:  | 
| 22 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 23 | 
            +
                requirements:
         | 
| 24 | 
            +
                - - "~>"
         | 
| 25 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 26 | 
            +
                    version: '0.19'
         | 
| 25 27 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 26 28 | 
             
              name: terminal-table
         | 
| 27 | 
            -
              requirement:  | 
| 28 | 
            -
                none: false
         | 
| 29 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 29 30 | 
             
                requirements:
         | 
| 30 | 
            -
                - - ~>
         | 
| 31 | 
            +
                - - "~>"
         | 
| 31 32 | 
             
                  - !ruby/object:Gem::Version
         | 
| 32 | 
            -
                    version: 1.4 | 
| 33 | 
            +
                    version: '1.4'
         | 
| 33 34 | 
             
              type: :runtime
         | 
| 34 35 | 
             
              prerelease: false
         | 
| 35 | 
            -
              version_requirements:  | 
| 36 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 37 | 
            +
                requirements:
         | 
| 38 | 
            +
                - - "~>"
         | 
| 39 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 40 | 
            +
                    version: '1.4'
         | 
| 36 41 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 37 42 | 
             
              name: highline
         | 
| 38 | 
            -
              requirement:  | 
| 39 | 
            -
                none: false
         | 
| 43 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 40 44 | 
             
                requirements:
         | 
| 41 | 
            -
                - - ~>
         | 
| 45 | 
            +
                - - "~>"
         | 
| 42 46 | 
             
                  - !ruby/object:Gem::Version
         | 
| 43 | 
            -
                    version: 1.6 | 
| 47 | 
            +
                    version: '1.6'
         | 
| 44 48 | 
             
              type: :runtime
         | 
| 45 49 | 
             
              prerelease: false
         | 
| 46 | 
            -
              version_requirements:  | 
| 50 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 51 | 
            +
                requirements:
         | 
| 52 | 
            +
                - - "~>"
         | 
| 53 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 54 | 
            +
                    version: '1.6'
         | 
| 47 55 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 48 | 
            -
              name:  | 
| 49 | 
            -
              requirement:  | 
| 50 | 
            -
                none: false
         | 
| 56 | 
            +
              name: mustache
         | 
| 57 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 51 58 | 
             
                requirements:
         | 
| 52 | 
            -
                - - ~>
         | 
| 59 | 
            +
                - - "~>"
         | 
| 53 60 | 
             
                  - !ruby/object:Gem::Version
         | 
| 54 | 
            -
                    version: 0. | 
| 61 | 
            +
                    version: '0.99'
         | 
| 55 62 | 
             
              type: :runtime
         | 
| 56 63 | 
             
              prerelease: false
         | 
| 57 | 
            -
              version_requirements:  | 
| 64 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 65 | 
            +
                requirements:
         | 
| 66 | 
            +
                - - "~>"
         | 
| 67 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 68 | 
            +
                    version: '0.99'
         | 
| 58 69 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 59 | 
            -
              name:  | 
| 60 | 
            -
              requirement:  | 
| 61 | 
            -
                none: false
         | 
| 70 | 
            +
              name: bundler
         | 
| 71 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 62 72 | 
             
                requirements:
         | 
| 63 | 
            -
                - - ~>
         | 
| 73 | 
            +
                - - "~>"
         | 
| 64 74 | 
             
                  - !ruby/object:Gem::Version
         | 
| 65 | 
            -
                    version:  | 
| 66 | 
            -
              type: : | 
| 75 | 
            +
                    version: '1.6'
         | 
| 76 | 
            +
              type: :development
         | 
| 77 | 
            +
              prerelease: false
         | 
| 78 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 79 | 
            +
                requirements:
         | 
| 80 | 
            +
                - - "~>"
         | 
| 81 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 82 | 
            +
                    version: '1.6'
         | 
| 83 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 84 | 
            +
              name: rake
         | 
| 85 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 86 | 
            +
                requirements:
         | 
| 87 | 
            +
                - - ">="
         | 
| 88 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 89 | 
            +
                    version: '0'
         | 
| 90 | 
            +
              type: :development
         | 
| 67 91 | 
             
              prerelease: false
         | 
| 68 | 
            -
              version_requirements:  | 
| 92 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 93 | 
            +
                requirements:
         | 
| 94 | 
            +
                - - ">="
         | 
| 95 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 96 | 
            +
                    version: '0'
         | 
| 69 97 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 70 98 | 
             
              name: rspec
         | 
| 71 | 
            -
              requirement:  | 
| 72 | 
            -
                none: false
         | 
| 99 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 73 100 | 
             
                requirements:
         | 
| 74 | 
            -
                - - ~>
         | 
| 101 | 
            +
                - - "~>"
         | 
| 75 102 | 
             
                  - !ruby/object:Gem::Version
         | 
| 76 | 
            -
                    version:  | 
| 103 | 
            +
                    version: 3.0.0
         | 
| 77 104 | 
             
              type: :development
         | 
| 78 105 | 
             
              prerelease: false
         | 
| 79 | 
            -
              version_requirements:  | 
| 80 | 
            -
             | 
| 106 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 107 | 
            +
                requirements:
         | 
| 108 | 
            +
                - - "~>"
         | 
| 109 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 110 | 
            +
                    version: 3.0.0
         | 
| 111 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 112 | 
            +
              name: fakefs
         | 
| 113 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 114 | 
            +
                requirements:
         | 
| 115 | 
            +
                - - ">="
         | 
| 116 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 117 | 
            +
                    version: '0'
         | 
| 118 | 
            +
              type: :development
         | 
| 119 | 
            +
              prerelease: false
         | 
| 120 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 121 | 
            +
                requirements:
         | 
| 122 | 
            +
                - - ">="
         | 
| 123 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 124 | 
            +
                    version: '0'
         | 
| 125 | 
            +
            description: Turn any csv into a Ledger journal
         | 
| 81 126 | 
             
            email:
         | 
| 82 127 | 
             
            - gert@thinkcreate.nl
         | 
| 83 128 | 
             
            executables:
         | 
| @@ -86,57 +131,46 @@ executables: | |
| 86 131 | 
             
            extensions: []
         | 
| 87 132 | 
             
            extra_rdoc_files: []
         | 
| 88 133 | 
             
            files:
         | 
| 89 | 
            -
            - .gitignore
         | 
| 134 | 
            +
            - ".gitignore"
         | 
| 135 | 
            +
            - ".travis.yml"
         | 
| 90 136 | 
             
            - Gemfile
         | 
| 91 137 | 
             
            - README.md
         | 
| 92 138 | 
             
            - Rakefile
         | 
| 93 139 | 
             
            - bin/total_recall
         | 
| 94 140 | 
             
            - bin/total_recall.rb
         | 
| 95 141 | 
             
            - lib/total_recall.rb
         | 
| 142 | 
            +
            - lib/total_recall/templates/sample.yml.tt
         | 
| 143 | 
            +
            - lib/total_recall/templates/simple.yml.tt
         | 
| 96 144 | 
             
            - lib/total_recall/version.rb
         | 
| 97 | 
            -
            - spec/fixtures/.keep
         | 
| 98 | 
            -
            - spec/fixtures/abn.csv
         | 
| 99 | 
            -
            - spec/fixtures/abncc.csv
         | 
| 100 | 
            -
            - spec/fixtures/ing.csv
         | 
| 101 145 | 
             
            - spec/spec.opts
         | 
| 102 146 | 
             
            - spec/spec_helper.rb
         | 
| 103 | 
            -
            - spec/total_recall/ | 
| 147 | 
            +
            - spec/total_recall/total_recall_spec.rb
         | 
| 104 148 | 
             
            - total_recall.gemspec
         | 
| 105 | 
            -
            homepage:  | 
| 106 | 
            -
            licenses: | 
| 149 | 
            +
            homepage: https://github.com/eval/total_recall
         | 
| 150 | 
            +
            licenses:
         | 
| 151 | 
            +
            - MIT
         | 
| 152 | 
            +
            metadata: {}
         | 
| 107 153 | 
             
            post_install_message: 
         | 
| 108 154 | 
             
            rdoc_options: []
         | 
| 109 155 | 
             
            require_paths:
         | 
| 110 156 | 
             
            - lib
         | 
| 111 157 | 
             
            required_ruby_version: !ruby/object:Gem::Requirement
         | 
| 112 | 
            -
              none: false
         | 
| 113 158 | 
             
              requirements:
         | 
| 114 | 
            -
              - -  | 
| 159 | 
            +
              - - ">="
         | 
| 115 160 | 
             
                - !ruby/object:Gem::Version
         | 
| 116 161 | 
             
                  version: '0'
         | 
| 117 | 
            -
                  segments:
         | 
| 118 | 
            -
                  - 0
         | 
| 119 | 
            -
                  hash: -1362278848564129285
         | 
| 120 162 | 
             
            required_rubygems_version: !ruby/object:Gem::Requirement
         | 
| 121 | 
            -
              none: false
         | 
| 122 163 | 
             
              requirements:
         | 
| 123 | 
            -
              - -  | 
| 164 | 
            +
              - - ">="
         | 
| 124 165 | 
             
                - !ruby/object:Gem::Version
         | 
| 125 166 | 
             
                  version: '0'
         | 
| 126 | 
            -
                  segments:
         | 
| 127 | 
            -
                  - 0
         | 
| 128 | 
            -
                  hash: -1362278848564129285
         | 
| 129 167 | 
             
            requirements: []
         | 
| 130 168 | 
             
            rubyforge_project: 
         | 
| 131 | 
            -
            rubygems_version:  | 
| 169 | 
            +
            rubygems_version: 2.2.2
         | 
| 132 170 | 
             
            signing_key: 
         | 
| 133 | 
            -
            specification_version:  | 
| 134 | 
            -
            summary: Turn  | 
| 171 | 
            +
            specification_version: 4
         | 
| 172 | 
            +
            summary: Turn any csv into a Ledger journal
         | 
| 135 173 | 
             
            test_files:
         | 
| 136 | 
            -
            - spec/fixtures/.keep
         | 
| 137 | 
            -
            - spec/fixtures/abn.csv
         | 
| 138 | 
            -
            - spec/fixtures/abncc.csv
         | 
| 139 | 
            -
            - spec/fixtures/ing.csv
         | 
| 140 174 | 
             
            - spec/spec.opts
         | 
| 141 175 | 
             
            - spec/spec_helper.rb
         | 
| 142 | 
            -
            - spec/total_recall/ | 
| 176 | 
            +
            - spec/total_recall/total_recall_spec.rb
         | 
    
        data/spec/fixtures/.keep
    DELETED
    
    | 
            File without changes
         | 
    
        data/spec/fixtures/abn.csv
    DELETED
    
    
    
        data/spec/fixtures/abncc.csv
    DELETED
    
    
    
        data/spec/fixtures/ing.csv
    DELETED
    
    | @@ -1,3 +0,0 @@ | |
| 1 | 
            -
            "Datum","Naam / Omschrijving","Rekening","Tegenrekening","Code","Af Bij","Bedrag (EUR)","MutatieSoort","Mededelingen"
         | 
| 2 | 
            -
            "20101231","Omschrijving                    ","123456789","012345678","GT","Af","100,00","Internetbankieren","mededeling eèéêë"
         | 
| 3 | 
            -
            "20101230","Omschrijving                    ","123456789","012345678","GT","Af","200,00","Internetbankieren","mededeling        "
         | 
| @@ -1,57 +0,0 @@ | |
| 1 | 
            -
            require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
         | 
| 2 | 
            -
             | 
| 3 | 
            -
            describe TotalRecall::BankParser do
         | 
| 4 | 
            -
              context '#parse' do
         | 
| 5 | 
            -
                subject{ described_class.new(:strategy => TotalRecall::ParseStrategy::Abn.new) }
         | 
| 6 | 
            -
             | 
| 7 | 
            -
                it "should return an Array" do
         | 
| 8 | 
            -
                  subject.parse(fixture_contents('abn')).class.should == Array
         | 
| 9 | 
            -
                end
         | 
| 10 | 
            -
              end
         | 
| 11 | 
            -
            end
         | 
| 12 | 
            -
             | 
| 13 | 
            -
             | 
| 14 | 
            -
            shared_examples "a parser" do
         | 
| 15 | 
            -
              context '#parse_row' do
         | 
| 16 | 
            -
                it "should return a hash" do
         | 
| 17 | 
            -
                  CSV.parse(fixture_contents(@fixture), subject.options).each do |i|
         | 
| 18 | 
            -
                    parsed_row = subject.parse_row(i)
         | 
| 19 | 
            -
             | 
| 20 | 
            -
                    parsed_row.class.should == Hash
         | 
| 21 | 
            -
                  end
         | 
| 22 | 
            -
                end
         | 
| 23 | 
            -
             | 
| 24 | 
            -
                it "should return the correct keys and classes" do
         | 
| 25 | 
            -
                  expected_keys_and_classes = {
         | 
| 26 | 
            -
                    :amount => Float,
         | 
| 27 | 
            -
                    :currency => String,
         | 
| 28 | 
            -
                    :description => String,
         | 
| 29 | 
            -
                    :date => Date
         | 
| 30 | 
            -
                  }
         | 
| 31 | 
            -
             | 
| 32 | 
            -
                  CSV.parse(fixture_contents(@fixture), subject.options).each do |i|
         | 
| 33 | 
            -
                    parsed_row = subject.parse_row(i)
         | 
| 34 | 
            -
             | 
| 35 | 
            -
                    parsed_row.keys.should =~ expected_keys_and_classes.keys
         | 
| 36 | 
            -
                    expected_keys_and_classes.each do |key, klass|
         | 
| 37 | 
            -
                      parsed_row.send(:[], key).class.should == klass
         | 
| 38 | 
            -
                    end
         | 
| 39 | 
            -
                  end
         | 
| 40 | 
            -
                end
         | 
| 41 | 
            -
              end
         | 
| 42 | 
            -
            end
         | 
| 43 | 
            -
             | 
| 44 | 
            -
            describe TotalRecall::ParseStrategy::Abn do
         | 
| 45 | 
            -
              before{ @fixture = 'abn' }
         | 
| 46 | 
            -
              it_behaves_like "a parser"
         | 
| 47 | 
            -
            end
         | 
| 48 | 
            -
             | 
| 49 | 
            -
            describe TotalRecall::ParseStrategy::Ing do
         | 
| 50 | 
            -
              before{ @fixture = 'ing' }
         | 
| 51 | 
            -
              it_behaves_like "a parser"
         | 
| 52 | 
            -
            end
         | 
| 53 | 
            -
             | 
| 54 | 
            -
            describe TotalRecall::ParseStrategy::AbnCC do
         | 
| 55 | 
            -
              before{ @fixture = 'abncc' }
         | 
| 56 | 
            -
              it_behaves_like "a parser"
         | 
| 57 | 
            -
            end
         |