storexplore 0.4.0 → 0.5.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 +4 -4
- data/.gitignore +3 -0
- data/.travis.yml +2 -2
- data/README.md +120 -50
- data/docker-compose.yml +8 -0
- data/ikea.jpg +0 -0
- data/lib/storexplore/testing/api_shared_examples.rb +3 -3
- data/lib/storexplore/version.rb +2 -2
- data/samples/ikea.rb +84 -0
- data/spec/lib/storexplore/testing/memory_usage_spec.rb +2 -1
- data/spec/lib/storexplore/walker_page_spec.rb +2 -2
- data/spec/spec_helper.rb +3 -4
- data/storexplore.gemspec +1 -1
- metadata +31 -49
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- metadata.gz.sig +0 -0
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA1:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 17cba9cf042f031c9d29a00f36ddc74bd1a55bde
         | 
| 4 | 
            +
              data.tar.gz: 5e4f2f3c4e5ee403527e1c2e0beb188c9536498b
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: a88009b403a3b8505104c25b020e01d246cd592140fa2ab43a4641143278588785391d6b33d74fb66f2c3efe12e48c743b3df4c1eed5c829fd4d746511107933
         | 
| 7 | 
            +
              data.tar.gz: 0efe5547f14b84d8c17b91cca614a6f2577733240b38f191815b405ce50c1e6cf94f629c86e0052433c88d8b8ba476214893cfe026fe24efcf651d3a9de2234e
         | 
    
        data/.gitignore
    CHANGED
    
    
    
        data/.travis.yml
    CHANGED
    
    
    
        data/README.md
    CHANGED
    
    | @@ -2,12 +2,32 @@ | |
| 2 2 |  | 
| 3 3 | 
             
            # Storexplore
         | 
| 4 4 |  | 
| 5 | 
            -
             | 
| 5 | 
            +
            Transform online stores into APIs !
         | 
| 6 6 |  | 
| 7 | 
            -
            ##  | 
| 7 | 
            +
            ## Why
         | 
| 8 | 
            +
            Once upon a time, I wanted to create online groceries with great user experience ! That's how I started [mes-courses.fr](https://github.com/philou/mes-courses). Unfortunately, most online groceries don't have APIs, so I resorted to scrapping. Scrapping comes with its (long) list of problems aswell !
         | 
| 8 9 |  | 
| 9 | 
            -
             | 
| 10 | 
            -
             | 
| 10 | 
            +
            * Scrapping code is a mess
         | 
| 11 | 
            +
            * The scrapped html can change at any time
         | 
| 12 | 
            +
            * Scrappers are difficult to test
         | 
| 13 | 
            +
             | 
