openbeautyfacts 0.6.0 → 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.
Files changed (66) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +21 -6
  3. data/Rakefile +5 -3
  4. data/lib/openbeautyfacts/additive.rb +9 -39
  5. data/lib/openbeautyfacts/allergen.rb +14 -22
  6. data/lib/openbeautyfacts/brand.rb +14 -22
  7. data/lib/openbeautyfacts/category.rb +14 -22
  8. data/lib/openbeautyfacts/city.rb +14 -22
  9. data/lib/openbeautyfacts/contributor.rb +14 -22
  10. data/lib/openbeautyfacts/country.rb +14 -22
  11. data/lib/openbeautyfacts/entry_date.rb +15 -23
  12. data/lib/openbeautyfacts/faq.rb +16 -41
  13. data/lib/openbeautyfacts/ingredient.rb +14 -22
  14. data/lib/openbeautyfacts/ingredient_that_may_be_from_palm_oil.rb +14 -22
  15. data/lib/openbeautyfacts/label.rb +14 -22
  16. data/lib/openbeautyfacts/last_edit_date.rb +15 -23
  17. data/lib/openbeautyfacts/locale.rb +8 -31
  18. data/lib/openbeautyfacts/manufacturing_place.rb +14 -22
  19. data/lib/openbeautyfacts/mission.rb +16 -63
  20. data/lib/openbeautyfacts/number_of_ingredients.rb +15 -23
  21. data/lib/openbeautyfacts/origin.rb +14 -22
  22. data/lib/openbeautyfacts/packager_code.rb +14 -22
  23. data/lib/openbeautyfacts/packaging.rb +14 -22
  24. data/lib/openbeautyfacts/period_after_opening.rb +14 -22
  25. data/lib/openbeautyfacts/press.rb +16 -47
  26. data/lib/openbeautyfacts/product.rb +18 -164
  27. data/lib/openbeautyfacts/product_state.rb +14 -22
  28. data/lib/openbeautyfacts/purchase_place.rb +14 -22
  29. data/lib/openbeautyfacts/store.rb +14 -22
  30. data/lib/openbeautyfacts/trace.rb +14 -22
  31. data/lib/openbeautyfacts/user.rb +15 -30
  32. data/lib/openbeautyfacts/version.rb +3 -1
  33. data/lib/openbeautyfacts.rb +6 -9
  34. data/test/fixtures/additives.yml +48 -0
  35. data/test/fixtures/brands.yml +48 -0
  36. data/test/fixtures/entry_dates.yml +48 -0
  37. data/test/fixtures/entry_dates_locale.yml +48 -0
  38. data/test/fixtures/faq.yml +48 -0
  39. data/test/fixtures/fetch_product_3600550362626.yml +54 -0
  40. data/test/fixtures/index.yml +48 -0
  41. data/test/fixtures/ingredients.yml +99 -0
  42. data/test/fixtures/ingredients_locale.yml +99 -0
  43. data/test/fixtures/last_edit_dates.yml +48 -0
  44. data/test/fixtures/last_edit_dates_locale.yml +48 -0
  45. data/test/fixtures/login_user.yml +363 -0
  46. data/test/fixtures/mission.yml +48 -0
  47. data/test/fixtures/missions.yml +48 -0
  48. data/test/fixtures/number_of_ingredients_locale.yml +48 -0
  49. data/test/fixtures/numbers_of_ingredients.yml +48 -0
  50. data/test/fixtures/press.yml +48 -0
  51. data/test/fixtures/product_3600550362626.yml +54 -0
  52. data/test/fixtures/product_states.yml +48 -0
  53. data/test/fixtures/product_states_locale.yml +48 -0
  54. data/test/fixtures/products_for_brand.yml +99 -0
  55. data/test/fixtures/products_for_entry_date.yml +99 -0
  56. data/test/fixtures/products_for_ingredient.yml +99 -0
  57. data/test/fixtures/products_for_last_edit_date.yml +99 -0
  58. data/test/fixtures/products_for_number_of_ingredients.yml +99 -0
  59. data/test/fixtures/products_for_period_after_opening.yml +99 -0
  60. data/test/fixtures/products_for_state.yml +99 -0
  61. data/test/fixtures/products_with_additive.yml +99 -0
  62. data/test/fixtures/search_doux.yml +105 -0
  63. data/test/fixtures/search_doux_1_000_000.yml +54 -0
  64. data/test/minitest_helper.rb +5 -3
  65. data/test/test_openbeautyfacts.rb +129 -97
  66. metadata +63 -21
