beerdb-models 0.10.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/.gemtest +0 -0
- data/HISTORY.md +3 -0
- data/Manifest.txt +25 -0
- data/README.md +97 -0
- data/Rakefile +31 -0
- data/lib/beerdb/deleter.rb +27 -0
- data/lib/beerdb/models.rb +103 -0
- data/lib/beerdb/models/beer.rb +201 -0
- data/lib/beerdb/models/brand.rb +62 -0
- data/lib/beerdb/models/brewery.rb +256 -0
- data/lib/beerdb/models/forward.rb +46 -0
- data/lib/beerdb/models/tag.rb +13 -0
- data/lib/beerdb/models/world/city.rb +14 -0
- data/lib/beerdb/models/world/country.rb +14 -0
- data/lib/beerdb/models/world/region.rb +14 -0
- data/lib/beerdb/reader.rb +219 -0
- data/lib/beerdb/reader_file.rb +62 -0
- data/lib/beerdb/reader_zip.rb +112 -0
- data/lib/beerdb/schema.rb +195 -0
- data/lib/beerdb/serializers/beer.rb +45 -0
- data/lib/beerdb/serializers/brewery.rb +46 -0
- data/lib/beerdb/stats.rb +27 -0
- data/lib/beerdb/version.rb +21 -0
- data/test/helper.rb +63 -0
- data/test/test_fixture_matchers.rb +85 -0
- data/test/test_values.rb +240 -0
- metadata +118 -0
@@ -0,0 +1,62 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module BeerDb
|
4
|
+
|
5
|
+
## todo: "old" classic reader - rename to FileReader ?? why? why not?
|
6
|
+
|
7
|
+
class Reader < ReaderBase
|
8
|
+
|
9
|
+
def initialize( include_path, opts = {} )
|
10
|
+
@include_path = include_path
|
11
|
+
end
|
12
|
+
|
13
|
+
def create_fixture_reader( name )
|
14
|
+
path = "#{@include_path}/#{name}.txt"
|
15
|
+
|
16
|
+
logger.info "parsing data (setup) '#{name}' (#{path})..."
|
17
|
+
|
18
|
+
FixtureReader.from_file( path )
|
19
|
+
end
|
20
|
+
|
21
|
+
def create_beers_reader( name, more_attribs={} )
|
22
|
+
real_name = name_to_real_name( name )
|
23
|
+
|
24
|
+
path = "#{@include_path}/#{real_name}.txt"
|
25
|
+
|
26
|
+
logger.info "parsing data (beers) '#{name}' (#{path})..."
|
27
|
+
|
28
|
+
ValuesReader.from_file( path, more_attribs )
|
29
|
+
## ValuesReaderV2.new( name, @include_path, more_attribs )
|
30
|
+
end
|
31
|
+
|
32
|
+
def create_breweries_reader( name, more_attribs={} )
|
33
|
+
real_name = name_to_real_name( name )
|
34
|
+
|
35
|
+
path = "#{@include_path}/#{real_name}.txt"
|
36
|
+
|
37
|
+
logger.info "parsing data (breweries) '#{name}' (#{path})..."
|
38
|
+
|
39
|
+
ValuesReader.from_file( path, more_attribs )
|
40
|
+
## ValuesReaderV2.new( name, @include_path, more_attribs )
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def name_to_real_name( name )
|
46
|
+
# map name to real_name path
|
47
|
+
# name might include !/ for virtual path (gets cut off)
|
48
|
+
# e.g. at-austria!/w-wien/beers becomse w-wien/beers
|
49
|
+
pos = name.index( '!/')
|
50
|
+
if pos.nil?
|
51
|
+
name # not found; real path is the same as name
|
52
|
+
else
|
53
|
+
# cut off everything until !/ e.g.
|
54
|
+
# at-austria!/w-wien/beers becomes
|
55
|
+
# w-wien/beers
|
56
|
+
name[ (pos+2)..-1 ]
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
end # class Reader
|
61
|
+
end # module BeerDb
|
62
|
+
|
@@ -0,0 +1,112 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module BeerDb
|
4
|
+
|
5
|
+
|
6
|
+
class ZipReader < ReaderBase
|
7
|
+
|
8
|
+
def initialize( name, include_path, opts = {} )
|
9
|
+
|
10
|
+
## todo/fix: make include_path an opts (included in opts?) - why? why not??
|
11
|
+
|
12
|
+
path = "#{include_path}/#{name}.zip"
|
13
|
+
## todo: check if zip exists
|
14
|
+
|
15
|
+
@zip_file = Zip::File.open( path ) ## NOTE: do NOT create if file is missing; let it crash
|
16
|
+
|
17
|
+
### allow prefix (path) in name
|
18
|
+
### e.g. assume all files relative to setup manifest
|
19
|
+
## e.g. at-austria-master/setups/all.txt or
|
20
|
+
## be-belgium-master/setups/all.txt
|
21
|
+
## for
|
22
|
+
## setups/all.txt
|
23
|
+
###
|
24
|
+
## will get (re)set w/ fixture/setup reader
|
25
|
+
##
|
26
|
+
## todo/fix: change/rename to @relative_path ?? - why? why not?
|
27
|
+
@zip_prefix = ''
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
def close
|
32
|
+
## todo/check: add a close method - why? why not ???
|
33
|
+
@zip_file.close
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
def create_fixture_reader( name )
|
38
|
+
## e.g. pass in => setups/all or setups/test etc. e.g. w/o .txt extension
|
39
|
+
query = "**/#{name}.txt"
|
40
|
+
|
41
|
+
## note: returns an array of Zip::Entry
|
42
|
+
candidates = @zip_file.glob( query )
|
43
|
+
pp candidates
|
44
|
+
|
45
|
+
## use first candidates entry as match
|
46
|
+
## todo/fix: issue warning if more than one entries/matches!!
|
47
|
+
|
48
|
+
## get fullpath e.g. at-austria-master/setups/all.txt
|
49
|
+
path = candidates[0].name
|
50
|
+
logger.debug " zip entry path >>#{path}<<"
|
51
|
+
|
52
|
+
## cut-off at-austria-master/ NOTE: includes trailing slash (if present)
|
53
|
+
## logger.debug " path.size #{path.size} >>#{path}<<"
|
54
|
+
## logger.debug " name.size #{name.size+4} >>#{name}<<"
|
55
|
+
|
56
|
+
## note: add +4 for extension (.txt)
|
57
|
+
@zip_prefix = path[ 0...(path.size-(name.size+4)) ]
|
58
|
+
logger.debug " zip entry prefix >>#{@zip_prefix}<<"
|
59
|
+
|
60
|
+
logger.info "parsing data in zip '#{name}' (#{path})..."
|
61
|
+
|
62
|
+
FixtureReader.from_zip( @zip_file, path )
|
63
|
+
end
|
64
|
+
|
65
|
+
|
66
|
+
def create_beers_reader( name, more_attribs={} )
|
67
|
+
path = name_to_zip_entry_path( name )
|
68
|
+
|
69
|
+
logger.debug "parsing data (beers) in zip '#{name}' (#{path})..."
|
70
|
+
|
71
|
+
ValuesReader.from_zip( @zip_file, path, more_attribs )
|
72
|
+
end
|
73
|
+
|
74
|
+
def create_breweries_reader( name, more_attribs={} )
|
75
|
+
path = name_to_zip_entry_path( name )
|
76
|
+
|
77
|
+
logger.debug "parsing data (breweries) in zip '#{name}' (#{path})..."
|
78
|
+
|
79
|
+
ValuesReader.from_zip( @zip_file, path, more_attribs )
|
80
|
+
end
|
81
|
+
|
82
|
+
private
|
83
|
+
|
84
|
+
def path_to_real_path( path )
|
85
|
+
# map name to name_real_path
|
86
|
+
# name might include !/ for virtual path (gets cut off)
|
87
|
+
# e.g. at-austria!/w-wien/beers becomse w-wien/beers
|
88
|
+
pos = path.index( '!/')
|
89
|
+
if pos.nil?
|
90
|
+
path # not found; real path is the same as name
|
91
|
+
else
|
92
|
+
# cut off everything until !/ e.g.
|
93
|
+
# at-austria!/w-wien/beers becomes
|
94
|
+
# w-wien/beers
|
95
|
+
path[ (pos+2)..-1 ]
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def name_to_zip_entry_path( name )
|
100
|
+
path = "#{name}.txt"
|
101
|
+
|
102
|
+
real_path = path_to_real_path( path )
|
103
|
+
|
104
|
+
# NOTE: add possible zip entry prefix path
|
105
|
+
# (if present includes trailing slash e.g. /)
|
106
|
+
entry_path = "#{@zip_prefix}#{real_path}"
|
107
|
+
entry_path
|
108
|
+
end
|
109
|
+
|
110
|
+
|
111
|
+
end # class ZipReader
|
112
|
+
end # module BeerDb
|
@@ -0,0 +1,195 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module BeerDb
|
4
|
+
|
5
|
+
|
6
|
+
class CreateDb < ActiveRecord::Migration
|
7
|
+
|
8
|
+
def up
|
9
|
+
|
10
|
+
create_table :beers do |t|
|
11
|
+
t.string :key, :null => false # import/export key
|
12
|
+
t.string :title, :null => false
|
13
|
+
t.string :synonyms # comma separated list of synonyms
|
14
|
+
|
15
|
+
t.string :web # optional url link (e.g. )
|
16
|
+
t.integer :since # optional year (e.g. 1896)
|
17
|
+
|
18
|
+
# t.boolean :bottle, :null => false, :default => false # Flaschenbier
|
19
|
+
# t.boolean :draft, :null => false, :default => false # Fassbier
|
20
|
+
## todo: check seasonal is it proper english?
|
21
|
+
t.boolean :seasonal, :null => false, :default => false # all year or just eg. Festbier/Oktoberfest Special
|
22
|
+
t.boolean :limited, :null => false, :default => false # one year or season only
|
23
|
+
## todo: add microbrew/brewpub flag?
|
24
|
+
#### t.boolean :brewpub, :null => false, :default => false
|
25
|
+
|
26
|
+
## add t.boolean :lite flag ??
|
27
|
+
t.decimal :kcal # kcal/100ml e.g. 45.0 kcal/100ml
|
28
|
+
|
29
|
+
## check: why decimal and not float?
|
30
|
+
t.decimal :abv # Alcohol by volume (abbreviated as ABV, abv, or alc/vol) e.g. 4.9 %
|
31
|
+
t.decimal :og # malt extract (original gravity) in plato
|
32
|
+
t.integer :srm # color in srm
|
33
|
+
t.integer :ibu # bitterness in ibu
|
34
|
+
|
35
|
+
### fix/todo: add bitterness field
|
36
|
+
|
37
|
+
# renamed - old field names
|
38
|
+
## t.decimal :plato # stammwuerze / gravity in plato scale (e.g. °P) e.g. 12.6° - todo: use a different field name e.g. just p or gravity?
|
39
|
+
## t.integer :color # beer color in Standard Reference Method (SRM)
|
40
|
+
|
41
|
+
# see en.wikipedia.org/wiki/Plato_scale#Colour
|
42
|
+
|
43
|
+
# SRM/Lovibond | Example | Beer color | EBC
|
44
|
+
# ---------------------------------------------------------------
|
45
|
+
# 2 | Pale lager, Witbier, Pilsener, Berliner Weisse | #F8F753 | 4
|
46
|
+
# 3 | Maibock, Blonde Ale | #F6F513 | 6
|
47
|
+
# 4 | Weissbier | #ECE61A | 8
|
48
|
+
# 6 | American Pale Ale, India Pale Ale | #D5BC26 | 12
|
49
|
+
# 8 | Weissbier, Saison | #BF923B | 16
|
50
|
+
# 10 | English Bitter, ESB | #BF813A | 20
|
51
|
+
# 13 | Biere de Garde, Double IPA | #BC6733 | 26
|
52
|
+
# 17 | Dark lager, Vienna lager, Marzen, Amber Ale | #8D4C32 | 33
|
53
|
+
# 20 | Brown Ale, Bock, Dunkel, Dunkelweizen | #5D341A | 39
|
54
|
+
# 24 | Irish Dry Stout, Doppelbock, Porter | #261716 | 47
|
55
|
+
# 29 | Stout | #0F0B0A | 57
|
56
|
+
# 35 | Foreign Stout, Baltic Porter | #080707 | 69
|
57
|
+
# 40+ | Imperial Stout | #030403 | 79
|
58
|
+
|
59
|
+
t.references :brewery # optional (for now)
|
60
|
+
t.references :brand # optional (for now)
|
61
|
+
|
62
|
+
|
63
|
+
## todo: add categories e.g. (A/B/C, 1/2/3, main/major/minor ??)
|
64
|
+
# - A-grade /1st class/ tier1 / main beer brand/bestseller/flagship ?
|
65
|
+
# - B-grade /2nd class/ tier2 / regular, major, - todo: find better names?
|
66
|
+
# - C-grade /3nd class/ tier3/ / speciality, minor ?
|
67
|
+
|
68
|
+
# use stars in .txt e.g. # ***/**/*/- => 1/2/3/4
|
69
|
+
t.integer :grade, :null => false, :default => 4
|
70
|
+
|
71
|
+
t.string :txt # source ref
|
72
|
+
t.boolean :txt_auto, :null => false, :default => false # inline? got auto-added?
|
73
|
+
|
74
|
+
|
75
|
+
t.references :country, :null => false
|
76
|
+
t.references :region # optional
|
77
|
+
t.references :city # optional
|
78
|
+
|
79
|
+
t.timestamps
|
80
|
+
end
|
81
|
+
|
82
|
+
|
83
|
+
create_table :brands do |t| # beer families (sharing same name e.g. brand)
|
84
|
+
t.string :key, :null => false # import/export key
|
85
|
+
t.string :title, :null => false
|
86
|
+
t.string :synonyms # comma separated list of synonyms
|
87
|
+
t.string :web # optional web page (e.g. www.ottakringer.at)
|
88
|
+
t.string :wiki # optional wiki(pedia page)
|
89
|
+
t.integer :since
|
90
|
+
|
91
|
+
## scope of brand (global/intern'l/national/regional/local) ??
|
92
|
+
t.boolean :global, :null => false, :default => false
|
93
|
+
t.boolean :internl, :null => false, :default => false
|
94
|
+
t.boolean :national, :null => false, :default => false
|
95
|
+
t.boolean :regional, :null => false, :default => false
|
96
|
+
t.boolean :local, :null => false, :default => false
|
97
|
+
|
98
|
+
# t.integer :brand_grade # 1/2/3/4/5 (global/intern'l/national/regional/local)
|
99
|
+
|
100
|
+
# use stars in .txt e.g. # ***/**/*/- => 1/2/3/4
|
101
|
+
t.integer :grade, :null => false, :default => 4
|
102
|
+
# -- todo: add plus 1 for brewery w/ *** ??
|
103
|
+
|
104
|
+
t.string :txt # source ref
|
105
|
+
t.boolean :txt_auto, :null => false, :default => false # inline? got auto-added?
|
106
|
+
|
107
|
+
|
108
|
+
t.references :brewery # optional (for now)
|
109
|
+
|
110
|
+
t.references :country, :null => false
|
111
|
+
t.references :region # optional
|
112
|
+
t.references :city # optional
|
113
|
+
|
114
|
+
t.timestamps
|
115
|
+
end
|
116
|
+
|
117
|
+
create_table :breweries do |t|
|
118
|
+
t.string :key, :null => false # import/export key
|
119
|
+
t.string :title, :null => false
|
120
|
+
t.string :synonyms # comma separated list of synonyms
|
121
|
+
t.string :address
|
122
|
+
t.integer :since
|
123
|
+
### fix: rename back to founded or use opened/closed
|
124
|
+
## fix: add flag for ca./about boolean opened_guess / opened_est / opened_??
|
125
|
+
## ca. / about 1010 marker e.g t.boolean : opened_est (for estimate) or similar!!!
|
126
|
+
## renamed to founded to since
|
127
|
+
## t.integer :founded # year founded/established - todo/fix: rename to since?
|
128
|
+
t.integer :closed # optional; year brewery closed
|
129
|
+
|
130
|
+
## todo: add optional parent brewery (owned_by) ???
|
131
|
+
|
132
|
+
t.boolean :brewpub, :null => false, :default => false
|
133
|
+
t.boolean :prod_m, :null => false, :default => false # prod medium (mid-size/regional brewery)
|
134
|
+
# e.g. > 15_000 barrels (us)
|
135
|
+
t.boolean :prod_l, :null => false, :default => false # prod large
|
136
|
+
# e.g. > 500_000 hl (at), > 6_000_000 barrels (us)
|
137
|
+
|
138
|
+
t.integer :prod # (estimated) annual production/capacity in hl (1hl=100l) e.g. megabrewery 2_000_000, microbrewery 1_000 hl; brewbup 500 hl etc.
|
139
|
+
t.integer :prod_grade # 1/2/3/4/5/6/7/8/9/10/11
|
140
|
+
|
141
|
+
# grade - classified using annual production (capacity) in hl
|
142
|
+
# < 1_000 hl => 11
|
143
|
+
# < 3_000 hl => 10
|
144
|
+
# < 5_000 hl => 9
|
145
|
+
# < 10_000 hl => 8
|
146
|
+
# < 50_000 hl => 7
|
147
|
+
# < 100_000 hl => 6
|
148
|
+
# < 200_000 hl => 5
|
149
|
+
# < 500_000 hl => 4
|
150
|
+
# < 1_000_000 hl => 3
|
151
|
+
# < 2_000_000 hl => 2
|
152
|
+
# > 2_000_000 hl => 1
|
153
|
+
|
154
|
+
|
155
|
+
# use stars in .txt e.g. # ***/**/*/- => 1/2/3/4
|
156
|
+
t.integer :grade, :null => false, :default => 4
|
157
|
+
|
158
|
+
|
159
|
+
t.string :txt # source ref
|
160
|
+
t.boolean :txt_auto, :null => false, :default => false # inline? got auto-added?
|
161
|
+
|
162
|
+
t.string :web # optional web page (e.g. www.ottakringer.at)
|
163
|
+
t.string :wikipedia # optional wiki(pedia page)
|
164
|
+
|
165
|
+
t.boolean :indie # independent brewery (flag)
|
166
|
+
|
167
|
+
# for convenience (easy queries) use flags for top beer multinationals (-- later use just tags? more flexible)
|
168
|
+
t.boolean :abinbev # owned by AB InBev / Anheuser-Busch InBev (and Grupo Modelo)
|
169
|
+
t.boolean :sabmiller # owned by SAB Miller (in US cooperates w/ Molson Coors using MillerCoors venture)
|
170
|
+
t.boolean :heineken # owned by Heineken
|
171
|
+
t.boolean :carlsberg # owned by Carlsberg
|
172
|
+
t.boolean :molsoncoors # owned by Molson Coors
|
173
|
+
t.boolean :diageo # owned by Diageo (e.g. Guiness, Kilkenny,...)
|
174
|
+
|
175
|
+
|
176
|
+
# todo: add t.references :parent # for parent brewery
|
177
|
+
# (or better use has many parents w/ percentage of ownership; might not be 100%)
|
178
|
+
|
179
|
+
t.references :country, :null => false
|
180
|
+
t.references :region # optional
|
181
|
+
t.references :city # optional
|
182
|
+
|
183
|
+
t.timestamps
|
184
|
+
end
|
185
|
+
|
186
|
+
end # method up
|
187
|
+
|
188
|
+
def down
|
189
|
+
raise ActiveRecord::IrreversibleMigration
|
190
|
+
end
|
191
|
+
|
192
|
+
|
193
|
+
end # class CreateDb
|
194
|
+
|
195
|
+
end # module BeerDb
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module BeerDb::Model
|
4
|
+
|
5
|
+
class BeerSerializer
|
6
|
+
|
7
|
+
def initialize( beer )
|
8
|
+
@beer = beer
|
9
|
+
end
|
10
|
+
|
11
|
+
attr_reader :beer
|
12
|
+
|
13
|
+
def as_json
|
14
|
+
brewery = {}
|
15
|
+
if beer.brewery.present?
|
16
|
+
brewery = { key: beer.brewery.key,
|
17
|
+
title: beer.brewery.title }
|
18
|
+
end
|
19
|
+
|
20
|
+
tags = []
|
21
|
+
if beer.tags.present?
|
22
|
+
beer.tags.each { |tag| tags << tag.key }
|
23
|
+
end
|
24
|
+
|
25
|
+
country = {
|
26
|
+
key: beer.country.key,
|
27
|
+
title: beer.country.title
|
28
|
+
}
|
29
|
+
|
30
|
+
data = { key: beer.key,
|
31
|
+
title: beer.title,
|
32
|
+
synonyms: beer.synonyms,
|
33
|
+
abv: beer.abv,
|
34
|
+
srm: beer.srm,
|
35
|
+
og: beer.og,
|
36
|
+
tags: tags,
|
37
|
+
brewery: brewery,
|
38
|
+
country: country }
|
39
|
+
|
40
|
+
data.to_json
|
41
|
+
end
|
42
|
+
|
43
|
+
end # class BeerSerializer
|
44
|
+
|
45
|
+
end # module BeerDb::Model
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module BeerDb::Model
|
4
|
+
|
5
|
+
class BrewerySerializer
|
6
|
+
|
7
|
+
def initialize( brewery )
|
8
|
+
@brewery = brewery
|
9
|
+
end
|
10
|
+
|
11
|
+
attr_reader :brewery
|
12
|
+
|
13
|
+
def as_json
|
14
|
+
|
15
|
+
beers = []
|
16
|
+
brewery.beers.each do |b|
|
17
|
+
beers << { key: b.key, title: b.title }
|
18
|
+
end
|
19
|
+
|
20
|
+
tags = []
|
21
|
+
if brewery.tags.present?
|
22
|
+
brewery.tags.each { |tag| tags << tag.key }
|
23
|
+
end
|
24
|
+
|
25
|
+
country = {
|
26
|
+
key: brewery.country.key,
|
27
|
+
title: brewery.country.title
|
28
|
+
}
|
29
|
+
|
30
|
+
data = { key: brewery.key,
|
31
|
+
title: brewery.title,
|
32
|
+
synonyms: brewery.synonyms,
|
33
|
+
since: brewery.since,
|
34
|
+
address: brewery.address,
|
35
|
+
web: brewery.web,
|
36
|
+
prod: brewery.prod, # (estimated) annual production in hl e.g. 2_000 hl
|
37
|
+
tags: tags,
|
38
|
+
beers: beers,
|
39
|
+
country: country }
|
40
|
+
|
41
|
+
data.to_json
|
42
|
+
end
|
43
|
+
|
44
|
+
end # class BrewerySerializer
|
45
|
+
|
46
|
+
end # module BeerDb::Model
|