| 14 | 
            +
            Refactoring by refactoring, I extracted this libary which defines scrappers for any online store in a straightforward way (check [auchandirect-scrAPI](https://github.com/philou/auchandirect-scrAPI) for my real world usage). A scrapper definition consists of :
         | 
| 15 | 
            +
             | 
| 16 | 
            +
            * a scrapper definition file
         | 
| 17 | 
            +
            * the selectors for the links
         | 
| 18 | 
            +
            * the selectors for the content you want to capture
         | 
| 19 | 
            +
             | 
| 20 | 
            +
            As a result of using storexplore for mes-courses, the scrapping code was split between the storexplore gem and my special scrapper definition :
         | 
| 21 | 
            +
             | 
| 22 | 
            +
            * This made the whole overall code cleaner
         | 
| 23 | 
            +
            * I could write simple and reliable tests
         | 
| 24 | 
            +
            * Most importantly, I could easily keep pace with the changes in the online store html
         | 
| 25 | 
            +
             | 
| 26 | 
            +
            ## Tutorial
         | 
| 27 | 
            +
             | 
| 28 | 
            +
            [](http://www.youtube.com/watch?v=O30xReGgdVU)
         | 
| 29 | 
            +
             | 
| 30 | 
            +
            ## Installation
         | 
| 11 31 |  | 
| 12 32 | 
             
            Add this line to your application's Gemfile:
         | 
| 13 33 |  | 
| @@ -21,44 +41,81 @@ Or install it yourself as: | |
| 21 41 |  | 
| 22 42 | 
             
                $ gem install storexplore
         | 
| 23 43 |  | 
| 24 | 
            -
             | 
| 25 | 
            -
             | 
| 26 | 
            -
            The library builds hierarchical APIs on online stores. Stores are typicaly
         | 
| 27 | 
            -
            organized in the following way :
         | 
| 28 | 
            -
             | 
| 29 | 
            -
                Store > Categories > ... > Sub Categories > Items
         | 
| 30 | 
            -
             | 
| 31 | 
            -
            The store is like a root category. Any category, at any depth level can have
         | 
| 32 | 
            -
            both children categories and items. Items cannot have children of any kind.
         | 
| 33 | 
            -
            Both categories and items can have attributes.
         | 
| 44 | 
            +
            In order to be able to enumerate all items of a store in constant memory,
         | 
| 45 | 
            +
            Storexplore requires ruby 2.0 for its lazy enumerators.
         | 
| 34 46 |  | 
| 35 | 
            -
             | 
| 36 | 
            -
            selectors (css or xpath).
         | 
| 47 | 
            +
            ## Usage
         | 
| 37 48 |  | 
| 38 | 
            -
             | 
| 49 | 
            +
            Online stores are typicaly organized as hierarchies. For example [Ikea (US)](http://www.ikea.com/us/en) is organized as follows :
         | 
| 50 | 
            +
             | 
| 51 | 
            +
                Ikea
         | 
| 52 | 
            +
                |-> Living room
         | 
| 53 | 
            +
                |   |-> Sofas & armchairs
         | 
| 54 | 
            +
                |   |   |-> Fabric Sofas
         | 
| 55 | 
            +
                |   |   |   |-> Norsborg Sofa
         | 
| 56 | 
            +
                |   |   |   |-> Norborg Loveseat
         | 
| 57 | 
            +
                |   |   |   |-> ...
         | 
| 58 | 
            +
                |   |   |   |-> Pöang Footstool cushion
         | 
| 59 | 
            +
                |   |   |-> Leather Sofas
         | 
| 60 | 
            +
                |   |   |-> ...
         | 
| 61 | 
            +
                |   |   |-> Armchairs
         | 
| 62 | 
            +
                |   |-> TV & media funiture
         | 
| 63 | 
            +
                |   |-> ...
         | 
| 64 | 
            +
                |   |-> Living room textiles & rugs
         | 
| 65 | 
            +
                |-> Bedroom
         | 
| 66 | 
            +
                |-> ...
         | 
| 67 | 
            +
                |-> Dining
         | 
| 68 | 
            +
             | 
| 69 | 
            +
            Storexplore builds hierarchical APIs on the following pattern :
         | 
| 70 | 
            +
             | 
| 71 | 
            +
                Store
         | 
| 72 | 
            +
                |-> Category 1
         | 
| 73 | 
            +
                |   |-> Sub Category 1
         | 
| 74 | 
            +
                |   |   |-> Item 1
         | 
| 75 | 
            +
                |   |   |-> ...
         | 
| 76 | 
            +
                |   |   |-> Item n
         | 
| 77 | 
            +
                |   |-> Sub Category 2
         | 
| 78 | 
            +
                |   |-> ...
         | 
| 79 | 
            +
                |   |-> Sub Category n
         | 
| 80 | 
            +
                |-> Category 2
         | 
| 81 | 
            +
                |-> ...
         | 
| 82 | 
            +
                |-> Category n
         | 
| 83 | 
            +
             | 
| 84 | 
            +
            The store is like a root category. Any level of depth is allowed. Any category, at any depth level can have both children categories and items. Items cannot have children of any kind. Both categories and items can have attributes.
         | 
| 85 | 
            +
             | 
| 86 | 
            +
            All searching of children and attributes is done through mechanize/nokogiri selectors (css or xpath).
         | 
| 87 | 
            +
             | 
| 88 | 
            +
            Here is a sample store api declaration for [Ikea](http://www.ikea.com/us/en) again:
         | 
| 39 89 |  | 
| 40 90 | 
             
            ```ruby
         | 
| 41 | 
            -
            Storexplore:: | 
| 91 | 
            +
            Storexplore::Api.define 'ikea.com/us' do
         | 
| 42 92 |  | 
| 43 | 
            -
              categories 'a | 
| 93 | 
            +
              categories '.departmentLinkBlock a' do
         | 
| 44 94 | 
             
                attributes do
         | 
| 45 | 
            -
                  { :name => page.get_one(" | 
| 95 | 
            +
                  { :name => page.get_one("#breadCrumbNew .activeLink a").content.strip }
         | 
| 46 96 | 
             
                end
         | 
| 47 97 |  | 
| 48 | 
            -
                categories 'a | 
| 98 | 
            +
                categories '.departmentLinks a' do
         | 
| 49 99 | 
             
                  attributes do
         | 
| 50 | 
            -
                    { :name => page.get_one(" | 
| 100 | 
            +
                    { :name => page.get_one("#breadCrumbNew .activeLink a").content.strip }
         | 
| 51 101 | 
             
                  end
         | 
| 52 102 |  | 
| 53 | 
            -
                   | 
| 103 | 
            +
                  categories 'a.categoryName' do
         | 
| 54 104 | 
             
                    attributes do
         | 
| 55 | 
            -
                      {
         | 
| 56 | 
            -
             | 
| 57 | 
            -
             | 
| 58 | 
            -
             | 
| 59 | 
            -
             | 
| 60 | 
            -
                         | 
| 61 | 
            -
             | 
| 105 | 
            +
                      { :name => page.get_one("#breadCrumbNew .activeLink a").content.strip }
         | 
| 106 | 
            +
                    end
         | 
| 107 | 
            +
             | 
| 108 | 
            +
                    items '.productDetails > a' do
         | 
| 109 | 
            +
                      attributes do
         | 
| 110 | 
            +
                        {
         | 
| 111 | 
            +
                          :name => page.get_one('#name').content.strip,
         | 
| 112 | 
            +
                          :type => page.get_one('#type').content.strip,
         | 
| 113 | 
            +
                          :price => page.get_one('#price1').content.strip.sub('$','').to_f,
         | 
| 114 | 
            +
                          :salesArgs => page.get_one('#salesArg').content.strip,
         | 
| 115 | 
            +
                          :image => page.get_one('#productImg').attributes['src'].content,
         | 
| 116 | 
            +
                          :ikea_id => page.uri.to_s.match("^.*\/([0-9]+)\/?$").captures.first
         | 
| 117 | 
            +
                        }
         | 
| 118 | 
            +
                      end
         | 
| 62 119 | 
             
                    end
         | 
| 63 120 | 
             
                  end
         | 
| 64 121 | 
             
                end
         | 
| @@ -66,41 +123,55 @@ Storexplore::define_api 'dummy-store.com' do | |
| 66 123 | 
             
            end
         | 
| 67 124 | 
             
            ```
         | 
| 68 125 |  | 
| 69 | 
            -
            This  | 
| 70 | 
            -
             | 
| 71 | 
            -
            uri contains 'dummy-store.com'.
         | 
| 126 | 
            +
            This defines a hierarchical API on the IKEA store that will be used to browse any store which
         | 
| 127 | 
            +
            uri contains 'ikea.com/us'.
         | 
| 72 128 |  | 
| 73 129 | 
             
            Now here is how this API can be accessed to pretty print all its content:
         | 
| 74 130 |  | 
| 75 131 | 
             
            ```ruby
         | 
| 76 | 
            -
            Api.browse('http://www. | 
| 132 | 
            +
            Storexplore::Api.browse('http://www.ikea.com/us/en').categories.each do |category|
         | 
| 77 133 |  | 
| 78 | 
            -
              puts "category: #{category.title}"
         | 
| 134 | 
            +
              puts "category: #{category.title.strip}"
         | 
| 79 135 | 
             
              puts "attributes: #{category.attributes}"
         | 
| 80 136 |  | 
| 81 137 | 
             
              category.categories.each do |sub_category|
         | 
| 82 138 |  | 
| 83 | 
            -
                puts "  category: #{sub_category.title}"
         | 
| 139 | 
            +
                puts "  category: #{sub_category.title.strip}"
         | 
| 84 140 | 
             
                puts "  attributes: #{sub_category.attributes}"
         | 
| 85 141 |  | 
| 86 | 
            -
                sub_category. | 
| 142 | 
            +
                sub_category.categories.each do |sub_sub_category|
         | 
| 143 | 
            +
             | 
| 144 | 
            +
                  puts "    category: #{sub_sub_category.title.strip}"
         | 
| 145 | 
            +
                  puts "    attributes: #{sub_sub_category.attributes}"
         | 
| 146 | 
            +
             | 
| 147 | 
            +
                  sub_sub_category.items.each do |item|
         | 
| 87 148 |  | 
| 88 | 
            -
             | 
| 89 | 
            -
             | 
| 149 | 
            +
                    puts "      item: #{item.title.strip}"
         | 
| 150 | 
            +
                    puts "      attributes: #{item.attributes}"
         | 
| 90 151 |  | 
| 152 | 
            +
                  end
         | 
| 91 153 | 
             
                end
         | 
| 92 154 | 
             
              end
         | 
| 93 155 | 
             
            end
         | 
| 94 156 | 
             
            ```
         | 
| 95 157 |  | 
| 96 | 
            -
             | 
| 158 | 
            +
            (This sample can be found in samples/ikea.rb)
         | 
| 159 | 
            +
             | 
| 160 | 
            +
            ## Testing
         | 
| 161 | 
            +
             | 
| 162 | 
            +
            NOTE : please keep in mind that these testing utilities have been extracted from my first real use case ([auchandirect-scrAPI](https://github.com/philou/auchandirect-scrAPI)) and might still rely on assumptions coming from there. Any help cleaning this up is welcome.
         | 
| 163 | 
            +
             | 
| 164 | 
            +
            ### Testing Code Relying On A Scrapped Thirdparty
         | 
| 165 | 
            +
             | 
| 166 | 
            +
            This can be quite a challenge. Storeexplore can help you with that :
         | 
| 167 | 
            +
             | 
| 168 | 
            +
            * it provides a customizable offline (disk) dummy store generator
         | 
| 169 | 
            +
            * it provides an API for this store
         | 
| 170 | 
            +
            * As long as your dummy store provides the same attributes than the real store, you can use it in your tests
         | 
| 97 171 |  | 
| 98 | 
            -
             | 
| 99 | 
            -
            be generated to the file system using the Storexplore::Testing::DummyStore and
         | 
| 100 | 
            -
            Storexplore::Testing::DummyStoreGenerator classes. This is particularly useful
         | 
| 101 | 
            -
            while testing.
         | 
| 172 | 
            +
            Dummy stores can be generated to the file system using the Storexplore::Testing::DummyStore and Storexplore::Testing::DummyStoreGenerator classes.
         | 
| 102 173 |  | 
| 103 | 
            -
            To use it, add the following | 
| 174 | 
            +
            To use it, add the following to your spec_helper.rb for example :
         | 
| 104 175 |  | 
| 105 176 | 
             
            ```ruby
         | 
| 106 177 | 
             
            require 'storexplore/testing'
         | 
| @@ -118,7 +189,7 @@ DummyStore.wipe_out_store(store_name) | |
| 118 189 | 
             
            @store_generator.generate(3).categories.and(3).categories.and(item_count).items
         | 
| 119 190 | 
             
            ```
         | 
| 120 191 |  | 
| 121 | 
            -
             | 
| 192 | 
            +
            You can add custom elements with explicit values :
         | 
| 122 193 |  | 
| 123 194 | 
             
            ```ruby
         | 
| 124 195 | 
             
            @store_generator.
         | 
| @@ -132,10 +203,9 @@ Storexplore provides an api definition for dummy stores in | |
| 132 203 | 
             
            'storexplore/testing/dummy_store_api'. It can be required independently if
         | 
| 133 204 | 
             
            needed.
         | 
| 134 205 |  | 
| 135 | 
            -
            ###  | 
| 206 | 
            +
            ### Testing Your Own Scrapper
         | 
| 136 207 |  | 
| 137 | 
            -
            Storexplore also ships with an rspec shared examples macro. It  | 
| 138 | 
            -
            any custom store API definition.
         | 
| 208 | 
            +
            Storexplore also ships with an rspec shared examples macro. It guarantees basic scrapper well behaviour such as the presence of many categories, of item names and prices
         | 
| 139 209 |  | 
| 140 210 | 
             
            ```ruby
         | 
| 141 211 | 
             
            require 'storexplore/testing'
         | 
| @@ -150,7 +220,7 @@ describe "MyStoreApi" do | |
| 150 220 | 
             
            end
         | 
| 151 221 | 
             
            ```
         | 
| 152 222 |  | 
| 153 | 
            -
            ### Testing  | 
| 223 | 
            +
            ### Summary Testing Files To Require
         | 
| 154 224 |  | 
| 155 225 | 
             
            * To only get the api definition for a previously generated dummy store, it is enough to require 'storexplore/testing/dummy_store_api'
         | 
| 156 226 | 
             
            * To be able to generate and scrap dummy stores, it's needed to require 'storexplore/testing/dummy_store_generator'
         | 
    
        data/docker-compose.yml
    ADDED
    
    
    
        data/ikea.jpg
    ADDED
    
    | Binary file | 
| @@ -2,7 +2,7 @@ | |
| 2 2 | 
             
            #
         | 
| 3 3 | 
             
            # api_shared_examples.rb
         | 
| 4 4 | 
             
            #
         | 
| 5 | 
            -
            # Copyright (c) 2010-2014 by Philippe Bourgau. All rights reserved.
         | 
| 5 | 
            +
            # Copyright (c) 2010-2014, 2016 by Philippe Bourgau. All rights reserved.
         | 
| 6 6 | 
             
            #
         | 
| 7 7 | 
             
            # This library is free software; you can redistribute it and/or
         | 
| 8 8 | 
             
            # modify it under the terms of the GNU Lesser General Public
         | 
| @@ -51,7 +51,7 @@ module Storexplore | |
| 51 51 | 
             
                  end
         | 
| 52 52 |  | 
| 53 53 | 
             
                  it "should have items with a price" do
         | 
| 54 | 
            -
                    expect(sample_items_attributes).to  | 
| 54 | 
            +
                    expect(sample_items_attributes).to all(have_key(:price))
         | 
| 55 55 | 
             
                  end
         | 
| 56 56 |  | 
| 57 57 | 
             
                  it "should mostly have items with an image" do
         | 
| @@ -63,7 +63,7 @@ module Storexplore | |
| 63 63 | 
             
                  end
         | 
| 64 64 |  | 
| 65 65 | 
             
                  it "should have items with unique remote id" do
         | 
| 66 | 
            -
                    expect(sample_items_attributes).to  | 
| 66 | 
            +
                    expect(sample_items_attributes).to all(have_unique(:remote_id).in(sample_items_attributes))
         | 
| 67 67 | 
             
                  end
         | 
| 68 68 |  | 
| 69 69 | 
             
                  it "should have items with unique uris" do
         | 
    
        data/lib/storexplore/version.rb
    CHANGED
    
    | @@ -2,7 +2,7 @@ | |
| 2 2 | 
             
            #
         | 
| 3 3 | 
             
            # version.rb
         | 
| 4 4 | 
             
            #
         | 
| 5 | 
            -
            # Copyright (c) 2010-2014 by Philippe Bourgau. All rights reserved.
         | 
| 5 | 
            +
            # Copyright (c) 2010-2014, 2016 by Philippe Bourgau. All rights reserved.
         | 
| 6 6 | 
             
            #
         | 
| 7 7 | 
             
            # This library is free software; you can redistribute it and/or
         | 
| 8 8 | 
             
            # modify it under the terms of the GNU Lesser General Public
         | 
| @@ -20,5 +20,5 @@ | |
| 20 20 | 
             
            # MA 02110-1301  USA
         | 
| 21 21 |  | 
| 22 22 | 
             
            module Storexplore
         | 
| 23 | 
            -
              VERSION = "0. | 
| 23 | 
            +
              VERSION = "0.5.0"
         | 
| 24 24 | 
             
            end
         | 
    
        data/samples/ikea.rb
    ADDED
    
    | @@ -0,0 +1,84 @@ | |
| 1 | 
            +
            # -*- encoding: utf-8 -*-
         | 
| 2 | 
            +
            #
         | 
| 3 | 
            +
            # ikea.rb
         | 
| 4 | 
            +
            #
         | 
| 5 | 
            +
            # Copyright (c) 2015 by Philippe Bourgau. All rights reserved.
         | 
| 6 | 
            +
            #
         | 
| 7 | 
            +
            # This library is free software; you can redistribute it and/or
         | 
| 8 | 
            +
            # modify it under the terms of the GNU Lesser General Public
         | 
| 9 | 
            +
            # License as published by the Free Software Foundation; either
         | 
| 10 | 
            +
            # version 3.0 of the License, or (at your option) any later version.
         | 
| 11 | 
            +
            #
         | 
| 12 | 
            +
            # This library is distributed in the hope that it will be useful,
         | 
| 13 | 
            +
            # but WITHOUT ANY WARRANTY; without even the implied warranty of
         | 
| 14 | 
            +
            # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
         | 
| 15 | 
            +
            # Lesser General Public License for more details.
         | 
| 16 | 
            +
            #
         | 
| 17 | 
            +
            # You should have received a copy of the GNU Lesser General Public
         | 
| 18 | 
            +
            # License along with this library; if not, write to the Free Software
         | 
| 19 | 
            +
            # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
         | 
| 20 | 
            +
            # MA 02110-1301  USA
         | 
| 21 | 
            +
             | 
| 22 | 
            +
            # Please note that I don't intend to maintain this scrapper definition :)
         | 
| 23 | 
            +
             | 
| 24 | 
            +
            require_relative "../lib/storexplore"
         | 
| 25 | 
            +
             | 
| 26 | 
            +
            Storexplore::Api.define 'ikea.com/us' do
         | 
| 27 | 
            +
             | 
| 28 | 
            +
              categories '.departmentLinkBlock a' do
         | 
| 29 | 
            +
                attributes do
         | 
| 30 | 
            +
                  { :name => page.get_one("#breadCrumbNew .activeLink a").content.strip }
         | 
| 31 | 
            +
                end
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                categories '.departmentLinks a' do
         | 
| 34 | 
            +
                  attributes do
         | 
| 35 | 
            +
                    { :name => page.get_one("#breadCrumbNew .activeLink a").content.strip }
         | 
| 36 | 
            +
                  end
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                  categories 'a.categoryName' do
         | 
| 39 | 
            +
                    attributes do
         | 
| 40 | 
            +
                      { :name => page.get_one("#breadCrumbNew .activeLink a").content.strip }
         | 
| 41 | 
            +
                    end
         | 
| 42 | 
            +
             | 
| 43 | 
            +
                    items '.productDetails > a' do
         | 
| 44 | 
            +
                      attributes do
         | 
| 45 | 
            +
                        {
         | 
| 46 | 
            +
                          :name => page.get_one('#name').content.strip,
         | 
| 47 | 
            +
                          :type => page.get_one('#type').content.strip,
         | 
| 48 | 
            +
                          :price => page.get_one('#price1').content.strip.sub('$','').to_f,
         | 
| 49 | 
            +
                          :salesArgs => page.get_one('#salesArg').content.strip,
         | 
| 50 | 
            +
                          :image => page.get_one('#productImg').attributes['src'].content,
         | 
| 51 | 
            +
                          :ikea_id => page.uri.to_s.match("^.*\/([0-9]+)\/?$").captures.first
         | 
| 52 | 
            +
                        }
         | 
| 53 | 
            +
                      end
         | 
| 54 | 
            +
                    end
         | 
| 55 | 
            +
                  end
         | 
| 56 | 
            +
                end
         | 
| 57 | 
            +
              end
         | 
| 58 | 
            +
            end
         | 
| 59 | 
            +
             | 
| 60 | 
            +
             | 
| 61 | 
            +
            Storexplore::Api.browse('http://www.ikea.com/us/en').categories.take(1).each do |category|
         | 
| 62 | 
            +
             | 
| 63 | 
            +
              puts "category: #{category.title.strip}"
         | 
| 64 | 
            +
              puts "attributes: #{category.attributes}"
         | 
| 65 | 
            +
             | 
| 66 | 
            +
              category.categories.take(2).each do |sub_category|
         | 
| 67 | 
            +
             | 
| 68 | 
            +
                puts "  category: #{sub_category.title.strip}"
         | 
| 69 | 
            +
                puts "  attributes: #{sub_category.attributes}"
         | 
| 70 | 
            +
             | 
| 71 | 
            +
                sub_category.categories.take(3).each do |sub_sub_category|
         | 
| 72 | 
            +
             | 
| 73 | 
            +
                  puts "    category: #{sub_sub_category.title.strip}"
         | 
| 74 | 
            +
                  puts "    attributes: #{sub_sub_category.attributes}"
         | 
| 75 | 
            +
             | 
| 76 | 
            +
                  sub_sub_category.items.take(4).each do |item|
         | 
| 77 | 
            +
             | 
| 78 | 
            +
                    puts "      item: #{item.title.strip}"
         | 
| 79 | 
            +
                    puts "      attributes: #{item.attributes}"
         | 
| 80 | 
            +
             | 
| 81 | 
            +
                  end
         | 
| 82 | 
            +
                end
         | 
| 83 | 
            +
              end
         | 
| 84 | 
            +
            end
         | 
| @@ -2,7 +2,7 @@ | |
| 2 2 | 
             
            #
         | 
| 3 3 | 
             
            # memory_usage_spec.rb
         | 
| 4 4 | 
             
            #
         | 
| 5 | 
            -
            # Copyright (c) 2011- | 
| 5 | 
            +
            # Copyright (c) 2011-2015 by Philippe Bourgau. All rights reserved.
         | 
| 6 6 | 
             
            #
         | 
| 7 7 | 
             
            # This library is free software; you can redistribute it and/or
         | 
| 8 8 | 
             
            # modify it under the terms of the GNU Lesser General Public
         | 
| @@ -89,6 +89,7 @@ module Storexplore | |
| 89 89 | 
             
                  end
         | 
| 90 90 |  | 
| 91 91 | 
             
                  def current_living_objects
         | 
| 92 | 
            +
                    GC.start
         | 
| 92 93 | 
             
                    object_counts = ObjectSpace.count_objects
         | 
| 93 94 | 
             
                    object_counts[:TOTAL] - object_counts[:FREE]
         | 
| 94 95 | 
             
                  end
         | 
| @@ -2,7 +2,7 @@ | |
| 2 2 | 
             
            #
         | 
| 3 3 | 
             
            # walker_page_spec.rb
         | 
| 4 4 | 
             
            #
         | 
| 5 | 
            -
            # Copyright (c) 2011-2014 by Philippe Bourgau. All rights reserved.
         | 
| 5 | 
            +
            # Copyright (c) 2011-2014, 2016 by Philippe Bourgau. All rights reserved.
         | 
| 6 6 | 
             
            #
         | 
| 7 7 | 
             
            # This library is free software; you can redistribute it and/or
         | 
| 8 8 | 
             
            # modify it under the terms of the GNU Lesser General Public
         | 
| @@ -97,7 +97,7 @@ module Storexplore | |
| 97 97 | 
             
                  end
         | 
| 98 98 |  | 
| 99 99 | 
             
                  it "links to other instances of WalkerPage" do
         | 
| 100 | 
            -
                    expect(@page.search_links("#myself").map { |link| link.get }).to  | 
| 100 | 
            +
                    expect(@page.search_links("#myself").map { |link| link.get }).to all(be_instance_of(WalkerPage))
         | 
| 101 101 | 
             
                  end
         | 
| 102 102 |  | 
| 103 103 | 
             
                  it "knows the text of the links" do
         | 
    
        data/spec/spec_helper.rb
    CHANGED
    
    | @@ -2,7 +2,7 @@ | |
| 2 2 | 
             
            #
         | 
| 3 3 | 
             
            # spec_helper.rb
         | 
| 4 4 | 
             
            #
         | 
| 5 | 
            -
            # Copyright (c) 2013-2014 by Philippe Bourgau. All rights reserved.
         | 
| 5 | 
            +
            # Copyright (c) 2013-2014, 2016 by Philippe Bourgau. All rights reserved.
         | 
| 6 6 | 
             
            #
         | 
| 7 7 | 
             
            # This library is free software; you can redistribute it and/or
         | 
| 8 8 | 
             
            # modify it under the terms of the GNU Lesser General Public
         | 
| @@ -19,12 +19,11 @@ | |
| 19 19 | 
             
            # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
         | 
| 20 20 | 
             
            # MA 02110-1301  USA
         | 
| 21 21 |  | 
| 22 | 
            -
            require ' | 
| 23 | 
            -
             | 
| 22 | 
            +
            require 'simplecov'
         | 
| 23 | 
            +
            SimpleCov.start
         | 
| 24 24 |  | 
| 25 25 | 
             
            require 'fakeweb'
         | 
| 26 26 | 
             
            require 'rspec/collection_matchers'
         | 
| 27 | 
            -
            require 'spec_combos'
         | 
| 28 27 | 
             
            require 'storexplore'
         | 
| 29 28 | 
             
            require 'storexplore/testing'
         | 
| 30 29 |  | 
    
        data/storexplore.gemspec
    CHANGED
    
    | @@ -23,8 +23,8 @@ Gem::Specification.new do |spec| | |
| 23 23 | 
             
              spec.add_development_dependency "bundler"
         | 
| 24 24 | 
             
              spec.add_development_dependency "rake"
         | 
| 25 25 | 
             
              spec.add_development_dependency "guard-rspec"
         | 
| 26 | 
            -
              spec.add_development_dependency "spec_combos"
         | 
| 27 26 | 
             
              spec.add_development_dependency "fakeweb"
         | 
| 28 27 | 
             
              spec.add_development_dependency "rspec-collection_matchers"
         | 
| 28 | 
            +
              spec.add_development_dependency "simplecov"
         | 
| 29 29 | 
             
              spec.add_development_dependency "codeclimate-test-reporter"
         | 
| 30 30 | 
             
            end
         | 
    
        metadata
    CHANGED
    
    | @@ -1,146 +1,125 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: storexplore
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 0. | 
| 4 | 
            +
              version: 0.5.0
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Philou
         | 
| 8 8 | 
             
            autorequire: 
         | 
| 9 9 | 
             
            bindir: bin
         | 
| 10 | 
            -
            cert_chain:
         | 
| 11 | 
            -
            -  | 
| 12 | 
            -
              -----BEGIN CERTIFICATE-----
         | 
| 13 | 
            -
              MIIDQjCCAiqgAwIBAgIBAjANBgkqhkiG9w0BAQUFADBHMRkwFwYDVQQDDBBwaGls
         | 
| 14 | 
            -
              aXBwZS5ib3VyZ2F1MRUwEwYKCZImiZPyLGQBGRYFZ21haWwxEzARBgoJkiaJk/Is
         | 
| 15 | 
            -
              ZAEZFgNjb20wHhcNMTQxMTA0MDUwNTA4WhcNMTUxMTA0MDUwNTA4WjBHMRkwFwYD
         | 
| 16 | 
            -
              VQQDDBBwaGlsaXBwZS5ib3VyZ2F1MRUwEwYKCZImiZPyLGQBGRYFZ21haWwxEzAR
         | 
| 17 | 
            -
              BgoJkiaJk/IsZAEZFgNjb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
         | 
| 18 | 
            -
              AQC8CpoqZwEbzXr55EUxdSplgn0MYZ9xPGO/XmRa8bD63n+JYWF0AS+mj452ZY18
         | 
| 19 | 
            -
              rwM+yKrKhtsA+aJJdlOafgIUnY5SrZOr7v7kgc6T2YNoUj+M00Um2jv+shQbOtV6
         | 
| 20 | 
            -
              qGp0Jw1HfPNUMVa+3pXZyAGCecN6rTnsZJIuW6KNaJUq6lEMVXanoTHgAKrH5aHd
         | 
| 21 | 
            -
              Y6ofwQL86d6LDkC1S4p86iMUWvF34w8h5ItVo+JKlPRR22rzsK/ZKgNH3lfjbS6i
         | 
| 22 | 
            -
              JWqPva70rL2xz5kCVn6DL7XhNZtqnAO4kvCQyQeWezvcoGXEnbHacKky7B+/WKec
         | 
| 23 | 
            -
              OIWEwedl6j+X0OD5OYki3QaTAgMBAAGjOTA3MAkGA1UdEwQCMAAwCwYDVR0PBAQD
         | 
| 24 | 
            -
              AgSwMB0GA1UdDgQWBBSZy8+NDp7v3X6S0oqVORYE8uli5jANBgkqhkiG9w0BAQUF
         | 
| 25 | 
            -
              AAOCAQEAjTpuKjUclY0kNioKIcwD9nEkAwxeGiMNrY2zc7FfabSVhNKa0GafjHiz
         | 
| 26 | 
            -
              6STN4S4j03wgmD8YMnA6uqQBSCgb3mcySr9sfglxub4sC9c3Ma+A3G7JEE+KPeLE
         | 
| 27 | 
            -
              gcVYqRPSgVV5naFYaFu4HK0o8ZY8smY30BM3uFmW3cQ9cTKNtaqgUQ177fMmXuYa
         | 
| 28 | 
            -
              QOTMg5o5t1YFdpg/RwQNoUX5DyAC8gWJY8vlVJbiIKvvB0z4+o7d5nEuuE3jvKQr
         | 
| 29 | 
            -
              LKjGZUoe+A3/FKYHWLPZAArCXrXLhpjAfopLBNmOAju0e30ObcjPQDWcj7z5jfJl
         | 
| 30 | 
            -
              ILiFgo6hdHrp/tFaDv14PiMRm6sZaQ==
         | 
| 31 | 
            -
              -----END CERTIFICATE-----
         | 
| 32 | 
            -
            date: 2014-11-05 00:00:00.000000000 Z
         | 
| 10 | 
            +
            cert_chain: []
         | 
| 11 | 
            +
            date: 2016-12-17 00:00:00.000000000 Z
         | 
| 33 12 | 
             
            dependencies:
         | 
| 34 13 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 35 14 | 
             
              name: mechanize
         | 
| 36 15 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| 37 16 | 
             
                requirements:
         | 
| 38 | 
            -
                - - ~>
         | 
| 17 | 
            +
                - - "~>"
         | 
| 39 18 | 
             
                  - !ruby/object:Gem::Version
         | 
| 40 19 | 
             
                    version: '2.0'
         | 
| 41 20 | 
             
              type: :runtime
         | 
| 42 21 | 
             
              prerelease: false
         | 
| 43 22 | 
             
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 44 23 | 
             
                requirements:
         | 
| 45 | 
            -
                - - ~>
         | 
| 24 | 
            +
                - - "~>"
         | 
| 46 25 | 
             
                  - !ruby/object:Gem::Version
         | 
| 47 26 | 
             
                    version: '2.0'
         | 
| 48 27 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 49 28 | 
             
              name: bundler
         | 
| 50 29 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| 51 30 | 
             
                requirements:
         | 
| 52 | 
            -
                - -  | 
| 31 | 
            +
                - - ">="
         | 
| 53 32 | 
             
                  - !ruby/object:Gem::Version
         | 
| 54 33 | 
             
                    version: '0'
         | 
| 55 34 | 
             
              type: :development
         | 
| 56 35 | 
             
              prerelease: false
         | 
| 57 36 | 
             
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 58 37 | 
             
                requirements:
         | 
| 59 | 
            -
                - -  | 
| 38 | 
            +
                - - ">="
         | 
| 60 39 | 
             
                  - !ruby/object:Gem::Version
         | 
| 61 40 | 
             
                    version: '0'
         | 
| 62 41 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 63 42 | 
             
              name: rake
         | 
| 64 43 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| 65 44 | 
             
                requirements:
         | 
| 66 | 
            -
                - -  | 
| 45 | 
            +
                - - ">="
         | 
| 67 46 | 
             
                  - !ruby/object:Gem::Version
         | 
| 68 47 | 
             
                    version: '0'
         | 
| 69 48 | 
             
              type: :development
         | 
| 70 49 | 
             
              prerelease: false
         | 
| 71 50 | 
             
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 72 51 | 
             
                requirements:
         | 
| 73 | 
            -
                - -  | 
| 52 | 
            +
                - - ">="
         | 
| 74 53 | 
             
                  - !ruby/object:Gem::Version
         | 
| 75 54 | 
             
                    version: '0'
         | 
| 76 55 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 77 56 | 
             
              name: guard-rspec
         | 
| 78 57 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| 79 58 | 
             
                requirements:
         | 
| 80 | 
            -
                - -  | 
| 59 | 
            +
                - - ">="
         | 
| 81 60 | 
             
                  - !ruby/object:Gem::Version
         | 
| 82 61 | 
             
                    version: '0'
         | 
| 83 62 | 
             
              type: :development
         | 
| 84 63 | 
             
              prerelease: false
         | 
| 85 64 | 
             
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 86 65 | 
             
                requirements:
         | 
| 87 | 
            -
                - -  | 
| 66 | 
            +
                - - ">="
         | 
| 88 67 | 
             
                  - !ruby/object:Gem::Version
         | 
| 89 68 | 
             
                    version: '0'
         | 
| 90 69 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 91 | 
            -
              name:  | 
| 70 | 
            +
              name: fakeweb
         | 
| 92 71 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| 93 72 | 
             
                requirements:
         | 
| 94 | 
            -
                - -  | 
| 73 | 
            +
                - - ">="
         | 
| 95 74 | 
             
                  - !ruby/object:Gem::Version
         | 
| 96 75 | 
             
                    version: '0'
         | 
| 97 76 | 
             
              type: :development
         | 
| 98 77 | 
             
              prerelease: false
         | 
| 99 78 | 
             
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 100 79 | 
             
                requirements:
         | 
| 101 | 
            -
                - -  | 
| 80 | 
            +
                - - ">="
         | 
| 102 81 | 
             
                  - !ruby/object:Gem::Version
         | 
| 103 82 | 
             
                    version: '0'
         | 
| 104 83 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 105 | 
            -
              name:  | 
| 84 | 
            +
              name: rspec-collection_matchers
         | 
| 106 85 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| 107 86 | 
             
                requirements:
         | 
| 108 | 
            -
                - -  | 
| 87 | 
            +
                - - ">="
         | 
| 109 88 | 
             
                  - !ruby/object:Gem::Version
         | 
| 110 89 | 
             
                    version: '0'
         | 
| 111 90 | 
             
              type: :development
         | 
| 112 91 | 
             
              prerelease: false
         | 
| 113 92 | 
             
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 114 93 | 
             
                requirements:
         | 
| 115 | 
            -
                - -  | 
| 94 | 
            +
                - - ">="
         | 
| 116 95 | 
             
                  - !ruby/object:Gem::Version
         | 
| 117 96 | 
             
                    version: '0'
         | 
| 118 97 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 119 | 
            -
              name:  | 
| 98 | 
            +
              name: simplecov
         | 
| 120 99 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| 121 100 | 
             
                requirements:
         | 
| 122 | 
            -
                - -  | 
| 101 | 
            +
                - - ">="
         | 
| 123 102 | 
             
                  - !ruby/object:Gem::Version
         | 
| 124 103 | 
             
                    version: '0'
         | 
| 125 104 | 
             
              type: :development
         | 
| 126 105 | 
             
              prerelease: false
         | 
| 127 106 | 
             
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 128 107 | 
             
                requirements:
         | 
| 129 | 
            -
                - -  | 
| 108 | 
            +
                - - ">="
         | 
| 130 109 | 
             
                  - !ruby/object:Gem::Version
         | 
| 131 110 | 
             
                    version: '0'
         | 
| 132 111 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 133 112 | 
             
              name: codeclimate-test-reporter
         | 
| 134 113 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| 135 114 | 
             
                requirements:
         | 
| 136 | 
            -
                - -  | 
| 115 | 
            +
                - - ">="
         | 
| 137 116 | 
             
                  - !ruby/object:Gem::Version
         | 
| 138 117 | 
             
                    version: '0'
         | 
| 139 118 | 
             
              type: :development
         | 
| 140 119 | 
             
              prerelease: false
         | 
| 141 120 | 
             
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 142 121 | 
             
                requirements:
         | 
| 143 | 
            -
                - -  | 
| 122 | 
            +
                - - ">="
         | 
| 144 123 | 
             
                  - !ruby/object:Gem::Version
         | 
| 145 124 | 
             
                    version: '0'
         | 
| 146 125 | 
             
            description: A declarative scrapping DSL that lets one define directory like apis
         | 
| @@ -151,14 +130,16 @@ executables: [] | |
| 151 130 | 
             
            extensions: []
         | 
| 152 131 | 
             
            extra_rdoc_files: []
         | 
| 153 132 | 
             
            files:
         | 
| 154 | 
            -
            - .gitignore
         | 
| 155 | 
            -
            - .rspec
         | 
| 156 | 
            -
            - .travis.yml
         | 
| 133 | 
            +
            - ".gitignore"
         | 
| 134 | 
            +
            - ".rspec"
         | 
| 135 | 
            +
            - ".travis.yml"
         | 
| 157 136 | 
             
            - Gemfile
         | 
| 158 137 | 
             
            - Guardfile
         | 
| 159 138 | 
             
            - LICENSE
         | 
| 160 139 | 
             
            - README.md
         | 
| 161 140 | 
             
            - Rakefile
         | 
| 141 | 
            +
            - docker-compose.yml
         | 
| 142 | 
            +
            - ikea.jpg
         | 
| 162 143 | 
             
            - lib/storexplore.rb
         | 
| 163 144 | 
             
            - lib/storexplore/api.rb
         | 
| 164 145 | 
             
            - lib/storexplore/array_utils.rb
         | 
| @@ -183,6 +164,7 @@ files: | |
| 183 164 | 
             
            - lib/storexplore/walker.rb
         | 
| 184 165 | 
             
            - lib/storexplore/walker_page.rb
         | 
| 185 166 | 
             
            - lib/storexplore/walker_page_error.rb
         | 
| 167 | 
            +
            - samples/ikea.rb
         | 
| 186 168 | 
             
            - spec/lib/storexplore/api_spec.rb
         | 
| 187 169 | 
             
            - spec/lib/storexplore/dsl_spec.rb
         | 
| 188 170 | 
             
            - spec/lib/storexplore/store_walker_page_spec_fixture.html
         | 
| @@ -203,17 +185,17 @@ require_paths: | |
| 203 185 | 
             
            - lib
         | 
| 204 186 | 
             
            required_ruby_version: !ruby/object:Gem::Requirement
         | 
| 205 187 | 
             
              requirements:
         | 
| 206 | 
            -
              - -  | 
| 188 | 
            +
              - - ">="
         | 
| 207 189 | 
             
                - !ruby/object:Gem::Version
         | 
| 208 190 | 
             
                  version: '0'
         | 
| 209 191 | 
             
            required_rubygems_version: !ruby/object:Gem::Requirement
         | 
| 210 192 | 
             
              requirements:
         | 
| 211 | 
            -
              - -  | 
| 193 | 
            +
              - - ">="
         | 
| 212 194 | 
             
                - !ruby/object:Gem::Version
         | 
| 213 195 | 
             
                  version: '0'
         | 
| 214 196 | 
             
            requirements: []
         | 
| 215 197 | 
             
            rubyforge_project: 
         | 
| 216 | 
            -
            rubygems_version: 2. | 
| 198 | 
            +
            rubygems_version: 2.6.8
         | 
| 217 199 | 
             
            signing_key: 
         | 
| 218 200 | 
             
            specification_version: 4
         | 
| 219 201 | 
             
            summary: Online store scraping library
         | 
    
        checksums.yaml.gz.sig
    DELETED
    
    | Binary file | 
    
        data.tar.gz.sig
    DELETED
    
    | Binary file | 
    
        metadata.gz.sig
    DELETED
    
    | Binary file |