ascii_invoicer 2.5.5

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.
@@ -0,0 +1,106 @@
1
+ require 'paint'
2
+
3
+ require File.join __dir__, 'tweaks.rb'
4
+
5
+
6
+ module ProjectFileReader
7
+ # reads @raw_data
8
+ # fills @data
9
+ # uses @defaults
10
+ # produces @errors
11
+ # has a @status
12
+
13
+ ##
14
+ # little read function
15
+ # returns data if read already
16
+ # or does a lookup
17
+ def read(key, data = @data)
18
+ return nil unless key.class == Symbol
19
+
20
+ # data has already been read
21
+ @logger.info Paint["KNOWN KEY: #{key}",:cyan] if data[key] if @settings.DEBUG
22
+ return data[key] if data[key]
23
+
24
+ @logger.info Paint["reading :#{key}", :green, :bold] if @settings.DEBUG
25
+
26
+ raw = @raw_data[key.to_s]
27
+ default = @defaults[key.to_s]
28
+
29
+ # if key is in raw_data walk it trough applying filters
30
+ if raw
31
+ @logger.info " FOUND RAW #{key}" if @settings.DEBUG
32
+ @logger.info " walking #{key}" if @settings.DEBUG
33
+ return data[key] = walk(raw, [key])
34
+ #fail "#{self.class} DOES NOT KNOW WHAT TO DO WITH #{raw.class}"
35
+ end
36
+
37
+ # or the default from the settings
38
+ unless default.nil?
39
+ @logger.info " FOUND DEFAULT #{key}" if @settings.DEBUG
40
+ return data[key] = walk(default, [key])
41
+ end
42
+
43
+ # otherwise fail
44
+ return data[key] = fail_at(key)
45
+ end
46
+
47
+ private
48
+ def walk(tree= @raw_data, path = [])
49
+ catch :filter_error do
50
+ if tree.class == Hash
51
+ new_tree = {}
52
+ tree.each{|k,v|
53
+ k = k.to_sym if k.class == String
54
+ k = walk(k, path+[k] )if k.class == Hash
55
+ new_tree[k] = walk(v, path+[k])
56
+ }
57
+ new_tree = apply_filter path, new_tree
58
+ return new_tree
59
+ elsif tree.class == Array
60
+ new_tree = []
61
+ tree.each_index{|i|
62
+ v = tree[i]
63
+ new_tree[i] = walk(v, path+[i])
64
+ }
65
+ new_tree = apply_filter path, new_tree
66
+ return new_tree
67
+ else
68
+ tree = apply_filter path, tree
69
+ return tree
70
+ end
71
+ end
72
+ end
73
+
74
+ def apply_generator(path, value)
75
+ apply_filter path, value, "generate_"
76
+ end
77
+
78
+ def apply_filter(path, value, prefix = "filter_")
79
+ begin
80
+ path = path.join('_') if path.class == Array
81
+ path = path.to_s if path.class == Symbol
82
+ parser = prefix+path
83
+ begin parser = method(parser)
84
+ rescue NameError
85
+ return value
86
+ else
87
+ @logger.info Paint[path, :yellow] if @settings.DEBUG
88
+ return parser.call(value)
89
+ end
90
+ end
91
+ rescue => error
92
+ fail_at path
93
+ puts Array.new($@).keep_if{|line| line.include? "filter_"}.map {|line| Paint[line, :red, :bold]}
94
+ puts Array.new($@).keep_if{|line| line.include? "generate_"}.map {|line| Paint[line, :blue, :bold]}
95
+ puts Paint[" #{error} (#{@project_path})", :yellow]
96
+ end
97
+
98
+ def fail_at(*criteria)
99
+ @data[:valid] = false
100
+ criteria.each {|c|
101
+ @errors.push c.to_sym unless @errors.include? c
102
+ }
103
+ return nil
104
+ end
105
+
106
+ end
@@ -0,0 +1,40 @@
1
+ # RFC 5322 Email Validation Regex in Ruby
2
+
3
+ $RFC5322 = /
4
+ (?<addr_spec> (?> \g<local_part> @ \g<domain> ) ){0}
5
+ (?<local_part> (?> \g<dot_atom> | \g<quoted_string> | \g<obs_local_part> ) ){0}
6
+ (?<domain> (?> \g<dot_atom> | \g<domain_literal> | \g<obs_domain> ) ){0}
7
+ (?<domain_literal> (?> \g<CFWS>? \[ (?: \g<FWS>? \g<dtext> )* \g<FWS>? \] \g<CFWS>? ) ){0}
8
+ (?<dtext> (?> [\x21-\x5a] | [\x5e-\x7e] | \g<obs_dtext> ) ){0}
9
+ (?<quoted_pair> (?> \\ (?: \g<VCHAR> | \g<WSP> ) | \g<obs_qp> ) ){0}
10
+ (?<dot_atom> (?> \g<CFWS>? \g<dot_atom_text> \g<CFWS>? ) ){0}
11
+ (?<dot_atom_text> (?> \g<atext> (?: \. \g<atext> )* ) ){0}
12
+ (?<atext> (?> [a-zA-Z0-9!\#\$%&'*\+\/\=\?\^_`{\|}~\-]+ ) ){0}
13
+ (?<atom> (?> \g<CFWS>? \g<atext> \g<CFWS>? ) ){0}
14
+ (?<word> (?> \g<atom> | \g<quoted_string> ) ){0}
15
+ (?<quoted_string> (?> \g<CFWS>? " (?: \g<FWS>? \g<qcontent> )* \g<FWS>? " \g<CFWS>? ) ){0}
16
+ (?<qcontent> (?> \g<qtext> | \g<quoted_pair> ) ){0}
17
+ (?<qtext> (?> \x21 | [\x23-\x5b] | [\x5d-\x7e] | \g<obs_qtext> ) ){0}
18
+
19
+ # comments and whitespace
20
+ (?<FWS> (?> (?: \g<WSP>* \r\n )? \g<WSP>+ | \g<obs_FWS> ) ){0}
21
+ (?<CFWS> (?> (?: \g<FWS>? \g<comment> )+ \g<FWS>? | \g<FWS> ) ){0}
22
+ (?<comment> (?> \( (?: \g<FWS>? \g<ccontent> )* \g<FWS>? \) ) ){0}
23
+ (?<ccontent> (?>\g<ctext> | \g<quoted_pair> | \g<comment> ) ){0}
24
+ (?<ctext> (?> [\x21-\x27] | [\x2a-\x5b] | [\x5d-\x7e] | \g<obs_ctext> ) ){0}
25
+
26
+ # obsolete tokens
27
+ (?<obs_domain> (?> \g<atom> (?: \. \g<atom> )* ) ){0}
28
+ (?<obs_local_part> (?> \g<word> (?: \. \g<word> )* ) ){0}
29
+ (?<obs_dtext> (?> \g<obs_NO_WS_CTL> | \g<quoted_pair> ) ){0}
30
+ (?<obs_qp> (?> \\ (?: \x00 | \g<obs_NO_WS_CTL> | \n | \r ) ) ){0}
31
+ (?<obs_FWS> (?> \g<WSP>+ (?: \r\n \g<WSP>+ )* ) ){0}
32
+ (?<obs_ctext> (?> \g<obs_NO_WS_CTL> ) ){0}
33
+ (?<obs_qtext> (?> \g<obs_NO_WS_CTL> ) ){0}
34
+ (?<obs_NO_WS_CTL> (?> [\x01-\x08] | \x0b | \x0c | [\x0e-\x1f] | \x7f ) ){0}
35
+
36
+ # character class definitions
37
+ (?<VCHAR> (?> [\x21-\x7E] ) ){0}
38
+ (?<WSP> [ \t] ){0}
39
+ \g<addr_spec>
40
+ /uix
@@ -0,0 +1,62 @@
1
+ require 'fileutils'
2
+
3
+ require 'hash-graft'
4
+ require 'hashr'
5
+ require 'yaml'
6
+
7
+ # all about settings
8
+ class SettingsManager
9
+ attr_reader :settings, :homedir_settings, :default_settings
10
+
11
+ #expacting :homedir_path, :default_path, #template_path
12
+ def initialize hash
13
+ @init_hash = Hashr.new hash
14
+
15
+ raise "SettingsManager: No default file given." unless @init_hash.default_path?
16
+ raise "SettingsManager: No homedir file given." unless @init_hash.homedir_path?
17
+
18
+ @init_hash.default_path = File.expand_path @init_hash.default_path
19
+ @init_hash.homedir_path = File.expand_path @init_hash.homedir_path
20
+ @init_hash.template_path = File.expand_path @init_hash.template_path if @init_hash.template_path?
21
+
22
+ if File.exists?(@init_hash.default_path)
23
+ @default_settings = load_file @init_hash.default_path
24
+ else
25
+ raise "SettingsManager: Default settings file does not exist (#{@init_hash.default_path})."
26
+ end
27
+
28
+ if not File.exists?(@init_hash.homedir_path)
29
+ if @init_hash.template_path?
30
+ raise "SettingsManager: Template file does not exist" unless File.exists?(@init_hash.template_path)
31
+ puts "#{@init_hash.homedir_path} does not exist, but #{@init_hash.homedir_path} does"
32
+ puts "-> copying over"
33
+ FileUtils.cp @init_hash.template_path, @init_hash.homedir_path
34
+ else
35
+ # using only default_settings
36
+ # suggested use: used a default_settings as template, perhaps comment out everything
37
+ end
38
+ else
39
+ @homedir_settings = load_file @init_hash.homedir_path
40
+ end
41
+
42
+ # putting it all together
43
+ @settings = Hashr.new @default_settings.graft @homedir_settings
44
+
45
+ # adding some meta stuff
46
+ @settings.settings_homedir_path = @init_hash.homedir_path
47
+ @settings.settings_default_path = @init_hash.default_path
48
+ @settings.settings_template_path = @init_hash.template_path
49
+ @settings.default_settings = @default_settings
50
+ @settings.homedir_settings = @homedir_settings
51
+ return @settings
52
+ end
53
+
54
+ def load_file path
55
+ begin
56
+ YAML::load File.open File.expand_path path
57
+ rescue SyntaxError => error
58
+ puts "ERROR parsing #{File.expand_path path}!"
59
+ puts error
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,105 @@
1
+ require 'logger'
2
+
3
+ module TexWriter
4
+
5
+ ##
6
+ # fills the template with mined DATA
7
+ def create_tex type, stdout = true
8
+ document_template = load_template :document
9
+ document_type = type
10
+
11
+ unless document_template
12
+ @logger.error "Template Error!"
13
+ exit
14
+ end
15
+
16
+ #check output path first
17
+ output_path = File.expand_path $SETTINGS.output_path
18
+ unless File.directory? output_path
19
+ @logger.error "your output_path is not a directory! (#{output_path})"
20
+ exit
21
+ end
22
+
23
+ #set the output filename
24
+ filename = export_filename type, "tex"
25
+
26
+ puts "% #{filename}"
27
+ # fill out ERB (twice), make sure everything's set
28
+ template = ERB.new(document_template).result(binding)
29
+ result = ERB.new(template).result(binding)
30
+
31
+ output_path = File.join @project_folder, filename
32
+
33
+ puts result if stdout
34
+ write_to_file result, output_path if !stdout
35
+ render_tex output_path, filename if !stdout
36
+ end
37
+
38
+ def write_to_file file_content, path
39
+ begin
40
+ File.open(path, ?w){ |file|
41
+ file_content.lines.each do |line|
42
+ file.write line
43
+ end
44
+ }
45
+ @logger.info "file written: #{path}"
46
+ rescue
47
+ @logger.error "Unable to write into #{path}"
48
+ end
49
+ end
50
+
51
+ def render_tex path, filename
52
+ @logger.info "Rendering #{path} with #{$SETTINGS.latex}"
53
+ silencer = $SETTINGS.verbose ? "" : "> /dev/null"
54
+
55
+ ## TODO output directory is not generic
56
+ system "#{$SETTINGS.latex} \"#{path}\" -output-directory . #{silencer}"
57
+
58
+ output_path = File.expand_path $SETTINGS.output_path
59
+ @logger.error "your output_path is not a directory! (#{output_path})" unless File.directory? output_path
60
+
61
+ pdf = filename.gsub('.tex','.pdf')
62
+ log = filename.gsub('.tex','.log')
63
+ aux = filename.gsub('.tex','.aux')
64
+ unless $SETTINGS.keep_log
65
+ FileUtils.rm log if File.exists? log
66
+ FileUtils.rm aux if File.exists? aux
67
+ else
68
+ unless File.expand_path output_path == FileUtils.pwd
69
+ FileUtils.mv log, output_path if File.exists? log
70
+ FileUtils.mv aux, output_path if File.exists? aux
71
+ end
72
+ end
73
+ target = File.join output_path, pdf
74
+
75
+ puts "moving #{pdf} to #{target}"
76
+ if not File.exists? pdf
77
+ @logger.error "#{pdf} does not exist, so it can not be moved to #{output_path}"
78
+ elsif File.expand_path(output_path)!= FileUtils.pwd
79
+ FileUtils.mv pdf, output_path, :force => true, :verbose => true
80
+ end
81
+
82
+
83
+ puts "Created #{path}"
84
+ end
85
+
86
+ # loads template files named in settings
87
+ def load_template(type)
88
+ return false unless $PLUMBER.check_dir :templates
89
+ #files = Dir.glob File.join @dirs[:templates] , ?*
90
+ files = Dir.glob File.join($PLUMBER.dirs[:templates], "*{tex.erb,tex}")
91
+ templates = {}
92
+ files.each{|file|
93
+ templates[File.basename(file.split(?.)[0]).to_sym] = file
94
+ }
95
+ path = templates[type]
96
+ if File.exists?(path)
97
+ return File.open(path).read
98
+ else
99
+ @logger.error "Template (#{path}) File not found!"
100
+ return false
101
+ end
102
+ end
103
+
104
+
105
+ end
@@ -0,0 +1,25 @@
1
+ $FB = "foobar"
2
+ class TrueClass
3
+ def print(symbol=?✓)
4
+ "\e[32m#{symbol}\e[0m"
5
+ end
6
+ end
7
+
8
+ class FalseClass
9
+ def print(symbol=?✗)
10
+ "\e[31m#{symbol}\e[0m"
11
+ end
12
+ end
13
+
14
+ class String
15
+ def words
16
+ self.split " "
17
+ end
18
+ end
19
+
20
+ class Date
21
+ def to_s
22
+ self.strftime "%d.%m.%Y"
23
+ end
24
+ end
25
+
@@ -0,0 +1,3 @@
1
+ module AsciiInvoicer
2
+ VERSION = '2.5.5'
3
+ end
data/repl/ascii ADDED
@@ -0,0 +1,2 @@
1
+ add archive calendar commit csv display edit help history invoice list new offer path pull push reopen settings status version whoami
2
+ show dir close ls
@@ -0,0 +1,76 @@
1
+ ---
2
+ manager_name: "The Unnamed Manager"
3
+ verbose: false
4
+ editor: "vim -O"
5
+ colors: false
6
+ list_sort: index
7
+
8
+ path: "~"
9
+ output_path: "."
10
+ dirs:
11
+ storage: caterings
12
+ working: working
13
+ archive: archive
14
+ templates: templates
15
+
16
+ ## CAREFUL HERE
17
+ project_file_extension: .yml
18
+ use_git: true
19
+ latex: pdflatex
20
+ log_file: ~/.ascii_log
21
+ calendar_file: invoicer.ics # will be put in current directory
22
+
23
+ defaults:
24
+ tax: 0.19
25
+ lang: de
26
+ canceled: false
27
+ includes:
28
+ logopath:
29
+ name:
30
+ strasse:
31
+ universitaet:
32
+ fakultaet:
33
+ zusatz:
34
+ retouradresse:
35
+ ort:
36
+ land:
37
+ telefon:
38
+ telefax:
39
+ telex:
40
+ http:
41
+ email:
42
+ bank:
43
+ blz:
44
+ iban:
45
+ bic:
46
+ konto:
47
+ steuernummer:
48
+
49
+ messages:
50
+ de:
51
+ offer:
52
+ - Angebot
53
+ - "hiermit möchten wir Ihnen für die gastronomische Betreuung Ihrer Veranstaltung am <%= @data[:event][:prettydate] %> folgendes Angebot unterbreiten:"
54
+ - ""
55
+ invoice:
56
+ - Rechnung
57
+ - "wir bedanken uns für Ihren Auftrag für das Catering am <%= @data[:event][:prettydate] %> und erlauben uns Ihnen folgende Rechnung zu stellen:"
58
+ - "Wir bitten um eine Begleichung des Betrags innerhalb von 14 Tagen nach Erhalt der Rechnung."
59
+ signature: Mit freundlichen Grüßen
60
+
61
+ currency_symbol: €
62
+
63
+ gender_matches:
64
+ herr: male
65
+ frau: female
66
+ professor: male
67
+ professorin: female
68
+
69
+ lang_addressing:
70
+ de:
71
+ male: Sehr geehrter
72
+ female: Sehr geehrte
73
+ en:
74
+ male: Dear
75
+ female: Dear
76
+ ...
@@ -0,0 +1,54 @@
1
+ ---
2
+ #manager_name: "The Unnamed Manager"
3
+ #verbose: false
4
+ #editor: "vim -O"
5
+ #colors: false
6
+ #list_sort: date # index, name
7
+
8
+ #path: "~" #/where/you/want/to/put/the/dirs
9
+ #output_path: "."
10
+ #dirs:
11
+ # storage: caterings
12
+ # working: working
13
+ # archive: archive
14
+ # templates: templates
15
+
16
+ #defaults:
17
+ # tax: 0.19
18
+ # lang: de
19
+ # canceled: false
20
+ # messages:
21
+ # de:
22
+ # invoice:
23
+ # - Rechnung
24
+ # - "wir bedanken uns für Ihren Auftrag für das Catering am <%= @data[:raw_date] %> und erlauben uns ihnen folgende Rechnung zu stellen:"
25
+ # - "Wir bitten um eine Begleichung des Betrags innerhalb von 14 Tagen nach Erhalt der Rechnung."
26
+ # offer:
27
+ # - Angebot
28
+ # - "hiermit möchten wir Ihnen für die gastronomische Betreuung Ihrer Veranstaltung am <%= @data[:raw_date] %> folgendes Angebot unterbreiten:"
29
+ # - ""
30
+ # signature: Mit freundlichen Grüßen
31
+ #
32
+ # includes:
33
+ # logopath:
34
+ # name:
35
+ # strasse:
36
+ # universitaet:
37
+ # fakultaet:
38
+ # zusatz:
39
+ # retouradresse:
40
+ # ort:
41
+ # land:
42
+ # telefon:
43
+ # telefax:
44
+ # telex:
45
+ # http:
46
+ # email:
47
+ # bank:
48
+ # blz:
49
+ # iban:
50
+ # bic:
51
+ # konto:
52
+ # steuernummer:
53
+ #
54
+ ...