@@ -1,186 +1,40 @@
1
- require 'cgi'
2
- require 'hashie'
3
- require 'net/http'
4
- require 'nokogiri'
5
- require 'open-uri'
1
+ # frozen_string_literal: true
6
2
 
7
3
  module Openbeautyfacts
8
- class Product < Hashie::Mash
4
+ class Product < Openfoodfacts::Product
5
+ # Override constants for openbeautyfacts domain
6
+ DEFAULT_LOCALE = Locale::GLOBAL
7
+ DEFAULT_DOMAIN = 'openbeautyfacts.org'
9
8
 
10
- # TODO: Add more locales
9
+ # OpenBeautyFacts uses the same URL prefixes as OpenFoodFacts
11
10
  LOCALE_WEBURL_PREFIXES = {
12
11
  'fr' => 'produit',
13
12
  'uk' => 'product',
14
13
  'us' => 'product',
15
14
  'world' => 'product'
16
- }
15
+ }.freeze
17
16
 
18
17
  class << self
19
-
20
- # Get product
21
- #
22
- def get(code, locale: DEFAULT_LOCALE)
23
- if code
24
- product_url = url(code, locale: locale)
25
- json = URI.open(product_url).read
26
- hash = JSON.parse(json)
27
-
28
- new(hash["product"]) if !hash["status"].nil? && hash["status"] == 1
29
- end
30
- end
31
- alias_method :find, :get
32
-
33
- # Return product API URL
34
- #
18
+ # Override URL method to use openbeautyfacts domain
35
19
  def url(code, locale: DEFAULT_LOCALE, domain: DEFAULT_DOMAIN)
36
- if code
37
- path = "api/v0/produit/#{code}.json"
38
- "https://#{locale}.#{domain}/#{path}"
39
- end
40
- end
41
-
42
- # Search products
43
- #
44
- def search(terms, locale: DEFAULT_LOCALE, page: 1, page_size: 20, sort_by: 'unique_scans_n', domain: DEFAULT_DOMAIN)
45
- terms = CGI.escape(terms)
46
- path = "cgi/search.pl?search_terms=#{terms}&jqm=1&page=#{page}&page_size=#{page_size}&sort_by=#{sort_by}"
47
- url = "https://#{locale}.#{domain}/#{path}"
48
- json = URI.open(url).read
49
- hash = JSON.parse(json)
50
- html = hash["jqm"]
51
-
52
- from_jquery_mobile_list(html)
53
- end
54
- alias_method :where, :search
55
-
56
- def from_html_list(html, list_css_selector, code_from_link_regex, locale: 'world')
57
- dom = Nokogiri::HTML.fragment(html)
58
- dom.css(list_css_selector).map do |product|
59
- attributes = {}
60
-
61
- if link = product.css('a').first
62
- attributes["product_name"] = link.inner_text.strip
63
-
64
- if code = link.attr('href')[code_from_link_regex, 1]
65
- attributes["_id"] = code
66
- attributes["code"] = code
67
- end
68
- end
69
-
70
- if image = product.css('img').first and image_url = image.attr('src')
71
- attributes["image_small_url"] = image_url
72
- attributes["lc"] = Locale.locale_from_link(image_url)
73
- end
74
- attributes["lc"] ||= locale
75
-
76
- new(attributes)
77
- end
78
-
79
- end
80
-
81
- def from_jquery_mobile_list(jqm_html)
82
- from_html_list(jqm_html, 'ul#search_results_list li:not(#loadmore)', /code=(\d+)\Z/i)
83
- end
84
-
85
- def from_website_list(html, locale: 'world')
86
- from_html_list(html, 'ul.products li', /\/(\d+)\/?/i, locale: 'world')
87
- end
88
-
89
- # page -1 to fetch all pages
90
- def from_website_page(page_url, page: -1, products_count: nil)
91
- if page == -1
92
- if products_count # Avoid one call
93
- pages_count = (products_count.to_f / 20).ceil
94
- (1..pages_count).map { |page| from_website_page(page_url, page: page) }.flatten
95
- else
96
- products = []
97
-
98
- page = 1
99
- begin
100
- products_on_page = from_website_page(page_url, page: page)
101
- products += products_on_page
102
- page += 1
103
- end while products_on_page.any?
104
-
105
- products
106
- end
107
- else
108
- html = URI.open("#{page_url}/#{page}").read
109
- from_website_list(html, locale: Locale.locale_from_link(page_url))
110
- end
20
+ super(code, locale: locale, domain: domain)
111
21
  end
