winedb 0.0.1 → 0.1.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.
- data/.gemtest +0 -0
- data/Manifest.txt +11 -0
- data/lib/winedb.rb +45 -0
- data/lib/winedb/models/city.rb +11 -0
- data/lib/winedb/models/country.rb +13 -0
- data/lib/winedb/models/forward.rb +39 -0
- data/lib/winedb/models/region.rb +10 -0
- data/lib/winedb/models/tag.rb +12 -0
- data/lib/winedb/models/wine.rb +123 -0
- data/lib/winedb/models/winery.rb +172 -0
- data/lib/winedb/reader.rb +156 -0
- data/lib/winedb/schema.rb +75 -0
- data/lib/winedb/version.rb +1 -1
- data/test/helper.rb +76 -0
- data/test/test_models.rb +70 -0
- metadata +25 -12
data/.gemtest
ADDED
File without changes
|
data/Manifest.txt
CHANGED
@@ -3,4 +3,15 @@ Manifest.txt
|
|
3
3
|
README.md
|
4
4
|
Rakefile
|
5
5
|
lib/winedb.rb
|
6
|
+
lib/winedb/models/city.rb
|
7
|
+
lib/winedb/models/country.rb
|
8
|
+
lib/winedb/models/forward.rb
|
9
|
+
lib/winedb/models/region.rb
|
10
|
+
lib/winedb/models/tag.rb
|
11
|
+
lib/winedb/models/wine.rb
|
12
|
+
lib/winedb/models/winery.rb
|
13
|
+
lib/winedb/reader.rb
|
14
|
+
lib/winedb/schema.rb
|
6
15
|
lib/winedb/version.rb
|
16
|
+
test/helper.rb
|
17
|
+
test/test_models.rb
|
data/lib/winedb.rb
CHANGED
@@ -1,7 +1,28 @@
|
|
1
1
|
|
2
2
|
|
3
|
+
# 3rd party gems / libs
|
4
|
+
|
5
|
+
require 'active_record' ## todo: add sqlite3? etc.
|
6
|
+
|
7
|
+
require 'logutils'
|
8
|
+
require 'textutils'
|
9
|
+
require 'worlddb'
|
10
|
+
|
11
|
+
|
12
|
+
### our own code
|
13
|
+
|
3
14
|
require 'winedb/version' # let it always go first
|
15
|
+
require 'winedb/schema'
|
16
|
+
|
17
|
+
require 'winedb/models/forward'
|
18
|
+
require 'winedb/models/city'
|
19
|
+
require 'winedb/models/country'
|
20
|
+
require 'winedb/models/region'
|
21
|
+
require 'winedb/models/tag'
|
22
|
+
require 'winedb/models/wine'
|
23
|
+
require 'winedb/models/winery'
|
4
24
|
|
25
|
+
require 'winedb/reader'
|
5
26
|
|
6
27
|
module WineDb
|
7
28
|
|
@@ -13,6 +34,30 @@ module WineDb
|
|
13
34
|
"#{File.expand_path( File.dirname(File.dirname(__FILE__)) )}"
|
14
35
|
end
|
15
36
|
|
37
|
+
def self.create
|
38
|
+
CreateDb.new.up
|
39
|
+
|
40
|
+
WineDb::Model::Prop.create!( key: 'db.schema.wine.version', value: VERSION )
|
41
|
+
end
|
42
|
+
|
43
|
+
|
44
|
+
def self.read( ary, include_path )
|
45
|
+
reader = Reader.new( include_path )
|
46
|
+
ary.each do |name|
|
47
|
+
reader.load( name )
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.read_setup( setup, include_path, opts={} )
|
52
|
+
reader = Reader.new( include_path, opts )
|
53
|
+
reader.load_setup( setup )
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.read_all( include_path, opts={} ) # load all builtins (using plain text reader); helper for convenience
|
57
|
+
read_setup( 'setups/all', include_path, opts )
|
58
|
+
end # method read_all
|
59
|
+
|
60
|
+
|
16
61
|
end # module WineDb
|
17
62
|
|
18
63
|
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module WorldDb
|
2
|
+
module Model
|
3
|
+
|
4
|
+
class City
|
5
|
+
has_many :wines, class_name: 'WineDb::Model::Wine', foreign_key: 'city_id'
|
6
|
+
has_many :wineries, class_name: 'WineDb::Model::Winery', foreign_key: 'city_id'
|
7
|
+
end # class Country
|
8
|
+
|
9
|
+
|
10
|
+
end # module Model
|
11
|
+
end # module WorldDb
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module WorldDb
|
2
|
+
module Model
|
3
|
+
|
4
|
+
|
5
|
+
class Country
|
6
|
+
has_many :wines, class_name: 'WineDb::Model::Wine', foreign_key: 'country_id'
|
7
|
+
has_many :wineries, class_name: 'WineDb::Model::Winery', foreign_key: 'country_id'
|
8
|
+
end # class Country
|
9
|
+
|
10
|
+
|
11
|
+
end # module Model
|
12
|
+
end # module WorldDb
|
13
|
+
|
@@ -0,0 +1,39 @@
|
|
1
|
+
### forward references
|
2
|
+
## require first to resolve circular references
|
3
|
+
|
4
|
+
|
5
|
+
module WineDb
|
6
|
+
module Model
|
7
|
+
|
8
|
+
## todo: why? why not use include WorldDb::Models here???
|
9
|
+
|
10
|
+
Continent = WorldDb::Model::Continent
|
11
|
+
Country = WorldDb::Model::Country
|
12
|
+
Region = WorldDb::Model::Region
|
13
|
+
City = WorldDb::Model::City
|
14
|
+
|
15
|
+
Tag = WorldDb::Model::Tag
|
16
|
+
Tagging = WorldDb::Model::Tagging
|
17
|
+
|
18
|
+
Prop = WorldDb::Model::Prop
|
19
|
+
|
20
|
+
class Wine < ActiveRecord::Base ; end
|
21
|
+
class Winery < ActiveRecord::Base ; end
|
22
|
+
|
23
|
+
end # module Model
|
24
|
+
|
25
|
+
## note: for convenciene (and compatibility) add alias Models for Model namespace
|
26
|
+
## e.g lets you use include WineDb::Models
|
27
|
+
Models = Model
|
28
|
+
|
29
|
+
end # module WineDb
|
30
|
+
|
31
|
+
|
32
|
+
module WorldDb
|
33
|
+
module Model
|
34
|
+
|
35
|
+
Wine = WineDb::Model::Wine
|
36
|
+
Winery = WineDb::Model::Winery
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
module WorldDb
|
2
|
+
module Model
|
3
|
+
|
4
|
+
class Region
|
5
|
+
has_many :wines, class_name: 'WineDb::Model::Wine', foreign_key: 'region_id'
|
6
|
+
has_many :wineries, class_name: 'WineDb::Model::Winery', foreign_key: 'region_id'
|
7
|
+
end # class Region
|
8
|
+
|
9
|
+
end # module Model
|
10
|
+
end # module WorldDb
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module WorldDb
|
2
|
+
module Model
|
3
|
+
|
4
|
+
|
5
|
+
class Tag
|
6
|
+
has_many :wines, :through => :taggings, :source => :taggable, source_type: 'WineDb::Model::Wine', class_name: 'WineDb::Model::Wine'
|
7
|
+
has_many :wineries, :through => :taggings, :source => :taggable, source_type: 'WineDb::Model::Winery', class_name: 'WineDb::Model::Winery'
|
8
|
+
end # class Country
|
9
|
+
|
10
|
+
|
11
|
+
end # module Model
|
12
|
+
end # module WorldDb
|
@@ -0,0 +1,123 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module WineDb
|
4
|
+
module Model
|
5
|
+
|
6
|
+
class Wine < ActiveRecord::Base
|
7
|
+
|
8
|
+
extend TextUtils::TagHelper # will add self.find_tags, self.find_tags_in_attribs!, etc.
|
9
|
+
|
10
|
+
# NB: use extend - is_<type>? become class methods e.g. self.is_<type>? for use in
|
11
|
+
# self.create_or_update_from_values
|
12
|
+
extend TextUtils::ValueHelper # e.g. self.is_year?, self.is_region?, self.is_address?, is_taglist? etc.
|
13
|
+
|
14
|
+
belongs_to :country, class_name: 'WorldDb::Model::Country', foreign_key: 'country_id'
|
15
|
+
belongs_to :region, class_name: 'WorldDb::Model::Region', foreign_key: 'region_id'
|
16
|
+
belongs_to :city, class_name: 'WorldDb::Model::City', foreign_key: 'city_id'
|
17
|
+
|
18
|
+
belongs_to :winery, :class_name => 'WineDb::Model::Winery', foreign_key: 'winery_id'
|
19
|
+
|
20
|
+
has_many :taggings, :as => :taggable, class_name: 'WorldDb::Model::Tagging'
|
21
|
+
has_many :tags, :through => :taggings, class_name: 'WorldDb::Model::Tag'
|
22
|
+
|
23
|
+
validates :key, :format => { :with => /^[a-z][a-z0-9]+$/, :message => 'expected two or more lowercase letters a-z or 0-9 digits' }
|
24
|
+
|
25
|
+
|
26
|
+
|
27
|
+
def self.create_or_update_from_values( values, more_attribs={} )
|
28
|
+
|
29
|
+
attribs, more_values = find_key_n_title( values )
|
30
|
+
attribs = attribs.merge( more_attribs )
|
31
|
+
|
32
|
+
# check for optional values
|
33
|
+
Wine.create_or_update_from_attribs( attribs, more_values )
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
def self.create_or_update_from_attribs( attribs, values )
|
38
|
+
|
39
|
+
# fix: add/configure logger for ActiveRecord!!!
|
40
|
+
logger = LogKernel::Logger.root
|
41
|
+
|
42
|
+
value_tag_keys = []
|
43
|
+
|
44
|
+
### check for "default" tags - that is, if present attribs[:tags] remove from hash
|
45
|
+
value_tag_keys += find_tags_in_attribs!( attribs )
|
46
|
+
|
47
|
+
## check for optional values
|
48
|
+
values.each_with_index do |value,index|
|
49
|
+
if match_country(value) do |country|
|
50
|
+
attribs[ :country_id ] = country.id
|
51
|
+
end
|
52
|
+
elsif match_region_for_country(value, attribs[:country_id]) do |region|
|
53
|
+
attribs[ :region_id ] = region.id
|
54
|
+
end
|
55
|
+
elsif match_city(value) do |city|
|
56
|
+
if city.present?
|
57
|
+
attribs[ :city_id ] = city.id
|
58
|
+
else
|
59
|
+
## todo/fix: add strict mode flag - fail w/ exit 1 in strict mode
|
60
|
+
logger.warn "city with key #{value[5..-1]} missing for beer #{attribs[:key]}"
|
61
|
+
end
|
62
|
+
end
|
63
|
+
elsif match_year( value ) do |num| # founded/established year e.g. 1776
|
64
|
+
attribs[ :since ] = num
|
65
|
+
end
|
66
|
+
elsif match_website( value ) do |website| # check for url/internet address e.g. www.ottakringer.at
|
67
|
+
attribs[ :web ] = website
|
68
|
+
end
|
69
|
+
elsif match_abv( value ) do |num| # abv (alcohol by volume)
|
70
|
+
# nb: also allows leading < e.g. <0.5%
|
71
|
+
attribs[ :abv ] = num
|
72
|
+
end
|
73
|
+
elsif (values.size==(index+1)) && is_taglist?( value ) # tags must be last entry
|
74
|
+
logger.debug " found tags: >>#{value}<<"
|
75
|
+
value_tag_keys += find_tags( value )
|
76
|
+
else
|
77
|
+
# issue warning: unknown type for value
|
78
|
+
logger.warn "unknown type for value >#{value}< - key #{attribs[:key]}"
|
79
|
+
end
|
80
|
+
end # each value
|
81
|
+
|
82
|
+
# rec = Wine.find_by_key_and_country_id( attribs[ :key ], attribs[ :country_id] )
|
83
|
+
rec = Wine.find_by_key( attribs[ :key ] )
|
84
|
+
|
85
|
+
if rec.present?
|
86
|
+
logger.debug "update Wine #{rec.id}-#{rec.key}:"
|
87
|
+
else
|
88
|
+
logger.debug "create Wine:"
|
89
|
+
rec = Wine.new
|
90
|
+
end
|
91
|
+
|
92
|
+
logger.debug attribs.to_json
|
93
|
+
|
94
|
+
rec.update_attributes!( attribs )
|
95
|
+
|
96
|
+
##################
|
97
|
+
# add taggings
|
98
|
+
|
99
|
+
if value_tag_keys.size > 0
|
100
|
+
|
101
|
+
value_tag_keys.uniq! # remove duplicates
|
102
|
+
logger.debug " adding #{value_tag_keys.size} taggings: >>#{value_tag_keys.join('|')}<<..."
|
103
|
+
|
104
|
+
### fix/todo: check tag_ids and only update diff (add/remove ids)
|
105
|
+
|
106
|
+
value_tag_keys.each do |key|
|
107
|
+
tag = Tag.find_by_key( key )
|
108
|
+
if tag.nil? # create tag if it doesn't exit
|
109
|
+
logger.debug " creating tag >#{key}<"
|
110
|
+
tag = Tag.create!( key: key )
|
111
|
+
end
|
112
|
+
rec.tags << tag
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
rec # NB: return created or updated obj
|
117
|
+
|
118
|
+
end # method create_or_update_from_values
|
119
|
+
|
120
|
+
end # class Wine
|
121
|
+
|
122
|
+
end # module Model
|
123
|
+
end # module WineDb
|
@@ -0,0 +1,172 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module WineDb
|
4
|
+
module Model
|
5
|
+
|
6
|
+
class Winery < ActiveRecord::Base
|
7
|
+
|
8
|
+
extend TextUtils::TagHelper # will add self.find_tags, self.find_tags_in_attribs!, etc.
|
9
|
+
|
10
|
+
# NB: use extend - is_<type>? become class methods e.g. self.is_<type>? for use in
|
11
|
+
# self.create_or_update_from_values
|
12
|
+
extend TextUtils::ValueHelper # e.g. self.is_year?, self.is_region?, is_address?, is_taglist? etc.
|
13
|
+
extend TextUtils::AddressHelper # e.g self.normalize_addr, self.find_city_in_addr, etc.
|
14
|
+
|
15
|
+
self.table_name = 'wineries'
|
16
|
+
|
17
|
+
belongs_to :country, class_name: 'WorldDb::Model::Country', foreign_key: 'country_id'
|
18
|
+
belongs_to :region, class_name: 'WorldDb::Model::Region', foreign_key: 'region_id'
|
19
|
+
belongs_to :city, class_name: 'WorldDb::Model::City', foreign_key: 'city_id'
|
20
|
+
|
21
|
+
has_many :wines, class_name: 'WineDb::Model::Wine', foreign_key: 'wine_id'
|
22
|
+
|
23
|
+
has_many :taggings, :as => :taggable, class_name: 'WorldDb::Model::Tagging'
|
24
|
+
has_many :tags, :through => :taggings, class_name: 'WorldDb::Model::Tag'
|
25
|
+
|
26
|
+
validates :key, :format => { :with => /^[a-z][a-z0-9]+$/, :message => 'expected two or more lowercase letters a-z or 0-9 digits' }
|
27
|
+
|
28
|
+
|
29
|
+
def self.create_or_update_from_values( values, more_attribs={} )
|
30
|
+
attribs, more_values = find_key_n_title( values )
|
31
|
+
attribs = attribs.merge( more_attribs )
|
32
|
+
|
33
|
+
# check for optional values
|
34
|
+
Winery.create_or_update_from_attribs( attribs, more_values )
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
def self.create_or_update_from_attribs( new_attributes, values )
|
39
|
+
|
40
|
+
## fix: add/configure logger for ActiveRecord!!!
|
41
|
+
logger = LogKernel::Logger.root
|
42
|
+
|
43
|
+
value_tag_keys = []
|
44
|
+
|
45
|
+
## check for grades (e.g. ***/**/*) in titles (will add new_attributes[:grade] to hash)
|
46
|
+
## if grade missing; set default to 4; lets us update overwrite 1,2,3 values on update
|
47
|
+
new_attributes[ :grade ] ||= 4
|
48
|
+
|
49
|
+
### check for "default" tags - that is, if present new_attributes[:tags] remove from hash
|
50
|
+
value_tag_keys += find_tags_in_attribs!( new_attributes )
|
51
|
+
|
52
|
+
## check for optional values
|
53
|
+
values.each_with_index do |value,index|
|
54
|
+
if match_country(value) do |country|
|
55
|
+
new_attributes[ :country_id ] = country.id
|
56
|
+
end
|
57
|
+
elsif match_region_for_country(value,new_attributes[:country_id]) do |region|
|
58
|
+
new_attributes[ :region_id ] = region.id
|
59
|
+
end
|
60
|
+
elsif match_city(value) do |city|
|
61
|
+
if city.present?
|
62
|
+
new_attributes[ :city_id ] = city.id
|
63
|
+
else
|
64
|
+
## todo/fix: add strict mode flag - fail w/ exit 1 in strict mode
|
65
|
+
logger.warn "city with key #{value[5..-1]} missing - for winery #{new_attributes[:key]}"
|
66
|
+
end
|
67
|
+
|
68
|
+
## for easy queries: cache region_id (from city)
|
69
|
+
# - check if city w/ region if yes, use it for winery too
|
70
|
+
if city.present? && city.region.present?
|
71
|
+
new_attributes[ :region_id ] = city.region.id
|
72
|
+
end
|
73
|
+
end
|
74
|
+
elsif match_year( value ) do |num| # founded/established year e.g. 1776
|
75
|
+
new_attributes[ :since ] = num
|
76
|
+
end
|
77
|
+
elsif match_website( value ) do |website| # check for url/internet address e.g. www.ottakringer.at
|
78
|
+
# fix: support more url format (e.g. w/o www. - look for .com .country code etc.)
|
79
|
+
new_attributes[ :web ] = website
|
80
|
+
end
|
81
|
+
elsif is_address?( value ) # if value includes // assume address e.g. 3970 Weitra // Sparkasseplatz 160
|
82
|
+
new_attributes[ :address ] = normalize_addr( value )
|
83
|
+
elsif (values.size==(index+1)) && is_taglist?( value ) # tags must be last entry
|
84
|
+
logger.debug " found tags: >>#{value}<<"
|
85
|
+
value_tag_keys += find_tags( value )
|
86
|
+
else
|
87
|
+
# issue warning: unknown type for value
|
88
|
+
logger.warn "unknown type for value >#{value}< - key #{new_attributes[:key]}"
|
89
|
+
end
|
90
|
+
end # each value
|
91
|
+
|
92
|
+
## todo: check better style using self.find_by_key?? why? why not?
|
93
|
+
rec = Winery.find_by_key( new_attributes[ :key ] )
|
94
|
+
|
95
|
+
if rec.present?
|
96
|
+
logger.debug "update Winery #{rec.id}-#{rec.key}:"
|
97
|
+
else
|
98
|
+
logger.debug "create Winery:"
|
99
|
+
rec = Winery.new
|
100
|
+
end
|
101
|
+
|
102
|
+
logger.debug new_attributes.to_json
|
103
|
+
|
104
|
+
rec.update_attributes!( new_attributes )
|
105
|
+
|
106
|
+
|
107
|
+
##############################
|
108
|
+
# auto-add city if not present and country n region present
|
109
|
+
|
110
|
+
if new_attributes[:city_id].blank? &&
|
111
|
+
new_attributes[:country_id].present? &&
|
112
|
+
new_attributes[:region_id].present?
|
113
|
+
|
114
|
+
country_key = rec.country.key
|
115
|
+
|
116
|
+
if country_key == 'at' || country_key == 'de'
|
117
|
+
|
118
|
+
## todo: how to handle nil/empty address lines?
|
119
|
+
|
120
|
+
city_title = find_city_in_addr( new_attributes[:address], country_key )
|
121
|
+
|
122
|
+
if city_title.present?
|
123
|
+
|
124
|
+
city_values = [city_title]
|
125
|
+
city_attributes = {
|
126
|
+
country_id: rec.country_id,
|
127
|
+
region_id: rec.region_id
|
128
|
+
}
|
129
|
+
# todo: add convenience helper create_or_update_from_title
|
130
|
+
city = City.create_or_update_from_values( city_values, city_attributes )
|
131
|
+
|
132
|
+
### fix/todo: set new autoadd flag too?
|
133
|
+
## e.g. check if updated? e.g. timestamp created <> updated otherwise assume created?
|
134
|
+
|
135
|
+
## now at last add city_id to winery!
|
136
|
+
rec.city_id = city.id
|
137
|
+
rec.save!
|
138
|
+
else
|
139
|
+
logger.warn "auto-add city for #{new_attributes[:key]} (#{country_key}) >>#{new_attributes[:address]}<< failed; no city title found"
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
##################
|
145
|
+
## add taggings
|
146
|
+
|
147
|
+
if value_tag_keys.size > 0
|
148
|
+
|
149
|
+
value_tag_keys.uniq! # remove duplicates
|
150
|
+
logger.debug " adding #{value_tag_keys.size} taggings: >>#{value_tag_keys.join('|')}<<..."
|
151
|
+
|
152
|
+
### fix/todo: check tag_ids and only update diff (add/remove ids)
|
153
|
+
|
154
|
+
value_tag_keys.each do |key|
|
155
|
+
tag = Tag.find_by_key( key )
|
156
|
+
if tag.nil? # create tag if it doesn't exit
|
157
|
+
logger.debug " creating tag >#{key}<"
|
158
|
+
tag = Tag.create!( key: key )
|
159
|
+
end
|
160
|
+
rec.tags << tag
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
rec # NB: return created or updated obj
|
165
|
+
|
166
|
+
end # method create_or_update_from_values
|
167
|
+
|
168
|
+
|
169
|
+
end # class Winery
|
170
|
+
|
171
|
+
end # module Model
|
172
|
+
end # module WineDb
|
@@ -0,0 +1,156 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module WineDb
|
4
|
+
|
5
|
+
|
6
|
+
module Matcher
|
7
|
+
|
8
|
+
def match_wines_for_country( name, &blk )
|
9
|
+
match_xxx_for_country( name, 'wines', &blk )
|
10
|
+
end
|
11
|
+
|
12
|
+
# def match_wines_for_country_n_region( name, &blk )
|
13
|
+
# match_xxx_for_country_n_region( name, 'wines', &blk )
|
14
|
+
# end
|
15
|
+
|
16
|
+
def match_wineries_for_country( name, &blk )
|
17
|
+
match_xxx_for_country( name, 'wineries', &blk )
|
18
|
+
end
|
19
|
+
|
20
|
+
# def match_wineries_for_country_n_region( name, &blk )
|
21
|
+
# match_xxx_for_country_n_region( name, 'wineries', &blk )
|
22
|
+
# end
|
23
|
+
|
24
|
+
end # module Matcher
|
25
|
+
|
26
|
+
|
27
|
+
class Reader
|
28
|
+
|
29
|
+
include LogUtils::Logging
|
30
|
+
|
31
|
+
include WineDb::Models
|
32
|
+
|
33
|
+
include WorldDb::Matcher ## fix: move to WineDb::Matcher module ??? - cleaner?? why? why not?
|
34
|
+
include WineDb::Matcher # lets us use match_teams_for_country etc.
|
35
|
+
|
36
|
+
attr_reader :include_path
|
37
|
+
|
38
|
+
|
39
|
+
def initialize( include_path, opts = {} )
|
40
|
+
@include_path = include_path
|
41
|
+
end
|
42
|
+
|
43
|
+
|
44
|
+
def load_setup( name )
|
45
|
+
path = "#{include_path}/#{name}.yml"
|
46
|
+
|
47
|
+
logger.info "parsing data '#{name}' (#{path})..."
|
48
|
+
|
49
|
+
reader = FixtureReader.new( path )
|
50
|
+
|
51
|
+
reader.each do |fixture_name|
|
52
|
+
load( fixture_name )
|
53
|
+
end
|
54
|
+
end # method load_setup
|
55
|
+
|
56
|
+
|
57
|
+
def load( name )
|
58
|
+
|
59
|
+
if match_wines_for_country( name ) do |country_key|
|
60
|
+
load_wines_for_country( country_key, name )
|
61
|
+
end
|
62
|
+
elsif match_wineries_for_country( name ) do |country_key|
|
63
|
+
load_wineries_for_country( country_key, name )
|
64
|
+
end
|
65
|
+
else
|
66
|
+
logger.error "unknown wine.db fixture type >#{name}<"
|
67
|
+
# todo/fix: exit w/ error
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
|
72
|
+
def load_wines_for_country( country_key, name, more_attribs={} )
|
73
|
+
country = Country.find_by_key!( country_key )
|
74
|
+
logger.debug "Country #{country.key} >#{country.title} (#{country.code})<"
|
75
|
+
|
76
|
+
more_attribs[ :country_id ] = country.id
|
77
|
+
|
78
|
+
more_attribs[ :txt ] = name # store source ref
|
79
|
+
|
80
|
+
load_wines_worker( name, more_attribs )
|
81
|
+
end
|
82
|
+
|
83
|
+
|
84
|
+
def load_wines_worker( name, more_attribs={} )
|
85
|
+
reader = ValuesReaderV2.new( name, include_path, more_attribs )
|
86
|
+
|
87
|
+
### todo: cleanup - check if [] works for build_title...
|
88
|
+
# better cleaner way ???
|
89
|
+
if more_attribs[:region_id].present?
|
90
|
+
known_wineries_source = Winery.where( region_id: more_attribs[:region_id] )
|
91
|
+
elsif more_attribs[:country_id].present?
|
92
|
+
known_wineries_source = Winery.where( country_id: more_attribs[:country_id] )
|
93
|
+
else
|
94
|
+
logger.warn "no region or country specified; use empty winery ary for header mapper"
|
95
|
+
known_wineries_source = []
|
96
|
+
end
|
97
|
+
|
98
|
+
known_wineries = TextUtils.build_title_table_for( known_wineries_source )
|
99
|
+
|
100
|
+
reader.each_line do |new_attributes, values|
|
101
|
+
|
102
|
+
## note: check for header attrib; if present remove
|
103
|
+
### todo: cleanup code later
|
104
|
+
## fix: add to new_attributes hash instead of values ary
|
105
|
+
## - fix: match_winery() move region,city code out of values loop for reuse at the end
|
106
|
+
if new_attributes[:header].present?
|
107
|
+
winery_line = new_attributes[:header].dup # note: make sure we make a copy; will use in-place string ops
|
108
|
+
new_attributes.delete(:header) ## note: do NOT forget to remove from hash!
|
109
|
+
|
110
|
+
logger.debug " trying to find winery in line >#{winery_line}<"
|
111
|
+
## todo: check what map_titles_for! returns (nothing ???)
|
112
|
+
TextUtils.map_titles_for!( 'winery', winery_line, known_wineries )
|
113
|
+
winery_key = TextUtils.find_key_for!( 'winery', winery_line )
|
114
|
+
logger.debug " winery_key = >#{winery_key}<"
|
115
|
+
unless winery_key.nil?
|
116
|
+
## bingo! add winery_id upfront, that is, as first value in ary
|
117
|
+
values = values.unshift "winery:#{winery_key}"
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
Wine.create_or_update_from_attribs( new_attributes, values )
|
122
|
+
end # each_line
|
123
|
+
end
|
124
|
+
|
125
|
+
|
126
|
+
def load_wineries_for_country( country_key, name, more_attribs={} )
|
127
|
+
country = Country.find_by_key!( country_key )
|
128
|
+
logger.debug "Country #{country.key} >#{country.title} (#{country.code})<"
|
129
|
+
|
130
|
+
more_attribs[ :country_id ] = country.id
|
131
|
+
|
132
|
+
more_attribs[ :txt ] = name # store source ref
|
133
|
+
|
134
|
+
load_wineries_worker( name, more_attribs )
|
135
|
+
end
|
136
|
+
|
137
|
+
def load_wineries_worker( name, more_attribs={} )
|
138
|
+
reader = ValuesReaderV2.new( name, include_path, more_attribs )
|
139
|
+
|
140
|
+
reader.each_line do |new_attributes, values|
|
141
|
+
|
142
|
+
#######
|
143
|
+
# fix: move to (inside)
|
144
|
+
# Winery.create_or_update_from_attribs ||||
|
145
|
+
## note: group header not used for now; do NOT forget to remove from hash!
|
146
|
+
if new_attributes[:header].present?
|
147
|
+
logger.warn "removing unused group header #{new_attributes[:header]}"
|
148
|
+
new_attributes.delete(:header) ## note: do NOT forget to remove from hash!
|
149
|
+
end
|
150
|
+
|
151
|
+
Winery.create_or_update_from_attribs( new_attributes, values )
|
152
|
+
end # each_line
|
153
|
+
end
|
154
|
+
|
155
|
+
end # class Reader
|
156
|
+
end # module WineDb
|
@@ -0,0 +1,75 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module WineDb
|
4
|
+
|
5
|
+
class CreateDb < ActiveRecord::Migration
|
6
|
+
|
7
|
+
|
8
|
+
def up
|
9
|
+
|
10
|
+
create_table :wines 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
|
+
## check: why decimal and not float?
|
19
|
+
t.decimal :abv # Alcohol by volume (abbreviated as ABV, abv, or alc/vol) e.g. 4.9 %
|
20
|
+
|
21
|
+
t.references :winery # optional (for now)
|
22
|
+
|
23
|
+
|
24
|
+
t.string :txt # source ref
|
25
|
+
t.boolean :txt_auto, null: false, default: false # inline? got auto-added?
|
26
|
+
|
27
|
+
|
28
|
+
t.references :country, null: false
|
29
|
+
t.references :region # optional
|
30
|
+
t.references :city # optional
|
31
|
+
|
32
|
+
t.timestamps
|
33
|
+
end
|
34
|
+
|
35
|
+
|
36
|
+
create_table :wineries do |t|
|
37
|
+
t.string :key, null: false # import/export key
|
38
|
+
t.string :title, null: false
|
39
|
+
t.string :synonyms # comma separated list of synonyms
|
40
|
+
t.string :address
|
41
|
+
t.integer :since
|
42
|
+
## renamed to founded to since
|
43
|
+
## t.integer :founded # year founded/established - todo/fix: rename to since?
|
44
|
+
t.integer :closed # optional; year winery closed
|
45
|
+
|
46
|
+
t.integer :area # in ha e.g. 8 ha # Weingarten/rebflaeche
|
47
|
+
|
48
|
+
# use stars in .txt e.g. # ***/**/*/- => 1/2/3/4
|
49
|
+
t.integer :grade, null: false, default: 4
|
50
|
+
|
51
|
+
|
52
|
+
t.string :txt # source ref
|
53
|
+
t.boolean :txt_auto, null: false, default: false # inline? got auto-added?
|
54
|
+
|
55
|
+
t.string :web # optional web page (e.g. www.ottakringer.at)
|
56
|
+
t.string :wikipedia # optional wiki(pedia page)
|
57
|
+
|
58
|
+
|
59
|
+
t.references :country, null: false
|
60
|
+
t.references :region # optional
|
61
|
+
t.references :city # optional
|
62
|
+
|
63
|
+
t.timestamps
|
64
|
+
end
|
65
|
+
|
66
|
+
end # method up
|
67
|
+
|
68
|
+
def down
|
69
|
+
raise ActiveRecord::IrreversibleMigration
|
70
|
+
end
|
71
|
+
|
72
|
+
|
73
|
+
end # class CreateDb
|
74
|
+
|
75
|
+
end # module WineDb
|
data/lib/winedb/version.rb
CHANGED
data/test/helper.rb
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
## $:.unshift(File.dirname(__FILE__))
|
4
|
+
|
5
|
+
## minitest setup
|
6
|
+
|
7
|
+
# require 'minitest/unit'
|
8
|
+
require 'minitest/autorun'
|
9
|
+
|
10
|
+
# include MiniTest::Unit # lets us use TestCase instead of MiniTest::Unit::TestCase
|
11
|
+
|
12
|
+
|
13
|
+
# ruby stdlibs
|
14
|
+
|
15
|
+
require 'json'
|
16
|
+
require 'uri'
|
17
|
+
require 'pp'
|
18
|
+
|
19
|
+
# ruby gems
|
20
|
+
|
21
|
+
require 'active_record'
|
22
|
+
|
23
|
+
# our own code
|
24
|
+
|
25
|
+
require 'winedb'
|
26
|
+
require 'logutils/db' # NB: explict require required for LogDb (not automatic)
|
27
|
+
|
28
|
+
Country = WorldDb::Model::Country
|
29
|
+
Region = WorldDb::Model::Region
|
30
|
+
City = WorldDb::Model::City
|
31
|
+
|
32
|
+
## todo: get all models aliases (e.g. from console script)
|
33
|
+
|
34
|
+
Wine = WineDb::Model::Wine
|
35
|
+
Winery = WineDb::Model::Winery
|
36
|
+
|
37
|
+
|
38
|
+
def setup_in_memory_db
|
39
|
+
# Database Setup & Config
|
40
|
+
|
41
|
+
db_config = {
|
42
|
+
adapter: 'sqlite3',
|
43
|
+
database: ':memory:'
|
44
|
+
}
|
45
|
+
|
46
|
+
pp db_config
|
47
|
+
|
48
|
+
ActiveRecord::Base.logger = Logger.new( STDOUT )
|
49
|
+
## ActiveRecord::Base.colorize_logging = false - no longer exists - check new api/config setting?
|
50
|
+
|
51
|
+
## NB: every connect will create a new empty in memory db
|
52
|
+
ActiveRecord::Base.establish_connection( db_config )
|
53
|
+
|
54
|
+
|
55
|
+
## build schema
|
56
|
+
|
57
|
+
LogDb.create
|
58
|
+
WorldDb.create
|
59
|
+
WineDb.create
|
60
|
+
end
|
61
|
+
|
62
|
+
|
63
|
+
def fillup_in_memory_db
|
64
|
+
## add some counties
|
65
|
+
|
66
|
+
at = Country.create!( key: 'at', title: 'Austria', code: 'AUT', pop: 0, area: 0 )
|
67
|
+
n = Region.create!( key: 'n', title: 'Niederösterreich', country_id: at.id )
|
68
|
+
|
69
|
+
end
|
70
|
+
|
71
|
+
setup_in_memory_db()
|
72
|
+
fillup_in_memory_db()
|
73
|
+
|
74
|
+
AT = Country.find_by_key!( 'at' )
|
75
|
+
N = Region.find_by_key!( 'n' )
|
76
|
+
|
data/test/test_models.rb
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
###
|
4
|
+
# to run use
|
5
|
+
# ruby -I ./lib -I ./test test/test_helper.rb
|
6
|
+
# or better
|
7
|
+
# rake test
|
8
|
+
|
9
|
+
require 'helper'
|
10
|
+
|
11
|
+
|
12
|
+
class TestModels < MiniTest::Unit::TestCase
|
13
|
+
|
14
|
+
def test_load_wine_values
|
15
|
+
|
16
|
+
key = 'gruenerveltlinerspiegel'
|
17
|
+
|
18
|
+
values = [
|
19
|
+
'Grüner Veltliner Spiegel',
|
20
|
+
'12.3 %',
|
21
|
+
'gv'
|
22
|
+
]
|
23
|
+
|
24
|
+
more_attribs = {
|
25
|
+
country_id: AT.id
|
26
|
+
}
|
27
|
+
|
28
|
+
wine = Wine.create_or_update_from_values( values, more_attribs )
|
29
|
+
|
30
|
+
wine2 = Wine.find_by_key!( key )
|
31
|
+
assert_equal wine.id, wine2.id
|
32
|
+
|
33
|
+
assert_equal wine.title, values[0]
|
34
|
+
assert_equal wine.country_id, AT.id
|
35
|
+
assert_equal wine.country.title, AT.title
|
36
|
+
assert_equal wine.abv, 12.3
|
37
|
+
end
|
38
|
+
|
39
|
+
def test_load_winery_values
|
40
|
+
|
41
|
+
key = 'antonbauer'
|
42
|
+
|
43
|
+
values = [
|
44
|
+
key,
|
45
|
+
'Anton Bauer (1971)',
|
46
|
+
'www.antonbauer.at',
|
47
|
+
'Neufang 42 // 3483 Feuersbrunn',
|
48
|
+
'25 ha', ### todo: make sure it will not get matched as tag
|
49
|
+
'tag'
|
50
|
+
]
|
51
|
+
|
52
|
+
more_attribs = {
|
53
|
+
country_id: AT.id
|
54
|
+
}
|
55
|
+
|
56
|
+
wy = Winery.create_or_update_from_values( values, more_attribs )
|
57
|
+
|
58
|
+
wy2 = Winery.find_by_key!( key )
|
59
|
+
assert_equal wy.id, wy2.id
|
60
|
+
|
61
|
+
assert_equal wy.title, values[1]
|
62
|
+
assert_equal wy.country_id, AT.id
|
63
|
+
assert_equal wy.country.title, AT.title
|
64
|
+
assert_equal wy.web, 'www.antonbauer.at'
|
65
|
+
assert_equal wy.address, 'Neufang 42 // 3483 Feuersbrunn'
|
66
|
+
end
|
67
|
+
|
68
|
+
|
69
|
+
end # class TestModels
|
70
|
+
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: winedb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -13,7 +13,7 @@ date: 2014-03-01 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: activerecord
|
16
|
-
requirement: &
|
16
|
+
requirement: &71681830 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ~>
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: '3.2'
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *71681830
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: worlddb
|
27
|
-
requirement: &
|
27
|
+
requirement: &71681430 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ~>
|
@@ -32,10 +32,10 @@ dependencies:
|
|
32
32
|
version: '1.7'
|
33
33
|
type: :runtime
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *71681430
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: gli
|
38
|
-
requirement: &
|
38
|
+
requirement: &71681160 !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
40
40
|
requirements:
|
41
41
|
- - ! '>='
|
@@ -43,10 +43,10 @@ dependencies:
|
|
43
43
|
version: 2.5.6
|
44
44
|
type: :runtime
|
45
45
|
prerelease: false
|
46
|
-
version_requirements: *
|
46
|
+
version_requirements: *71681160
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
48
|
name: rdoc
|
49
|
-
requirement: &
|
49
|
+
requirement: &71680880 !ruby/object:Gem::Requirement
|
50
50
|
none: false
|
51
51
|
requirements:
|
52
52
|
- - ~>
|
@@ -54,10 +54,10 @@ dependencies:
|
|
54
54
|
version: '3.10'
|
55
55
|
type: :development
|
56
56
|
prerelease: false
|
57
|
-
version_requirements: *
|
57
|
+
version_requirements: *71680880
|
58
58
|
- !ruby/object:Gem::Dependency
|
59
59
|
name: hoe
|
60
|
-
requirement: &
|
60
|
+
requirement: &71680600 !ruby/object:Gem::Requirement
|
61
61
|
none: false
|
62
62
|
requirements:
|
63
63
|
- - ~>
|
@@ -65,7 +65,7 @@ dependencies:
|
|
65
65
|
version: '3.3'
|
66
66
|
type: :development
|
67
67
|
prerelease: false
|
68
|
-
version_requirements: *
|
68
|
+
version_requirements: *71680600
|
69
69
|
description: winedb - wine.db command line tool
|
70
70
|
email: winedb@googlegroups.com
|
71
71
|
executables: []
|
@@ -78,7 +78,19 @@ files:
|
|
78
78
|
- README.md
|
79
79
|
- Rakefile
|
80
80
|
- lib/winedb.rb
|
81
|
+
- lib/winedb/models/city.rb
|
82
|
+
- lib/winedb/models/country.rb
|
83
|
+
- lib/winedb/models/forward.rb
|
84
|
+
- lib/winedb/models/region.rb
|
85
|
+
- lib/winedb/models/tag.rb
|
86
|
+
- lib/winedb/models/wine.rb
|
87
|
+
- lib/winedb/models/winery.rb
|
88
|
+
- lib/winedb/reader.rb
|
89
|
+
- lib/winedb/schema.rb
|
81
90
|
- lib/winedb/version.rb
|
91
|
+
- test/helper.rb
|
92
|
+
- test/test_models.rb
|
93
|
+
- .gemtest
|
82
94
|
homepage: https://github.com/geraldb/wine.db.ruby
|
83
95
|
licenses:
|
84
96
|
- Public Domain
|
@@ -106,4 +118,5 @@ rubygems_version: 1.8.17
|
|
106
118
|
signing_key:
|
107
119
|
specification_version: 3
|
108
120
|
summary: winedb - wine.db command line tool
|
109
|
-
test_files:
|
121
|
+
test_files:
|
122
|
+
- test/test_models.rb
|