grocery_list 0.0.1

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: c08af85be634afbb4f912332746152da9d682304
4
+ data.tar.gz: f3603ba95a08980c9c901a50b5d276b6cf7af355
5
+ SHA512:
6
+ metadata.gz: 9fac06fe431c682442997a80b4a726777c90525cabba030070717f13261fcc7d52423820b068105a084911cc4f2c92c1a58015f11c3e1b1a236a3e620e77a1cc
7
+ data.tar.gz: 38c6fe94ca1c7900ebf7c2c905b94505a5bab775cb35567303e5d769222e1c8f9a93831e5d6a58e05af454f03649fc1dd28d443af59ac3f8f8ec9eba3c6459c0
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ tags
16
+ test/tmp
17
+ test/version_tmp
18
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in grocery_list.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Charles-P. Clermont
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,53 @@
1
+ # Grocery-list
2
+ Turn your json, csv, md, or strings into grocery lists that you can
3
+ then search for.
4
+
5
+ ## Example usage
6
+ ```ruby
7
+ require 'grocery_list'
8
+
9
+ GroceryList.search_all('spam, eggs, bacon')
10
+ #=> opens 1 tab/item on iga.net sorted by price ascending
11
+ ```
12
+
13
+ ## Installation
14
+ ```bash
15
+ gem install grocery_list
16
+ ```
17
+
18
+ ## Formats
19
+ ### Strings
20
+ Strings are expected to be split by commas.
21
+
22
+ ```ruby
23
+ "spam, egg, bacon, maple syrup" #=> valid
24
+ "spam, egg, \nbacon, maple syrup" #=> invalid
25
+ ```
26
+
27
+ ### JSON
28
+ Just pass in an array of strings and you're golden.
29
+
30
+ ```ruby
31
+ '["spam", "egg", "bacon", "maple syrup"]'
32
+ ```
33
+
34
+ ### Markdown File
35
+ Every list items identified by a ` *` is considered an item. Every
36
+ line which doesn't respect this isn't.
37
+
38
+ ```markdown
39
+ # Grocery List
40
+ * Spam
41
+ * Eggs
42
+ * Bacon
43
+ * Maple syrup
44
+
45
+ Anything else isn't considered an item.
46
+ ```
47
+
48
+ ## To do
49
+ * [ ] Implement more searchers? I have IGA.net working in Montreal;
50
+ Could be extended?
51
+
52
+ ## License
53
+ MIT
data/example.rb ADDED
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'grocery_list'
4
+
5
+ # make sure arguments are ok
6
+ unless ARGV.length <= 2 && ARGV.length > 0
7
+ puts "Wrong number of arguments"
8
+ puts "Usage: example.rb [source] [:sort-option]"
9
+ exit
10
+ end
11
+
12
+ # parse input arguments
13
+ input_source = ARGV[0]
14
+ sort_option = ARGV[1].to_sym unless ARGV.length < 2
15
+
16
+ # search
17
+ GroceryList.search_all(input_source, GroceryList::IGASearcher.new(sort_option || :priceAsc))
@@ -0,0 +1,23 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'grocery_list/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "grocery_list"
8
+ spec.version = GroceryList::VERSION
9
+ spec.authors = ["Charles-P. Clermont"]
10
+ spec.email = ["charles.pclermont@gmail.com"]
11
+ spec.summary = %q{Turn your json, csv, md, or strings into grocery lists that you can then search for.}
12
+ # spec.description = %q{}
13
+ spec.homepage = "https://github.com/charlespwd/grocery-list"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_runtime_dependency "launchy", "~> 2.4"
22
+ spec.add_development_dependency "bundler", "~> 1.5"
23
+ end
@@ -0,0 +1,14 @@
1
+ module GroceryList
2
+ class Item
3
+ attr_reader :item_name
4
+
5
+ def initialize(item_name)
6
+ @item_name = item_name
7
+ end
8
+
9
+ def ==(y)
10
+ y.item_name == @item_name
11
+ end
12
+ end
13
+ end
14
+
@@ -0,0 +1,11 @@
1
+ module GroceryList
2
+ class AbstractItemParser
3
+ def self.read(string)
4
+ raise NotImplementedError
5
+ end
6
+ end
7
+ end
8
+
9
+ require 'grocery_list/item_parsers/string_item_parser'
10
+ require 'grocery_list/item_parsers/file_item_parser'
11
+ require 'grocery_list/item_parsers/json_item_parser'
@@ -0,0 +1,32 @@
1
+ module GroceryList
2
+ class FileItemParser < AbstractItemParser
3
+ class << self
4
+ def read(file_path)
5
+ raise ArgumentError unless file_path.is_a? String
6
+ file = IO.read(file_path)
7
+ items_from_file(file)
8
+ end
9
+
10
+ private
11
+ def items_from_file(file)
12
+ file.split("\n").map do |line|
13
+ line_to_item(line)
14
+ end.select do |item|
15
+ item.is_a? Item
16
+ end
17
+ end
18
+
19
+ def line_to_item(line)
20
+ Item.new(item_name(line)) if is_valid_item(line)
21
+ end
22
+
23
+ def is_valid_item(line)
24
+ line.start_with? " *"
25
+ end
26
+
27
+ def item_name(line)
28
+ line[2..-1].strip
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,14 @@
1
+ require 'json'
2
+
3
+ module GroceryList
4
+ class JsonItemParser < AbstractItemParser
5
+ def self.read(json_string)
6
+ parsed_json = JSON.parse(json_string)
7
+ raise ArgumentError unless parsed_json.is_a? Array
8
+
9
+ parsed_json.map do |item|
10
+ Item.new(item.strip)
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,8 @@
1
+ module GroceryList
2
+ class StringItemParser < AbstractItemParser
3
+ def self.read(string)
4
+ raise ArgumentError unless string.is_a? String
5
+ string.split(',').map {|s| Item.new(s.strip)}
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,9 @@
1
+ module GroceryList
2
+ class AbstractSearcher
3
+ def search(item)
4
+ raise NotImplementedError
5
+ end
6
+ end
7
+ end
8
+
9
+ require 'grocery_list/item_searchers/iga_item_searcher'
@@ -0,0 +1,46 @@
1
+ module GroceryList
2
+ class IGASearcher < AbstractSearcher
3
+ attr :search_options
4
+
5
+ @@search_url = "http://magasin.iga.net/Search/BasicSearch.aspx?Search="
6
+
7
+ @@valid_options = [
8
+ :priceDesc,
9
+ :priceAsc,
10
+ :DisplayNameDesc,
11
+ :DisplayNameAsc,
12
+ ]
13
+
14
+ def initialize(search_option = :priceAsc)
15
+ @search_options = search_option
16
+ end
17
+
18
+ def search(item)
19
+ raise ArgumentError, "#{item} isn't an Item" unless item.is_a? Item
20
+ Launchy.open url(item.item_name)
21
+ end
22
+
23
+ def search_options=(option = :priceAsc)
24
+ raise ArgumentError, "#{option} is an invalid option" unless @@valid_options.include? option
25
+ @search_options = option
26
+ end
27
+
28
+ def search_options
29
+ @search_options
30
+ end
31
+
32
+ private
33
+ def url(item_name)
34
+ "#{@@search_url}#{url_encode(item_name)}#{encoded_options}"
35
+ end
36
+
37
+ def url_encode(str)
38
+ str.gsub(/ /, '%20')
39
+ end
40
+
41
+ def encoded_options
42
+ @search_options ? "&sort=#{@search_options.to_s}" : ""
43
+ end
44
+ end
45
+ end
46
+
@@ -0,0 +1,3 @@
1
+ module GroceryList
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,50 @@
1
+ require 'launchy'
2
+
3
+ module GroceryList
4
+ class << self
5
+ attr_reader :items, :searcher
6
+
7
+ @@source_type_tests = {
8
+ :is_json_array? => ->(s) { s.start_with? '[' },
9
+ :is_a_path? => ->(s) { s.is_a? String and File.readable? s },
10
+ :is_a_string? => ->(s) { s.is_a? String and not File.readable? s},
11
+ }
12
+ @@searchers = {
13
+ :is_json_array? => ->(s) { JsonItemParser.read(s) },
14
+ :is_a_path? => ->(s) { FileItemParser.read(s) },
15
+ :is_a_string? => ->(s) { StringItemParser.read(s) },
16
+ }
17
+
18
+ def search_all(items_source, searcher = IGASearcher.new)
19
+ validate_input(items_source, searcher)
20
+ @items.each do |item|
21
+ @searcher.search(item)
22
+ end
23
+ end
24
+
25
+ private
26
+ def validate_input(items_source, searcher)
27
+ raise ArgumentError, "isn't a valid source" unless is_valid_source? items_source
28
+ raise ArgumentError, "searcher isn't a AbstractSearcher" unless searcher.is_a? AbstractSearcher
29
+ @items = items_from_source(items_source)
30
+ @searcher = searcher
31
+ end
32
+
33
+ def items_from_source(items_source)
34
+ @@source_type_tests.each do |key, test|
35
+ if test.call(items_source)
36
+ return @@searchers[key].call(items_source)
37
+ end
38
+ end
39
+ end
40
+
41
+ def is_valid_source?(items_source)
42
+ @@source_type_tests.values.any? {|f| f.call(items_source)}
43
+ end
44
+ end
45
+ end
46
+
47
+ require 'grocery_list/item'
48
+ require 'grocery_list/item_parser'
49
+ require 'grocery_list/item_searcher'
50
+ require "grocery_list/version"
@@ -0,0 +1,7 @@
1
+ # My test list
2
+
3
+ * bread
4
+ * philadelphia
5
+ * bagel all dressed
6
+
7
+ Anything else shoudn't be parsed
@@ -0,0 +1,42 @@
1
+ require 'grocery_list'
2
+ require './spec/stubs/IGASearcherStub'
3
+
4
+ ITEMS_AS_STRING = "bread, philadelphia, bagel all dressed"
5
+ ITEMS_AS_JSON = '["bread", "philadelphia", "bagel all dressed"]'
6
+
7
+ describe GroceryList do
8
+ it "should accept a list as a string and a searcher" do
9
+ expect { GroceryList.search_all(ITEMS_AS_STRING, IGASearcherStub.new) }.to_not raise_error
10
+ end
11
+
12
+ it "should accept a list as a path and a searcher" do
13
+ expect { GroceryList.search_all('./spec/fixtures/list_test.md', IGASearcherStub.new) }.to_not raise_error
14
+ end
15
+
16
+ it "should accept a list as a json string and a searcher" do
17
+ expect { GroceryList.search_all(ITEMS_AS_JSON, IGASearcherStub.new) }.to_not raise_error
18
+ end
19
+
20
+ it "should throw an error if path isn't readble or if not a string" do
21
+ expect {GroceryList.search_all('./lib', IGASearcherStub.new)}.to raise_error
22
+ expect {GroceryList.search_all(5, IGASearcherStub.new)}.to raise_error
23
+ end
24
+
25
+ it "should not throw an error if empty string is provided" do
26
+ expect {GroceryList.search_all('', IGASearcherStub.new)}.to_not raise_error
27
+ end
28
+
29
+ it "should not throw an error if the searcher isn't a searcher" do
30
+ expect {GroceryList.search_all('', 'IGASearcherStub')}.to raise_error
31
+ end
32
+
33
+ it "should search all items" do
34
+ expect { GroceryList.search_all('./spec/fixtures/list_test.md', IGASearcherStub.new) }.to_not raise_error
35
+ end
36
+
37
+ it "should search all items sorted by priceAsc" do
38
+ searcher = IGASearcherStub.new
39
+ searcher.search_options = :priceAsc
40
+ expect { GroceryList.search_all('./spec/fixtures/list_test.md', searcher) }.to_not raise_error
41
+ end
42
+ end
@@ -0,0 +1,52 @@
1
+ require 'grocery_list'
2
+
3
+ ITEMS_AS_STRING = "bread, philadelphia, bagel all dressed"
4
+ ITEMS_AS_JSON = '["bread", "philadelphia", "bagel all dressed"]'
5
+
6
+ describe GroceryList::AbstractItemParser do
7
+ let(:reader) { GroceryList::AbstractItemParser }
8
+
9
+ it "should have an unimplemented read method" do
10
+ expect{ reader.read }.to raise_error
11
+ end
12
+ end
13
+
14
+ describe GroceryList::StringItemParser do
15
+ let(:reader) { GroceryList::StringItemParser }
16
+
17
+ it "should turn a string into an array of item" do
18
+ items = reader.read(ITEMS_AS_STRING)
19
+ expect(items.is_a? Array).to eql(true)
20
+ end
21
+ end
22
+
23
+ describe GroceryList::FileItemParser do
24
+ let(:reader) { GroceryList::FileItemParser }
25
+
26
+ it "should raise an error if no files are found" do
27
+ expect{ reader.read('null') }.to raise_error
28
+ expect{ reader.read('./spec/lib/searcher_spec.rb') }.to_not raise_error
29
+ end
30
+
31
+ it "should only read lines prefixed by a star" do
32
+ expected_result = GroceryList::StringItemParser.read(ITEMS_AS_STRING)
33
+
34
+ expect(reader.read('./spec/fixtures/list_test.md')).to eq(expected_result)
35
+ end
36
+ end
37
+
38
+ describe GroceryList::JsonItemParser do
39
+ let(:reader) { GroceryList::JsonItemParser }
40
+
41
+ it "should raise an error if json isn't an array" do
42
+ expect{ reader.read(42) }.to raise_error
43
+ expect{ reader.read('null') }.to raise_error
44
+ expect{ reader.read(ITEMS_AS_JSON) }.to_not raise_error
45
+ end
46
+
47
+ it "should turn parsed array into an array of items" do
48
+ expected_result = GroceryList::StringItemParser.read(ITEMS_AS_STRING)
49
+
50
+ expect(reader.read(ITEMS_AS_JSON)).to eq(expected_result)
51
+ end
52
+ end
@@ -0,0 +1,26 @@
1
+ require "grocery_list"
2
+ require "./spec/stubs/IGASearcherStub"
3
+
4
+ describe GroceryList::AbstractSearcher do
5
+ it "should have a search method that is not implemented" do
6
+ searcher = GroceryList::AbstractSearcher.new
7
+ expect{ searcher.search("") }.to raise_error
8
+ end
9
+ end
10
+
11
+ describe GroceryList::IGASearcher do
12
+ it "should have a search method that is implemented" do
13
+ searcher = GroceryList::IGASearcher.new
14
+ expect{ searcher.search("") }.to raise_error
15
+ # expect{ searcher.search(Item.new("pain")) }.to_not raise_error
16
+ end
17
+
18
+ it "should throw an error if trying to set an invalid option" do
19
+ searcher = IGASearcherStub.new
20
+ expect { searcher.search_options = :invalid }.to raise_error
21
+ expect { searcher.search_options = :priceAsc }.to_not raise_error
22
+ expect { searcher.search_options = :priceDesc }.to_not raise_error
23
+ expect { searcher.search_options = :DisplayNameAsc }.to_not raise_error
24
+ expect { searcher.search_options = :DisplayNameDesc }.to_not raise_error
25
+ end
26
+ end
@@ -0,0 +1,8 @@
1
+ require "grocery_list"
2
+
3
+ class IGASearcherStub < GroceryList::IGASearcher
4
+ def search(item)
5
+ raise ArgumentError, "#{item} isn't an Item" unless item.is_a? GroceryList::Item
6
+ puts url(item.item_name)
7
+ end
8
+ end
metadata ADDED
@@ -0,0 +1,98 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: grocery_list
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Charles-P. Clermont
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-09-02 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: launchy
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '2.4'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '2.4'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: '1.5'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: '1.5'
41
+ description:
42
+ email:
43
+ - charles.pclermont@gmail.com
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - .gitignore
49
+ - Gemfile
50
+ - LICENSE.txt
51
+ - README.md
52
+ - example.rb
53
+ - grocery_list.gemspec
54
+ - lib/grocery_list.rb
55
+ - lib/grocery_list/item.rb
56
+ - lib/grocery_list/item_parser.rb
57
+ - lib/grocery_list/item_parsers/file_item_parser.rb
58
+ - lib/grocery_list/item_parsers/json_item_parser.rb
59
+ - lib/grocery_list/item_parsers/string_item_parser.rb
60
+ - lib/grocery_list/item_searcher.rb
61
+ - lib/grocery_list/item_searchers/iga_item_searcher.rb
62
+ - lib/grocery_list/version.rb
63
+ - spec/fixtures/list_test.md
64
+ - spec/lib/groceries_spec.rb
65
+ - spec/lib/item_reader_spec.rb
66
+ - spec/lib/searcher_spec.rb
67
+ - spec/stubs/IGASearcherStub.rb
68
+ homepage: https://github.com/charlespwd/grocery-list
69
+ licenses:
70
+ - MIT
71
+ metadata: {}
72
+ post_install_message:
73
+ rdoc_options: []
74
+ require_paths:
75
+ - lib
76
+ required_ruby_version: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - '>='
79
+ - !ruby/object:Gem::Version
80
+ version: '0'
81
+ required_rubygems_version: !ruby/object:Gem::Requirement
82
+ requirements:
83
+ - - '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ requirements: []
87
+ rubyforge_project:
88
+ rubygems_version: 2.3.0
89
+ signing_key:
90
+ specification_version: 4
91
+ summary: Turn your json, csv, md, or strings into grocery lists that you can then
92
+ search for.
93
+ test_files:
94
+ - spec/fixtures/list_test.md
95
+ - spec/lib/groceries_spec.rb
96
+ - spec/lib/item_reader_spec.rb
97
+ - spec/lib/searcher_spec.rb
98
+ - spec/stubs/IGASearcherStub.rb