112
22
 
113
- def tags_from_page(_klass, page_url, &custom_tag_parsing)
114
- html = URI.open(page_url).read
115
- dom = Nokogiri::HTML.fragment(html)
116
-
117
- dom.css('table#tagstable tbody tr').map do |tag|
118
- if custom_tag_parsing
119
- custom_tag_parsing.call(tag)
120
- else
121
- link = tag.css('a').first
122
-
123
- _klass.new({
124
- "name" => link.text.strip,
125
- "url" => URI.join(page_url, link.attr('href')).to_s,
126
- "products_count" => tag.css('td')[1].text.to_i
127
- })
128
- end
129
- end
23
+ # Override search method to use openbeautyfacts domain
24
+ def search(terms, locale: DEFAULT_LOCALE, page: 1, page_size: 20, sort_by: 'unique_scans_n',
25
+ domain: DEFAULT_DOMAIN)
26
+ super(terms, locale: locale, page: page, page_size: page_size, sort_by: sort_by, domain: domain)
130
27
  end
131
-
132
28
  end
133
29
 
134
- # Fetch product
135
- #
136
- def fetch
137
- if (self.code)
138
- product = self.class.get(self.code)
139
- self.merge!(product)
140
- end
141
-
142
- self
30
+ # Override weburl method to use openbeautyfacts domain
31
+ def weburl(locale: nil, domain: DEFAULT_DOMAIN)
32
+ super(locale: locale, domain: domain)
143
33
  end
144
- alias_method :reload, :fetch
145
34
 
146
- # Update product
147
- # Only product_name, brands and quantity fields seems to be updatable throught app / API.
148
- # User can be nil
149
- # Tested not updatable fields: countries, ingredients_text, purchase_places, purchase_places_tag, purchase_places_tags
150
- #
35
+ # Override update method to use openbeautyfacts domain
151
36
  def update(user: nil, domain: DEFAULT_DOMAIN)
152
- if self.code && self.lc
153
- subdomain = self.lc == 'world' ? 'world' : "world-#{self.lc}"
154
- path = 'cgi/product_jqm.pl'
155
- uri = URI("https://#{subdomain}.#{domain}/#{path}")
156
- params = self.to_hash
157
- params.merge!("user_id" => user.user_id, "password" => user.password) if user
158
- response = Net::HTTP.post_form(uri, params)
159
-
160
- data = JSON.parse(response.body)
161
- data["status"] == 1
162
- else
163
- false
164
- end
165
- end
166
- alias_method :save, :update
167
-
168
- # Return Product API URL
169
- #
170
- def url(locale: DEFAULT_LOCALE)
171
- self.class.url(self.code, locale: locale)
37
+ super(user: user, domain: domain)
172
38
  end
