packlist 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,2 @@
1
+ @import "system-font";
2
+ @import "report";
@@ -0,0 +1,124 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Packlist - <%= packlist.name %></title>
5
+ <style type="text/css">
6
+ <%= partial '../stylesheets/system-font' %>
7
+ <%= partial '../stylesheets/report' %>
8
+ </style>
9
+ </head>
10
+ <body>
11
+ <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="display: none">
12
+ <%= partial "../images/worn.svg" %>
13
+ <%= partial "../images/consumed.svg" %>
14
+ </svg>
15
+
16
+ <header>
17
+ <h1><%= packlist.name %></h1>
18
+ <p><%= packlist.description %></p>
19
+ </header>
20
+
21
+ <section>
22
+ <div class="summary">
23
+ <table>
24
+ <thead>
25
+ <tr>
26
+ <th>Category</th>
27
+ <th>Weight</th>
28
+ </tr>
29
+ </thead>
30
+ <tfoot>
31
+ <tr>
32
+ <td>Base</td>
33
+ <td class="weight"><%= packlist.base_weight.lb %></td>
34
+ </tr>
35
+ <tr>
36
+ <td>Consumable</td>
37
+ <td class="weight"><%= packlist.consumable_weight.lb %></td>
38
+ </tr>
39
+ <tr>
40
+ <td>Worn</td>
41
+ <td class="weight"><%= packlist.worn_weight.lb %></td>
42
+ </tr>
43
+ <tr>
44
+ <td>Max Pack Weight</td>
45
+ <td class="weight"><%= packlist.total_pack_weight.lb %></td>
46
+ </tr>
47
+ <tr>
48
+ <td>Total</td>
49
+ <td class="weight"><%= packlist.total_weight.lb %></td>
50
+ </tr>
51
+ </tfoot>
52
+ <tbody>
53
+ <% for category in packlist %>
54
+ <tr>
55
+ <td><%= category.name %></td>
56
+ <td><%= category.total_weight.lb %></td>
57
+ </tr>
58
+ <% end %>
59
+ </tbody>
60
+ </table>
61
+ </div>
62
+
63
+ <div class="categories">
64
+ <% for category in packlist %>
65
+ <div class="category">
66
+ <h2><%= category.name %></h2>
67
+
68
+ <table>
69
+ <tfoot>
70
+ <tr>
71
+ <td colspan="2"></td>
72
+ <td class="subtotal" colspan="3">Subtotal</td>
73
+ <td class="numeric">
74
+ <%= category.total_weight.g %>
75
+ </td>
76
+ <td class="numeric">
77
+ <%= category.total_weight.lb %>
78
+ </td>
79
+ </tr>
80
+ </tfoot>
81
+ <tbody>
82
+ <% for item in category %>
83
+ <tr>
84
+ <td>
85
+ <input type="checkbox">
86
+ </td>
87
+ <td class="item-type">
88
+ <% if item.worn? %>
89
+ <svg class="icon worn">
90
+ <use xlink:href="#worn" />
91
+ </svg>
92
+ <% elsif item.consumable? %>
93
+ <svg class="icon consumable">
94
+ <use xlink:href="#consumed" />
95
+ </svg>
96
+ <% end %>
97
+ </td>
98
+ <td class="item-name">
99
+ <%= item.name %>
100
+ </td>
101
+ <td class="item-description">
102
+ <%= item.description %>
103
+ </td>
104
+ <td class="quantity">
105
+ <% if item.quantity != 1 %>
106
+ <%= item.quantity %>
107
+ <% end %>
108
+ </td>
109
+ <td class="item-weight">
110
+ <%= item.total_weight.g %>
111
+ </td>
112
+ <td class="item-weight">
113
+ <%= item.total_weight.oz %>
114
+ </td>
115
+ </tr>
116
+ <% end %>
117
+ </tbody>
118
+ </table>
119
+ </div>
120
+ <% end %>
121
+ </div>
122
+ </section>
123
+ </body>
124
+ </html>
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "packlist"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ require "pry"
11
+ Pry.start
12
+
13
+ # require "irb"
14
+ # IRB.start
data/bin/setup ADDED
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
6
+
7
+ # Do any other automated setup that you need to do here
Binary file
@@ -0,0 +1,68 @@
1
+ packlist do
2
+ name "Epic Backpacking Trip"
3
+ description "Seven nights in the Sierras, September 2016"
4
+
5
+ category "Shelter and sleeping" do
6
+ # Weight for this item is 280 grams (280.g)
7
+ item "Tarp", "2-person tarp", 280.g
8
+
9
+ # Item quantity can be specified. Here, the weight is per
10
+ # item, not the total weight of all items.
11
+ # Here, we specify 8 tent stakes, at 14 grams each.
12
+ item "Tent stakes", "Aluminum Y-stakes", 14.g, 8
13
+
14
+ # Other units of weight can be used as well.
15
+ # g - grams
16
+ # kg - kilograms
17
+ # oz - ounces
18
+ # lb - pounds
19
+ # Weight for this item is 24 ounces (24.oz)
20
+ item "Sleeping back", "Down 20 degree bag", 24.oz
21
+
22
+ # Leaving the heavy 4-season tent behind, so we comment it out
23
+ # item "Tent", "4-season, 3-person tent", 8.5.lb
24
+ end
25
+
26
+ category "Clothing" do
27
+ # On a backpacking trip, you're likely to wear some of your
28
+ # clothes (we hope!), while others, like rain gear and extra socks
29
+ # will be carried most of the time in your pack.
30
+ worn "T-shirt", "Wool shirt", 150.g
31
+ worn "Sock", "Wool socks", 80.g
32
+ worn "Shorts", "Running shorts", 90.g
33
+
34
+ # Some things are better left without a description. Just use an
35
+ # empty string ("").
36
+ worn "Underwear", "", 85.g
37
+
38
+ # The following items are carried in the pack, not worn.
39
+ item "Rain Jacket", "Keepin' high and dry!", 200.g
40
+ item "Insulated Jacket", "Down duffy jacket", 275.g
41
+ item "Warm hat", "Fleece beanie", 90.g
42
+ end
43
+
44
+ # Group your pack's items into multiple categories.
45
+ category "Cooking" do
46
+ item "Stove", "Canister stove", 90.g
47
+ item "Cooking pot", "900ml Titanium pot", 125.g
48
+
49
+ # Items like fuel are consumable. They are included in your
50
+ # maximum pack weight, but not in your base weight.
51
+ consumable "Fuel", "All season fuel blend", 110.g
52
+
53
+ # The canister the fuel comes in is not consumable; to accurately
54
+ # calculate your pack weight, you should list it separately.
55
+ item "Fuel canister", "4 ounce fuel canister, empty weight", 90.g
56
+ end
57
+
58
+ category "Food" do
59
+ # Food is also consumable, of course. You can use either the
60
+ # "food" or "consumable" item type; the result is the same.
61
+
62
+ # For each meal (breakfast, snacks, dinner), we specify the weight
63
+ # per day, with a quantity of 7, one for each day.
64
+ food "Breakfast", "Oatmeal", 150.g, 7
65
+ food "Snacks", "G.O.R.P", 300.g, 7
66
+ food "Dinner", "Dehydrated gruel", 180.g, 7
67
+ end
68
+ end
data/exe/packlist ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'packlist'
4
+
5
+ def output_filename(filename)
6
+ basename = File.basename(filename, ".*")
7
+ dirname = File.dirname(filename)
8
+ File.join(dirname, "#{basename}.html")
9
+ end
10
+
11
+ ARGV.each do |file|
12
+ pack = PackList::DSL.load(file)
13
+ report = PackList::Report.new pack
14
+
15
+ report.save_to(output_filename(file))
16
+ end
data/lib/packlist.rb ADDED
@@ -0,0 +1,4 @@
1
+ require 'packlist/version'
2
+ require 'packlist/model'
3
+ require 'packlist/dsl'
4
+ require 'packlist/report'
@@ -0,0 +1,81 @@
1
+ require 'packlist/model'
2
+
3
+ module PackList
4
+ module DSL
5
+ def packlist(&block)
6
+ builder = PackBuilder.new &block
7
+ builder.build
8
+ end
9
+ module_function :packlist
10
+
11
+ class PackBuilder
12
+ def initialize(&block)
13
+ @categories = []
14
+ if block_given?
15
+ instance_eval &block
16
+ end
17
+ end
18
+
19
+ def name(name)
20
+ @name = name
21
+ self
22
+ end
23
+
24
+ def description(description)
25
+ @description = description
26
+ self
27
+ end
28
+
29
+ def build
30
+ PackList.new @name, @description, @categories
31
+ end
32
+
33
+ def category(name, description=nil, &block)
34
+ cat_builder = CategoryBuilder.new name, description, &block
35
+ category = cat_builder.build
36
+ @categories << category
37
+ category
38
+ end
39
+ end
40
+
41
+
42
+ class CategoryBuilder
43
+ def initialize(name, description, &block)
44
+ @category = Category.new name, description
45
+ if block_given?
46
+ instance_eval(&block)
47
+ end
48
+ end
49
+
50
+ def item(name, description, weight, quantity=1)
51
+ add_item(name, description, weight, :item, quantity)
52
+ end
53
+
54
+ def worn(name, description, weight, quantity=1)
55
+ add_item(name, description, weight, :worn, quantity)
56
+ end
57
+
58
+ def consumable(name, description, weight=:oz, quantity=1)
59
+ add_item(name, description, weight, :consumable, quantity)
60
+ end
61
+
62
+ alias_method :food, :consumable
63
+
64
+ def build
65
+ @category
66
+ end
67
+
68
+ private
69
+
70
+ def add_item(name, description, weight, type, quantity)
71
+ item = Item.new(name, description, weight, type, quantity: quantity)
72
+ @category << item
73
+ end
74
+
75
+ end
76
+
77
+ def self.load(filename)
78
+ instance_eval(File.read(filename, encoding: "UTF-8"), filename)
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,178 @@
1
+ require 'forwardable'
2
+
3
+ module PackList
4
+ module WeightMixin
5
+ [:oz, :lb, :g, :kg].each do |units|
6
+ send :define_method, units do
7
+ return Weight.new self, units
8
+ end
9
+ end
10
+ end
11
+
12
+ class Weight
13
+ WEIGHT_CONVERSIONS = {lb: 453.592, oz: 453.592 / 16, kg: 1000, g: 1}
14
+ attr_reader :quantity, :units
15
+
16
+ def initialize(quantity, units)
17
+ raise ArgumentError, "Invalid units: #{units}" unless WEIGHT_CONVERSIONS.key?(units)
18
+ @quantity, @units = quantity, units
19
+ end
20
+
21
+ [:oz, :lb, :g, :kg].each do |units|
22
+ send :define_method, units do
23
+ to_units units
24
+ end
25
+ end
26
+
27
+ def +(w)
28
+ # avoid changing units when either weight is zero
29
+ case
30
+ when w.quantity == 0
31
+ self
32
+ when self.quantity == 0
33
+ w
34
+ when w.units == @units
35
+ Weight.new w.quantity + @quantity, @units
36
+ else
37
+ self + w.to_units(@units)
38
+ end
39
+ end
40
+
41
+ def *(x)
42
+ Weight.new x * @quantity, @units
43
+ end
44
+
45
+ ZERO = Weight.new(0, :g)
46
+
47
+ # private
48
+ def in_units(new_units)
49
+ quantity * WEIGHT_CONVERSIONS[units] / WEIGHT_CONVERSIONS[new_units]
50
+ end
51
+
52
+ def to_units(new_units)
53
+ Weight.new in_units(new_units), new_units
54
+ end
55
+
56
+ def to_s
57
+ "#{'%.1f' % quantity} #{units}"
58
+ end
59
+
60
+ end
61
+
62
+
63
+ class Item
64
+ attr_accessor :name, :description, :weight, :quantity
65
+
66
+ def initialize(name, description, weight, type=nil, quantity: 1)
67
+ @name, @description, @quantity, @type = name, description, quantity, type
68
+ @weight = weight.respond_to?(:units) ? weight : Weight.new(weight, :oz)
69
+ end
70
+
71
+ def total_weight
72
+ return @weight * @quantity
73
+ end
74
+
75
+ def worn?
76
+ return @type == :worn
77
+ end
78
+
79
+ def consumable?
80
+ return @type == :consumable
81
+ end
82
+
83
+ end
84
+
85
+ class Category
86
+ include Enumerable
87
+ extend Forwardable
88
+
89
+ attr_accessor :name, :description
90
+ attr_reader :items
91
+ def_delegators :@items, :each, :<<, :include?, :empty?
92
+
93
+ def initialize(name, description=nil)
94
+ @name, @description = name, description
95
+ @items = []
96
+ end
97
+
98
+ def total_weight
99
+ @items.collect {|item| item.total_weight}.reduce(Weight::ZERO, :+)
100
+ end
101
+
102
+ def total_pack_weight
103
+ base_weight + consumable_weight
104
+ end
105
+
106
+ def worn_weight
107
+ items = @items.select {|item| item.worn? }
108
+ items.collect {|item| item.total_weight}.reduce(Weight::ZERO, :+)
109
+ end
110
+
111
+ def consumable_weight
112
+ items = @items.select {|item| item.consumable? }
113
+ items.collect {|item| item.total_weight}.reduce(Weight::ZERO, :+)
114
+ end
115
+
116
+ def base_weight
117
+ items = @items.reject {|item| item.worn? || item.consumable? }
118
+ items.collect {|item| item.total_weight}.reduce(Weight::ZERO, :+)
119
+ end
120
+
121
+ def total_quantity
122
+ @items.collect {|item| item.quantity}.reduce(0, :+)
123
+ end
124
+
125
+ def item_count
126
+ @items.length
127
+ end
128
+
129
+ end
130
+
131
+ class PackList
132
+ include Enumerable
133
+ extend Forwardable
134
+
135
+ attr_accessor :name, :description
136
+ attr_reader :categories
137
+ def_delegators :@categories, :each, :<<, :include?, :empty?
138
+
139
+ def initialize(name, description=nil, categories=[])
140
+ @name, @description = name, description
141
+
142
+ @categories = categories
143
+ end
144
+
145
+ def total_weight
146
+ @categories.collect {|category| category.total_weight}.reduce(Weight::ZERO, :+)
147
+ end
148
+
149
+ def total_pack_weight
150
+ base_weight + consumable_weight
151
+ end
152
+
153
+ def worn_weight
154
+ @categories.collect {|category| category.worn_weight}.reduce(Weight::ZERO, :+)
155
+ end
156
+
157
+ def consumable_weight
158
+ @categories.collect {|category| category.consumable_weight}.reduce(Weight::ZERO, :+)
159
+ end
160
+
161
+ def base_weight
162
+ @categories.collect {|category| category.base_weight}.reduce(Weight::ZERO, :+)
163
+ end
164
+
165
+ def total_quantity
166
+ @categories.collect {|category| category.total_quantity}.reduce(0, :+)
167
+ end
168
+
169
+ def item_count
170
+ @categories.collect {|category| category.item_count}.reduce(0, :+)
171
+ end
172
+
173
+ end
174
+ end
175
+
176
+ class Numeric
177
+ include PackList::WeightMixin
178
+ end