173
-
174
- # Return Product web URL according to locale
175
- #
176
- def weburl(locale: nil, domain: DEFAULT_DOMAIN)
177
- locale ||= self.lc || DEFAULT_LOCALE
178
-
179
- if self.code && prefix = LOCALE_WEBURL_PREFIXES[locale]
180
- path = "#{prefix}/#{self.code}"
181
- "https://#{locale}.#{domain}/#{path}"
182
- end
183
- end
184
-
185
39
  end
186
40
  end
@@ -1,33 +1,25 @@
1
- require 'hashie'
1
+ # frozen_string_literal: true
2
2
 
3
3
  module Openbeautyfacts
4
- class ProductState < Hashie::Mash
5
-
6
- # TODO: Add more locales
7
- LOCALE_PATHS = {
8
- 'fr' => 'etats',
9
- 'uk' => 'states',
10
- 'us' => 'states',
11
- 'world' => 'states'
12
- }
4
+ class ProductState < Openfoodfacts::ProductState
5
+ # Override constants for openbeautyfacts domain
6
+ DEFAULT_LOCALE = Locale::GLOBAL
7
+ DEFAULT_DOMAIN = 'openbeautyfacts.org'
13
8
 
14
9
  class << self
15
-
16
- # Get product states
17
- #
18
- def all(locale: DEFAULT_LOCALE, domain: DEFAULT_DOMAIN)
19
- if path = LOCALE_PATHS[locale]
20
- Product.tags_from_page(self, "https://#{locale}.#{domain}/#{path}")
21
- end
10
+ # Override all method to use openbeautyfacts domain if it exists
11
+ def all(locale: DEFAULT_LOCALE, domain: DEFAULT_DOMAIN, **options)
12
+ super(locale: locale, domain: domain, **options)
13
+ rescue NoMethodError
14
+ # Method doesn't exist in parent class, skip
22
15
  end
23
-
24
16
  end
25
17
 
26
- # Get products with state
27
- #
18
+ # Override products method to use openbeautyfacts domain if it exists
28
19
  def products(page: -1)
29
- Product.from_website_page(url, page: page, products_count: products_count) if url
20
+ super(page: page)
21
+ rescue NoMethodError
22
+ # Method doesn't exist in parent class, skip
30
23
  end
31
-
32
24
  end
33
25
  end
@@ -1,33 +1,25 @@
1
- require 'hashie'
1
+ # frozen_string_literal: true
2
2
 
3
3
  module Openbeautyfacts
4
- class PurchasePlace < Hashie::Mash
5
-
6
- # TODO: Add more locales
7
- LOCALE_PATHS = {
8
- 'fr' => 'lieux-de-vente',
9
- 'uk' => 'purchase-places',
10
- 'us' => 'purchase-places',
11
- 'world' => 'purchase-places'
12
- }
4
+ class PurchasePlace < Openfoodfacts::PurchasePlace
5
+ # Override constants for openbeautyfacts domain
6
+ DEFAULT_LOCALE = Locale::GLOBAL
7
+ DEFAULT_DOMAIN = 'openbeautyfacts.org'
13
8
 
14
9
  class << self
15
-
16
- # Get purchase places
17
- #
18
- def all(locale: DEFAULT_LOCALE, domain: DEFAULT_DOMAIN)
19
- if path = LOCALE_PATHS[locale]
20
- Product.tags_from_page(self, "https://#{locale}.#{domain}/#{path}")
21
- end
10
+ # Override all method to use openbeautyfacts domain if it exists
11
+ def all(locale: DEFAULT_LOCALE, domain: DEFAULT_DOMAIN, **options)
12
+ super(locale: locale, domain: domain, **options)
13
+ rescue NoMethodError
14
+ # Method doesn't exist in parent class, skip
22
15
  end
23
-
24
16
  end
25
17
 
26
- # Get products with purchase place
27
- #
18
+ # Override products method to use openbeautyfacts domain if it exists
28
19
  def products(page: -1)
29
- Product.from_website_page(url, page: page, products_count: products_count) if url
20
+ super(page: page)
21
+ rescue NoMethodError
22
+ # Method doesn't exist in parent class, skip
30
23
  end
31
-
32
24
  end
33
25
  end
@@ -1,33 +1,25 @@
1
- require 'hashie'
1
+ # frozen_string_literal: true
2
2
 
3
3
  module Openbeautyfacts
4
- class Store < Hashie::Mash
5
-
6
- # TODO: Add more locales
7
- LOCALE_PATHS = {
8
- 'fr' => 'magasins',
9
- 'uk' => 'stores',
10
- 'us' => 'stores',
11
- 'world' => 'stores'
12
- }
4
+ class Store < Openfoodfacts::Store
5
+ # Override constants for openbeautyfacts domain
6
+ DEFAULT_LOCALE = Locale::GLOBAL
7
+ DEFAULT_DOMAIN = 'openbeautyfacts.org'
13
8
 
14
9
  class << self
15
-
16
- # Get stores
17
- #
18
- def all(locale: DEFAULT_LOCALE, domain: DEFAULT_DOMAIN)
19
- if path = LOCALE_PATHS[locale]
20
- Product.tags_from_page(self, "https://#{locale}.#{domain}/#{path}")
21
- end
10
+ # Override all method to use openbeautyfacts domain if it exists
11
+ def all(locale: DEFAULT_LOCALE, domain: DEFAULT_DOMAIN, **options)
12
+ super(locale: locale, domain: domain, **options)
13
+ rescue NoMethodError
14
+ # Method doesn't exist in parent class, skip
22
15
  end
23
-
24
16
  end
25
17
 
26
- # Get products from store
27
- #
18
+ # Override products method to use openbeautyfacts domain if it exists
28
19
  def products(page: -1)
29
- Product.from_website_page(url, page: page, products_count: products_count) if url
20
+ super(page: page)
21
+ rescue NoMethodError
22
+ # Method doesn't exist in parent class, skip
30
23
  end
31
-
32
24
  end
33
25
  end
@@ -1,33 +1,25 @@
1
- require 'hashie'
1
+ # frozen_string_literal: true
2
2
 
3
3
  module Openbeautyfacts
4
- class Trace < Hashie::Mash
5
-
6
- # TODO: Add more locales
7
- LOCALE_PATHS = {
8
- 'fr' => 'traces',
9
- 'uk' => 'traces',
10
- 'us' => 'traces',
11
- 'world' => 'traces'
12
- }
4
+ class Trace < Openfoodfacts::Trace
5
+ # Override constants for openbeautyfacts domain
6
+ DEFAULT_LOCALE = Locale::GLOBAL
7
+ DEFAULT_DOMAIN = 'openbeautyfacts.org'
13
8
 
14
9
  class << self
15
-
16
- # Get traces
17
- #
18
- def all(locale: DEFAULT_LOCALE, domain: DEFAULT_DOMAIN)
19
- if path = LOCALE_PATHS[locale]
20
- Product.tags_from_page(self, "https://#{locale}.#{domain}/#{path}")
21
- end
10
+ # Override all method to use openbeautyfacts domain if it exists
11
+ def all(locale: DEFAULT_LOCALE, domain: DEFAULT_DOMAIN, **options)
12
+ super(locale: locale, domain: domain, **options)
13
+ rescue NoMethodError
14
+ # Method doesn't exist in parent class, skip
22
15
  end
23
-
24
16
  end
25
17
 
26
- # Get products with trace
27
- #
18
+ # Override products method to use openbeautyfacts domain if it exists
28
19
  def products(page: -1)
29
- Product.from_website_page(url, page: page, products_count: products_count) if url
20
+ super(page: page)
21
+ rescue NoMethodError
22
+ # Method doesn't exist in parent class, skip
30
23
  end
31
-
32
24
  end
33
25
  end
@@ -1,40 +1,25 @@
1
- require 'net/http'
1
+ # frozen_string_literal: true
2
2
 
3
3
  module Openbeautyfacts
4
- class User < Hashie::Mash
4
+ class User < Openfoodfacts::User
5
+ # Override constants for openbeautyfacts domain
6
+ DEFAULT_LOCALE = Locale::GLOBAL
7
+ DEFAULT_DOMAIN = 'openbeautyfacts.org'
5
8
 
6
9
  class << self
7
-
8
- # Login
9
- #
10
- def login(user_id, password, locale: DEFAULT_LOCALE, domain: DEFAULT_DOMAIN)
11
- path = 'cgi/session.pl'
12
- uri = URI("https://#{locale}.#{domain}/#{path}")
13
- params = {
14
- "jqm" => "1",
15
- "user_id" => user_id,
16
- "password" => password
17
- }
18
-
19
- response = Net::HTTP.post_form(uri, params)
20
- data = JSON.parse(response.body)
21
-
22
- if data['user_id']
23
- data.merge!(password: password)
24
- new(data)
25
- end
10
+ # Override all method to use openbeautyfacts domain if it exists
11
+ def all(locale: DEFAULT_LOCALE, domain: DEFAULT_DOMAIN, **options)
12
+ super(locale: locale, domain: domain, **options)
13
+ rescue NoMethodError
14
+ # Method doesn't exist in parent class, skip
26
15
  end
27
-
28
16
  end
29
17
 
30
- # Login
31
- #
32
- def login(locale: DEFAULT_LOCALE)
33
- if user = self.class.login(self.user_id, self.password, locale: locale)
34
- self.name = user.name
35
- self
36
- end
18
+ # Override products method to use openbeautyfacts domain if it exists
19
+ def products(page: -1)
20
+ super(page: page)
21
+ rescue NoMethodError
22
+ # Method doesn't exist in parent class, skip
37
23
  end
38
-
39
24
  end
40
25
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Openbeautyfacts
2
- VERSION = "0.6.0"
4
+ VERSION = '0.10.0'
3
5
  end
@@ -1,3 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'openfoodfacts'
4
+
5
+ require_relative 'openbeautyfacts/version'
6
+ require_relative 'openbeautyfacts/locale'
1
7
  require_relative 'openbeautyfacts/additive'
2
8
  require_relative 'openbeautyfacts/brand'
3
9
  require_relative 'openbeautyfacts/category'
@@ -10,7 +16,6 @@ require_relative 'openbeautyfacts/ingredient'
10
16
  require_relative 'openbeautyfacts/ingredient_that_may_be_from_palm_oil'
11
17
  require_relative 'openbeautyfacts/label'
12
18
  require_relative 'openbeautyfacts/last_edit_date'
13
- require_relative 'openbeautyfacts/locale'
14
19
  require_relative 'openbeautyfacts/manufacturing_place'
15
20
  require_relative 'openbeautyfacts/mission'
16
21
  require_relative 'openbeautyfacts/number_of_ingredients'
@@ -25,19 +30,12 @@ require_relative 'openbeautyfacts/purchase_place'
25
30
  require_relative 'openbeautyfacts/store'
26
31
  require_relative 'openbeautyfacts/trace'
27
32
  require_relative 'openbeautyfacts/user'
28
- require_relative 'openbeautyfacts/version'
29
-
30
- require 'json'
31
- require 'nokogiri'
32
- require 'open-uri'
33
33
 
34
34
  module Openbeautyfacts
35
-
36
35
  DEFAULT_LOCALE = Locale::GLOBAL
37
36
  DEFAULT_DOMAIN = 'openbeautyfacts.org'
38
37
 
39
38
  class << self
40
-
41
39
  # Return locale from link
42
40
  #
43
41
  def locale_from_link(link)
@@ -61,6 +59,5 @@ module Openbeautyfacts
61
59
  def product_url(barcode, locale: DEFAULT_LOCALE)
62
60
  Product.url(barcode, locale: locale)
63
61
  end
64
-
65
62
  end
66
63
  end