async_storage 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 +7 -0
 - data/.gitignore +11 -0
 - data/.rspec +3 -0
 - data/.travis.yml +6 -0
 - data/Gemfile +13 -0
 - data/Gemfile.lock +50 -0
 - data/LICENSE.txt +21 -0
 - data/README.md +94 -0
 - data/Rakefile +8 -0
 - data/async_storage.gemspec +32 -0
 - data/bin/console +21 -0
 - data/bin/setup +8 -0
 - data/lib/async_storage.rb +35 -0
 - data/lib/async_storage/bath_actions.rb +19 -0
 - data/lib/async_storage/config.rb +84 -0
 - data/lib/async_storage/json.rb +42 -0
 - data/lib/async_storage/naming.rb +74 -0
 - data/lib/async_storage/redis_pool.rb +30 -0
 - data/lib/async_storage/repo.rb +196 -0
 - data/lib/async_storage/util/strings.rb +18 -0
 - data/lib/async_storage/version.rb +5 -0
 - metadata +94 -0
 
    
        checksums.yaml
    ADDED
    
    | 
         @@ -0,0 +1,7 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            ---
         
     | 
| 
      
 2 
     | 
    
         
            +
            SHA256:
         
     | 
| 
      
 3 
     | 
    
         
            +
              metadata.gz: ffa57bbe53ad587cdd628ce556aaf679ed9b4ff02f5a9b8c4217be4b6ffa495d
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: 5557aa324800a20d599a94ddcbf21894f8ffa3ccc97672f7e0329d5283090d84
         
     | 
| 
      
 5 
     | 
    
         
            +
            SHA512:
         
     | 
| 
      
 6 
     | 
    
         
            +
              metadata.gz: f6f33f4f1d4728c5933c7b312daa602a14eb2408f40168a8b5a3f490e8853e1b84464cc698fb6a3c6721f4bc0b72fb1dae0c4341bec2c481ce46f1acb5128718
         
     | 
| 
      
 7 
     | 
    
         
            +
              data.tar.gz: 0d976a57d701639a1e46b3f9d236b3e983ac6a652e96e6ac481475f1293326251e0f4e1045ed945027ed6cd26f552fa9ec5793b2337fa6179ebc380f6fb42651
         
     | 
    
        data/.gitignore
    ADDED
    
    
    
        data/.rspec
    ADDED
    
    
    
        data/.travis.yml
    ADDED
    
    
    
        data/Gemfile
    ADDED
    
    | 
         @@ -0,0 +1,13 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            source 'https://rubygems.org'
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            # Specify your gem's dependencies in async_storage.gemspec
         
     | 
| 
      
 6 
     | 
    
         
            +
            gemspec
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
            gem 'rake', '~> 12.0'
         
     | 
| 
      
 9 
     | 
    
         
            +
            gem 'rspec', '~> 3.0'
         
     | 
| 
      
 10 
     | 
    
         
            +
            gem 'connection_pool', '~> 2.2.3'
         
     | 
| 
      
 11 
     | 
    
         
            +
            gem 'pry', '~> 0.13.1'
         
     | 
| 
      
 12 
     | 
    
         
            +
            gem 'awesome_print', '~> 1.8.0'
         
     | 
| 
      
 13 
     | 
    
         
            +
            gem 'dotenv', '~> 2.7.6'
         
     | 
    
        data/Gemfile.lock
    ADDED
    
    | 
         @@ -0,0 +1,50 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            PATH
         
     | 
| 
      
 2 
     | 
    
         
            +
              remote: .
         
     | 
| 
      
 3 
     | 
    
         
            +
              specs:
         
     | 
| 
      
 4 
     | 
    
         
            +
                async_storage (0.1.0)
         
     | 
| 
      
 5 
     | 
    
         
            +
                  multi_json (> 0.0.0)
         
     | 
| 
      
 6 
     | 
    
         
            +
                  redis (> 0.0.0)
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
            GEM
         
     | 
| 
      
 9 
     | 
    
         
            +
              remote: https://rubygems.org/
         
     | 
| 
      
 10 
     | 
    
         
            +
              specs:
         
     | 
| 
      
 11 
     | 
    
         
            +
                awesome_print (1.8.0)
         
     | 
| 
      
 12 
     | 
    
         
            +
                coderay (1.1.3)
         
     | 
| 
      
 13 
     | 
    
         
            +
                connection_pool (2.2.3)
         
     | 
| 
      
 14 
     | 
    
         
            +
                diff-lcs (1.4.4)
         
     | 
| 
      
 15 
     | 
    
         
            +
                dotenv (2.7.6)
         
     | 
| 
      
 16 
     | 
    
         
            +
                method_source (1.0.0)
         
     | 
| 
      
 17 
     | 
    
         
            +
                multi_json (1.15.0)
         
     | 
| 
      
 18 
     | 
    
         
            +
                pry (0.13.1)
         
     | 
| 
      
 19 
     | 
    
         
            +
                  coderay (~> 1.1)
         
     | 
| 
      
 20 
     | 
    
         
            +
                  method_source (~> 1.0)
         
     | 
| 
      
 21 
     | 
    
         
            +
                rake (12.3.3)
         
     | 
| 
      
 22 
     | 
    
         
            +
                redis (4.2.2)
         
     | 
| 
      
 23 
     | 
    
         
            +
                rspec (3.10.0)
         
     | 
| 
      
 24 
     | 
    
         
            +
                  rspec-core (~> 3.10.0)
         
     | 
| 
      
 25 
     | 
    
         
            +
                  rspec-expectations (~> 3.10.0)
         
     | 
| 
      
 26 
     | 
    
         
            +
                  rspec-mocks (~> 3.10.0)
         
     | 
| 
      
 27 
     | 
    
         
            +
                rspec-core (3.10.0)
         
     | 
| 
      
 28 
     | 
    
         
            +
                  rspec-support (~> 3.10.0)
         
     | 
| 
      
 29 
     | 
    
         
            +
                rspec-expectations (3.10.0)
         
     | 
| 
      
 30 
     | 
    
         
            +
                  diff-lcs (>= 1.2.0, < 2.0)
         
     | 
| 
      
 31 
     | 
    
         
            +
                  rspec-support (~> 3.10.0)
         
     | 
| 
      
 32 
     | 
    
         
            +
                rspec-mocks (3.10.0)
         
     | 
| 
      
 33 
     | 
    
         
            +
                  diff-lcs (>= 1.2.0, < 2.0)
         
     | 
| 
      
 34 
     | 
    
         
            +
                  rspec-support (~> 3.10.0)
         
     | 
| 
      
 35 
     | 
    
         
            +
                rspec-support (3.10.0)
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
            PLATFORMS
         
     | 
| 
      
 38 
     | 
    
         
            +
              ruby
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
            DEPENDENCIES
         
     | 
| 
      
 41 
     | 
    
         
            +
              async_storage!
         
     | 
| 
      
 42 
     | 
    
         
            +
              awesome_print (~> 1.8.0)
         
     | 
| 
      
 43 
     | 
    
         
            +
              connection_pool (~> 2.2.3)
         
     | 
| 
      
 44 
     | 
    
         
            +
              dotenv (~> 2.7.6)
         
     | 
| 
      
 45 
     | 
    
         
            +
              pry (~> 0.13.1)
         
     | 
| 
      
 46 
     | 
    
         
            +
              rake (~> 12.0)
         
     | 
| 
      
 47 
     | 
    
         
            +
              rspec (~> 3.0)
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
            BUNDLED WITH
         
     | 
| 
      
 50 
     | 
    
         
            +
               2.1.4
         
     | 
    
        data/LICENSE.txt
    ADDED
    
    | 
         @@ -0,0 +1,21 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            The MIT License (MIT)
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            Copyright (c) 2020 Marcos G. Zimmermann
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            Permission is hereby granted, free of charge, to any person obtaining a copy
         
     | 
| 
      
 6 
     | 
    
         
            +
            of this software and associated documentation files (the "Software"), to deal
         
     | 
| 
      
 7 
     | 
    
         
            +
            in the Software without restriction, including without limitation the rights
         
     | 
| 
      
 8 
     | 
    
         
            +
            to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
         
     | 
| 
      
 9 
     | 
    
         
            +
            copies of the Software, and to permit persons to whom the Software is
         
     | 
| 
      
 10 
     | 
    
         
            +
            furnished to do so, subject to the following conditions:
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
            The above copyright notice and this permission notice shall be included in
         
     | 
| 
      
 13 
     | 
    
         
            +
            all copies or substantial portions of the Software.
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
            THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
         
     | 
| 
      
 16 
     | 
    
         
            +
            IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
         
     | 
| 
      
 17 
     | 
    
         
            +
            FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
         
     | 
| 
      
 18 
     | 
    
         
            +
            AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
         
     | 
| 
      
 19 
     | 
    
         
            +
            LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
         
     | 
| 
      
 20 
     | 
    
         
            +
            OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
         
     | 
| 
      
 21 
     | 
    
         
            +
            THE SOFTWARE.
         
     | 
    
        data/README.md
    ADDED
    
    | 
         @@ -0,0 +1,94 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # AsyncStorage
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/async_storage`. To experiment with that code, run `bin/console` for an interactive prompt.
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            TODO: Delete this and the text above, and describe your gem
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
            ## Installation
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
            Add this line to your application's Gemfile:
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 12 
     | 
    
         
            +
            gem 'async_storage'
         
     | 
| 
      
 13 
     | 
    
         
            +
            ```
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
            And then execute:
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                $ bundle install
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
            Or install it yourself as:
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                $ gem install async_storage
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
            ## Usage
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
            ** Idea about the API of this gem. Update accordingly before release it
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
            Define global configurations
         
     | 
| 
      
 28 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 29 
     | 
    
         
            +
            # Configurations
         
     | 
| 
      
 30 
     | 
    
         
            +
            AsyncStorage.configuration do |config|
         
     | 
| 
      
 31 
     | 
    
         
            +
              config.redis = ConnectionPool.new(size: 10, timeout: 1) do
         
     | 
| 
      
 32 
     | 
    
         
            +
                Redis.new(url: ENV.fetch('REDIS_URL', 'redis://0.0.0.0:6379'))
         
     | 
| 
      
 33 
     | 
    
         
            +
              end
         
     | 
| 
      
 34 
     | 
    
         
            +
              config.namespace = 'async_storage'  # Default to 'async_storage'
         
     | 
| 
      
 35 
     | 
    
         
            +
              config.expires_in = 3_600           # Default to nil
         
     | 
| 
      
 36 
     | 
    
         
            +
            end
         
     | 
| 
      
 37 
     | 
    
         
            +
            ```
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
            Useful methods to get, set and check data
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 42 
     | 
    
         
            +
            # app/resolvers/user_tweets_resolver.rb
         
     | 
| 
      
 43 
     | 
    
         
            +
            class UserTweetsResolver
         
     | 
| 
      
 44 
     | 
    
         
            +
              def call(user_id)
         
     | 
| 
      
 45 
     | 
    
         
            +
                # Return JSON friendly object
         
     | 
| 
      
 46 
     | 
    
         
            +
                { 'user_id' => user_id, 'tweets' => Twitter::API.tweets(user_id).as_json }
         
     | 
| 
      
 47 
     | 
    
         
            +
              end
         
     | 
| 
      
 48 
     | 
    
         
            +
            end
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
            AsyncStorage[UserTweetResolver].get('123') # Try to retrieve data. If does not exist enqueue a Background Job and return nil
         
     | 
| 
      
 51 
     | 
    
         
            +
            AsyncStorage[UserTweetResolver].get!('123') # Try to retrieve data. If does not exist imediate call the Resolver and return data
         
     | 
| 
      
 52 
     | 
    
         
            +
            AsyncStorage[UserTweetResolver, namespace: current_site.id].get(9) # Create a new Set using site id namepace
         
     | 
| 
      
 53 
     | 
    
         
            +
            AsyncStorage[UserTweetResolver, expires_in: 60].get(9) # Overwrite global expires_in
         
     | 
| 
      
 54 
     | 
    
         
            +
            ```
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
      
 56 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 57 
     | 
    
         
            +
            class Site
         
     | 
| 
      
 58 
     | 
    
         
            +
              # site.cache.user_tweets.get(@user.id)
         
     | 
| 
      
 59 
     | 
    
         
            +
              def cache
         
     | 
| 
      
 60 
     | 
    
         
            +
                Cache.new(self.slug)
         
     | 
| 
      
 61 
     | 
    
         
            +
              end
         
     | 
| 
      
 62 
     | 
    
         
            +
             
     | 
| 
      
 63 
     | 
    
         
            +
              class Cache
         
     | 
| 
      
 64 
     | 
    
         
            +
                RESOLVERS = {
         
     | 
| 
      
 65 
     | 
    
         
            +
                  user_tweets: UserTweetsResolver,
         
     | 
| 
      
 66 
     | 
    
         
            +
                }.freeze
         
     | 
| 
      
 67 
     | 
    
         
            +
             
     | 
| 
      
 68 
     | 
    
         
            +
                RESOLVERS.each do |method, resolver|
         
     | 
| 
      
 69 
     | 
    
         
            +
                  define_method method do
         
     | 
| 
      
 70 
     | 
    
         
            +
                    AsyncStorage[resolver, namespace: @namespace]
         
     | 
| 
      
 71 
     | 
    
         
            +
                  end
         
     | 
| 
      
 72 
     | 
    
         
            +
                end
         
     | 
| 
      
 73 
     | 
    
         
            +
             
     | 
| 
      
 74 
     | 
    
         
            +
                def initialize(namespace)
         
     | 
| 
      
 75 
     | 
    
         
            +
                  @namespace = namespace
         
     | 
| 
      
 76 
     | 
    
         
            +
                end
         
     | 
| 
      
 77 
     | 
    
         
            +
              end
         
     | 
| 
      
 78 
     | 
    
         
            +
            end
         
     | 
| 
      
 79 
     | 
    
         
            +
            ```
         
     | 
| 
      
 80 
     | 
    
         
            +
             
     | 
| 
      
 81 
     | 
    
         
            +
            ## Development
         
     | 
| 
      
 82 
     | 
    
         
            +
             
     | 
| 
      
 83 
     | 
    
         
            +
            After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
         
     | 
| 
      
 84 
     | 
    
         
            +
             
     | 
| 
      
 85 
     | 
    
         
            +
            To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
         
     | 
| 
      
 86 
     | 
    
         
            +
             
     | 
| 
      
 87 
     | 
    
         
            +
            ## Contributing
         
     | 
| 
      
 88 
     | 
    
         
            +
             
     | 
| 
      
 89 
     | 
    
         
            +
            Bug reports and pull requests are welcome on GitHub at https://github.com/marcosgz/async_storage.
         
     | 
| 
      
 90 
     | 
    
         
            +
             
     | 
| 
      
 91 
     | 
    
         
            +
             
     | 
| 
      
 92 
     | 
    
         
            +
            ## License
         
     | 
| 
      
 93 
     | 
    
         
            +
             
     | 
| 
      
 94 
     | 
    
         
            +
            The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
         
     | 
    
        data/Rakefile
    ADDED
    
    
| 
         @@ -0,0 +1,32 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require_relative 'lib/async_storage/version'
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            Gem::Specification.new do |spec|
         
     | 
| 
      
 6 
     | 
    
         
            +
              spec.name          = 'async_storage'
         
     | 
| 
      
 7 
     | 
    
         
            +
              spec.version       = AsyncStorage::VERSION
         
     | 
| 
      
 8 
     | 
    
         
            +
              spec.authors       = ['Marcos G. Zimmermann']
         
     | 
| 
      
 9 
     | 
    
         
            +
              spec.email         = ['mgzmaster@gmail.com']
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
              spec.summary       = 'Asynchronous key-value storage system'
         
     | 
| 
      
 12 
     | 
    
         
            +
              spec.description   = 'Asynchronous key-value storage system'
         
     | 
| 
      
 13 
     | 
    
         
            +
              spec.homepage      = 'https://github.com/marcosgz/async_storage'
         
     | 
| 
      
 14 
     | 
    
         
            +
              spec.license       = 'MIT'
         
     | 
| 
      
 15 
     | 
    
         
            +
              spec.required_ruby_version = Gem::Requirement.new('>= 2.3.0')
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
              spec.metadata['homepage_uri'] = spec.homepage
         
     | 
| 
      
 18 
     | 
    
         
            +
              spec.metadata['source_code_uri'] = 'https://github.com/marcosgz/async_storage'
         
     | 
| 
      
 19 
     | 
    
         
            +
              spec.metadata['changelog_uri'] = 'https://github.com/marcosgz/async_storage/blob/master/CHANGELOG.md'
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
              # Specify which files should be added to the gem when it is released.
         
     | 
| 
      
 22 
     | 
    
         
            +
              # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
         
     | 
| 
      
 23 
     | 
    
         
            +
              spec.files = Dir.chdir(File.expand_path(__dir__)) do
         
     | 
| 
      
 24 
     | 
    
         
            +
                `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
         
     | 
| 
      
 25 
     | 
    
         
            +
              end
         
     | 
| 
      
 26 
     | 
    
         
            +
              spec.bindir        = 'exe'
         
     | 
| 
      
 27 
     | 
    
         
            +
              spec.executables   = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
         
     | 
| 
      
 28 
     | 
    
         
            +
              spec.require_paths = ['lib']
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
              spec.add_dependency 'redis', '> 0.0.0'
         
     | 
| 
      
 31 
     | 
    
         
            +
              spec.add_dependency 'multi_json', '> 0.0.0'
         
     | 
| 
      
 32 
     | 
    
         
            +
            end
         
     | 
    
        data/bin/console
    ADDED
    
    | 
         @@ -0,0 +1,21 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            #!/usr/bin/env ruby
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require 'bundler/setup'
         
     | 
| 
      
 4 
     | 
    
         
            +
            require 'dotenv/load'
         
     | 
| 
      
 5 
     | 
    
         
            +
            require 'pry'
         
     | 
| 
      
 6 
     | 
    
         
            +
            require 'awesome_print'
         
     | 
| 
      
 7 
     | 
    
         
            +
            require 'async_storage'
         
     | 
| 
      
 8 
     | 
    
         
            +
            require 'connection_pool'
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
            AsyncStorage.configure do |config|
         
     | 
| 
      
 11 
     | 
    
         
            +
              config.redis = ConnectionPool.new(size: 2, timeout: 0.5) do
         
     | 
| 
      
 12 
     | 
    
         
            +
                Redis.new(url: ENV.fetch('REDIS_URL', 'redis://0.0.0.0:6379'))
         
     | 
| 
      
 13 
     | 
    
         
            +
              end
         
     | 
| 
      
 14 
     | 
    
         
            +
              config.expires_in = 120
         
     | 
| 
      
 15 
     | 
    
         
            +
            end
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
            class DummyResolver
         
     | 
| 
      
 18 
     | 
    
         
            +
              def call(*values); values; end
         
     | 
| 
      
 19 
     | 
    
         
            +
            end
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
            Pry.start
         
     | 
    
        data/bin/setup
    ADDED
    
    
| 
         @@ -0,0 +1,35 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require 'async_storage/version'
         
     | 
| 
      
 4 
     | 
    
         
            +
            require 'async_storage/config'
         
     | 
| 
      
 5 
     | 
    
         
            +
            require 'async_storage/redis_pool'
         
     | 
| 
      
 6 
     | 
    
         
            +
            require 'async_storage/json'
         
     | 
| 
      
 7 
     | 
    
         
            +
            require 'async_storage/repo'
         
     | 
| 
      
 8 
     | 
    
         
            +
            require 'async_storage/bath_actions'
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
            module AsyncStorage
         
     | 
| 
      
 11 
     | 
    
         
            +
              class Error < StandardError; end
         
     | 
| 
      
 12 
     | 
    
         
            +
              class InvalidConfig < Error; end
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
              module_function
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
              def [](klass, **options)
         
     | 
| 
      
 17 
     | 
    
         
            +
                Repo.new(klass, **options)
         
     | 
| 
      
 18 
     | 
    
         
            +
              end
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
              def config
         
     | 
| 
      
 21 
     | 
    
         
            +
                @config ||= Config.new
         
     | 
| 
      
 22 
     | 
    
         
            +
              end
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
              def configure(&block)
         
     | 
| 
      
 25 
     | 
    
         
            +
                return unless block_given?
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
                config.instance_eval(&block)
         
     | 
| 
      
 28 
     | 
    
         
            +
                @redis_pool = nil
         
     | 
| 
      
 29 
     | 
    
         
            +
                config
         
     | 
| 
      
 30 
     | 
    
         
            +
              end
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
              def redis_pool
         
     | 
| 
      
 33 
     | 
    
         
            +
                @redis_pool ||= RedisPool.new(config.redis)
         
     | 
| 
      
 34 
     | 
    
         
            +
              end
         
     | 
| 
      
 35 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,19 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module AsyncStorage
         
     | 
| 
      
 4 
     | 
    
         
            +
              module_function
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
              def flush_all
         
     | 
| 
      
 7 
     | 
    
         
            +
                keys.inject(0) do |total, (key, cli)|
         
     | 
| 
      
 8 
     | 
    
         
            +
                  total + cli.del(key)
         
     | 
| 
      
 9 
     | 
    
         
            +
                end
         
     | 
| 
      
 10 
     | 
    
         
            +
              end
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
              def keys
         
     | 
| 
      
 13 
     | 
    
         
            +
                Enumerator.new do |yielder|
         
     | 
| 
      
 14 
     | 
    
         
            +
                  redis_pool.with do |cli|
         
     | 
| 
      
 15 
     | 
    
         
            +
                    cli.keys("#{config.namespace}:*").each { |key| yielder.yield(key, cli) }
         
     | 
| 
      
 16 
     | 
    
         
            +
                  end
         
     | 
| 
      
 17 
     | 
    
         
            +
                end
         
     | 
| 
      
 18 
     | 
    
         
            +
              end
         
     | 
| 
      
 19 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,84 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frizen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require 'redis'
         
     | 
| 
      
 4 
     | 
    
         
            +
            require 'yaml'
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            module AsyncStorage
         
     | 
| 
      
 7 
     | 
    
         
            +
              class Config
         
     | 
| 
      
 8 
     | 
    
         
            +
                class << self
         
     | 
| 
      
 9 
     | 
    
         
            +
                  private
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                  def attribute_accessor(field, validator: nil, normalizer: nil, default: nil)
         
     | 
| 
      
 12 
     | 
    
         
            +
                    normalizer ||= :"normalize_#{field}"
         
     | 
| 
      
 13 
     | 
    
         
            +
                    validator ||= :"validate_#{field}"
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                    define_method(field) do
         
     | 
| 
      
 16 
     | 
    
         
            +
                      unless instance_variable_defined?(:"@#{field}")
         
     | 
| 
      
 17 
     | 
    
         
            +
                        fallback = config_from_yaml[field.to_s] || default
         
     | 
| 
      
 18 
     | 
    
         
            +
                        return if fallback.nil?
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                        send(:"#{field}=", fallback.respond_to?(:call) ? fallback.call : fallback)
         
     | 
| 
      
 21 
     | 
    
         
            +
                      end
         
     | 
| 
      
 22 
     | 
    
         
            +
                      instance_variable_get(:"@#{field}")
         
     | 
| 
      
 23 
     | 
    
         
            +
                    end
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                    define_method(:"#{field}=") do |value|
         
     | 
| 
      
 26 
     | 
    
         
            +
                      value = send(normalizer, field, value) if respond_to?(normalizer, true)
         
     | 
| 
      
 27 
     | 
    
         
            +
                      send(validator, field, value) if respond_to?(validator, true)
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                      instance_variable_set(:"@#{field}", value)
         
     | 
| 
      
 30 
     | 
    
         
            +
                    end
         
     | 
| 
      
 31 
     | 
    
         
            +
                  end
         
     | 
| 
      
 32 
     | 
    
         
            +
                end
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
                # Path to the YAML file with configs
         
     | 
| 
      
 35 
     | 
    
         
            +
                attr_accessor :config_path
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
                # Redis/ConnectionPool instance of a valid list of configs to build a new redis connection
         
     | 
| 
      
 38 
     | 
    
         
            +
                attribute_accessor :redis, default: nil
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
                # Namespace used to group group data stored by this package
         
     | 
| 
      
 41 
     | 
    
         
            +
                attribute_accessor :namespace, default: 'async_storage'
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
                # The global TTL for the redis storage. Keep nil if you don't want to expire objects.
         
     | 
| 
      
 44 
     | 
    
         
            +
                attribute_accessor :expires_in, default: nil
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
                def config_path=(value)
         
     | 
| 
      
 47 
     | 
    
         
            +
                  @config_from_yaml = nil
         
     | 
| 
      
 48 
     | 
    
         
            +
                  @config_path = value
         
     | 
| 
      
 49 
     | 
    
         
            +
                end
         
     | 
| 
      
 50 
     | 
    
         
            +
             
     | 
| 
      
 51 
     | 
    
         
            +
                private
         
     | 
| 
      
 52 
     | 
    
         
            +
             
     | 
| 
      
 53 
     | 
    
         
            +
                def normalize_namespace(_attribute, value)
         
     | 
| 
      
 54 
     | 
    
         
            +
                  return value.to_s if value.is_a?(Symbol)
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
      
 56 
     | 
    
         
            +
                  value
         
     | 
| 
      
 57 
     | 
    
         
            +
                end
         
     | 
| 
      
 58 
     | 
    
         
            +
             
     | 
| 
      
 59 
     | 
    
         
            +
                def validate_namespace(attribute, value)
         
     | 
| 
      
 60 
     | 
    
         
            +
                  return if value.is_a?(String) && !value.empty?
         
     | 
| 
      
 61 
     | 
    
         
            +
             
     | 
| 
      
 62 
     | 
    
         
            +
                  raise InvalidConfig, format(
         
     | 
| 
      
 63 
     | 
    
         
            +
                    "The %<value>p for %<attr>s is not valid. It can't be blank",
         
     | 
| 
      
 64 
     | 
    
         
            +
                    value: value,
         
     | 
| 
      
 65 
     | 
    
         
            +
                    attr: attribute,
         
     | 
| 
      
 66 
     | 
    
         
            +
                  )
         
     | 
| 
      
 67 
     | 
    
         
            +
                end
         
     | 
| 
      
 68 
     | 
    
         
            +
             
     | 
| 
      
 69 
     | 
    
         
            +
                def normalize_expires_in(_attr, value)
         
     | 
| 
      
 70 
     | 
    
         
            +
                  ttl = value.to_i
         
     | 
| 
      
 71 
     | 
    
         
            +
                  return unless ttl > 0
         
     | 
| 
      
 72 
     | 
    
         
            +
             
     | 
| 
      
 73 
     | 
    
         
            +
                  ttl
         
     | 
| 
      
 74 
     | 
    
         
            +
                end
         
     | 
| 
      
 75 
     | 
    
         
            +
             
     | 
| 
      
 76 
     | 
    
         
            +
                def config_from_yaml
         
     | 
| 
      
 77 
     | 
    
         
            +
                  @config_from_yaml ||= begin
         
     | 
| 
      
 78 
     | 
    
         
            +
                    config_path ? YAML.load_file(config_path) : {}
         
     | 
| 
      
 79 
     | 
    
         
            +
                  rescue Errno::ENOENT, Errno::ESRCH
         
     | 
| 
      
 80 
     | 
    
         
            +
                    {}
         
     | 
| 
      
 81 
     | 
    
         
            +
                  end
         
     | 
| 
      
 82 
     | 
    
         
            +
                end
         
     | 
| 
      
 83 
     | 
    
         
            +
              end
         
     | 
| 
      
 84 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,42 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require 'multi_json'
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            module AsyncStorage
         
     | 
| 
      
 6 
     | 
    
         
            +
              module JSON
         
     | 
| 
      
 7 
     | 
    
         
            +
                module_function
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
                # Parses JSON data.
         
     | 
| 
      
 10 
     | 
    
         
            +
                #
         
     | 
| 
      
 11 
     | 
    
         
            +
                # @param data [String] JSON data
         
     | 
| 
      
 12 
     | 
    
         
            +
                # @param options [Hash] Options hash for `MultiJson.load`
         
     | 
| 
      
 13 
     | 
    
         
            +
                # @return [Object] Parsed JSON
         
     | 
| 
      
 14 
     | 
    
         
            +
                # @raise [MultiJson::ParseError] MultiJson error classes
         
     | 
| 
      
 15 
     | 
    
         
            +
                def load(data, **options)
         
     | 
| 
      
 16 
     | 
    
         
            +
                  MultiJson.load(data, **options)
         
     | 
| 
      
 17 
     | 
    
         
            +
                end
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                # Generates JSON.
         
     | 
| 
      
 20 
     | 
    
         
            +
                #
         
     | 
| 
      
 21 
     | 
    
         
            +
                # @param object [Object] Object to convert to JSON
         
     | 
| 
      
 22 
     | 
    
         
            +
                # @param options [Hash] Options hash for `MultiJson.dump` and additional options below
         
     | 
| 
      
 23 
     | 
    
         
            +
                # @return [String] Generated JSON
         
     | 
| 
      
 24 
     | 
    
         
            +
                # @raise [MultiJson::DecodeError] MultiJson error classes
         
     | 
| 
      
 25 
     | 
    
         
            +
                def dump(object, **options)
         
     | 
| 
      
 26 
     | 
    
         
            +
                  object = as_json(object)
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
                  MultiJson.dump(object, **options)
         
     | 
| 
      
 29 
     | 
    
         
            +
                end
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
                def as_json(value)
         
     | 
| 
      
 32 
     | 
    
         
            +
                  case value
         
     | 
| 
      
 33 
     | 
    
         
            +
                  when Hash
         
     | 
| 
      
 34 
     | 
    
         
            +
                    value.transform_values { |val| as_json(val) }
         
     | 
| 
      
 35 
     | 
    
         
            +
                  when Enumerable
         
     | 
| 
      
 36 
     | 
    
         
            +
                    value.map { |val| as_json(val) }
         
     | 
| 
      
 37 
     | 
    
         
            +
                  else
         
     | 
| 
      
 38 
     | 
    
         
            +
                    value
         
     | 
| 
      
 39 
     | 
    
         
            +
                  end
         
     | 
| 
      
 40 
     | 
    
         
            +
                end
         
     | 
| 
      
 41 
     | 
    
         
            +
              end
         
     | 
| 
      
 42 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,74 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require 'securerandom'
         
     | 
| 
      
 4 
     | 
    
         
            +
            require 'async_storage/util/strings'
         
     | 
| 
      
 5 
     | 
    
         
            +
            require 'async_storage/json'
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
            module AsyncStorage
         
     | 
| 
      
 8 
     | 
    
         
            +
              class Naming
         
     | 
| 
      
 9 
     | 
    
         
            +
                include Util::Strings
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                SET = {
         
     | 
| 
      
 12 
     | 
    
         
            +
                  head: 'h',
         
     | 
| 
      
 13 
     | 
    
         
            +
                  body: 'b',
         
     | 
| 
      
 14 
     | 
    
         
            +
                  none: '_null_',
         
     | 
| 
      
 15 
     | 
    
         
            +
                }.freeze
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                attr_reader :class_name, :class_args
         
     | 
| 
      
 18 
     | 
    
         
            +
                attr_accessor :prefix
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                def initialize(klass, *args)
         
     | 
| 
      
 21 
     | 
    
         
            +
                  @class_name = normalize_class(klass.name)
         
     | 
| 
      
 22 
     | 
    
         
            +
                  @class_args = normalize_args(args)
         
     | 
| 
      
 23 
     | 
    
         
            +
                end
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                def head
         
     | 
| 
      
 26 
     | 
    
         
            +
                  "#{base}:#{SET[:head]}"
         
     | 
| 
      
 27 
     | 
    
         
            +
                end
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                def body
         
     | 
| 
      
 30 
     | 
    
         
            +
                  "#{base}:#{SET[:body]}"
         
     | 
| 
      
 31 
     | 
    
         
            +
                end
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                def to_s
         
     | 
| 
      
 34 
     | 
    
         
            +
                  format(
         
     | 
| 
      
 35 
     | 
    
         
            +
                    '#<AsyncStorage::Naming head=%<head>p body=%<body>p>',
         
     | 
| 
      
 36 
     | 
    
         
            +
                    head: head,
         
     | 
| 
      
 37 
     | 
    
         
            +
                    body: body,
         
     | 
| 
      
 38 
     | 
    
         
            +
                  )
         
     | 
| 
      
 39 
     | 
    
         
            +
                end
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
                def eql?(other)
         
     | 
| 
      
 42 
     | 
    
         
            +
                  return false unless other.is_a?(self.class)
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
                  [head, body] == [other.head, other.body]
         
     | 
| 
      
 45 
     | 
    
         
            +
                end
         
     | 
| 
      
 46 
     | 
    
         
            +
                alias == eql?
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
                protected
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
                def base
         
     | 
| 
      
 51 
     | 
    
         
            +
                  [ns, prefix, class_name, class_args].compact.join(':')
         
     | 
| 
      
 52 
     | 
    
         
            +
                end
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
                def normalize_class(name)
         
     | 
| 
      
 55 
     | 
    
         
            +
                  if name.nil? || name.empty?
         
     | 
| 
      
 56 
     | 
    
         
            +
                    raise ArgumentError, 'Anonymous class is not allowed'
         
     | 
| 
      
 57 
     | 
    
         
            +
                  end
         
     | 
| 
      
 58 
     | 
    
         
            +
             
     | 
| 
      
 59 
     | 
    
         
            +
                  underscore(name, ':')
         
     | 
| 
      
 60 
     | 
    
         
            +
                end
         
     | 
| 
      
 61 
     | 
    
         
            +
             
     | 
| 
      
 62 
     | 
    
         
            +
                def normalize_args(args)
         
     | 
| 
      
 63 
     | 
    
         
            +
                  return SET[:none] if args.empty?
         
     | 
| 
      
 64 
     | 
    
         
            +
             
     | 
| 
      
 65 
     | 
    
         
            +
                  Digest::SHA256.hexdigest(
         
     | 
| 
      
 66 
     | 
    
         
            +
                    AsyncStorage::JSON.dump(args, mode: :compat),
         
     | 
| 
      
 67 
     | 
    
         
            +
                  )
         
     | 
| 
      
 68 
     | 
    
         
            +
                end
         
     | 
| 
      
 69 
     | 
    
         
            +
             
     | 
| 
      
 70 
     | 
    
         
            +
                def ns
         
     | 
| 
      
 71 
     | 
    
         
            +
                  AsyncStorage.config.namespace
         
     | 
| 
      
 72 
     | 
    
         
            +
                end
         
     | 
| 
      
 73 
     | 
    
         
            +
              end
         
     | 
| 
      
 74 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,30 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require 'redis'
         
     | 
| 
      
 4 
     | 
    
         
            +
            require 'forwardable'
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            module AsyncStorage
         
     | 
| 
      
 7 
     | 
    
         
            +
              class RedisPool
         
     | 
| 
      
 8 
     | 
    
         
            +
                extend Forwardable
         
     | 
| 
      
 9 
     | 
    
         
            +
                def_delegator :@connection, :with
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                module ConnectionPoolLike
         
     | 
| 
      
 12 
     | 
    
         
            +
                  def with
         
     | 
| 
      
 13 
     | 
    
         
            +
                    yield self
         
     | 
| 
      
 14 
     | 
    
         
            +
                  end
         
     | 
| 
      
 15 
     | 
    
         
            +
                end
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                def initialize(connection)
         
     | 
| 
      
 18 
     | 
    
         
            +
                  if connection.respond_to?(:with)
         
     | 
| 
      
 19 
     | 
    
         
            +
                    @connection = connection
         
     | 
| 
      
 20 
     | 
    
         
            +
                  else
         
     | 
| 
      
 21 
     | 
    
         
            +
                    if connection.respond_to?(:client)
         
     | 
| 
      
 22 
     | 
    
         
            +
                      @connection = connection
         
     | 
| 
      
 23 
     | 
    
         
            +
                    else
         
     | 
| 
      
 24 
     | 
    
         
            +
                      @connection = ::Redis.new(*[connection].compact)
         
     | 
| 
      
 25 
     | 
    
         
            +
                    end
         
     | 
| 
      
 26 
     | 
    
         
            +
                    @connection.extend(ConnectionPoolLike)
         
     | 
| 
      
 27 
     | 
    
         
            +
                  end
         
     | 
| 
      
 28 
     | 
    
         
            +
                end
         
     | 
| 
      
 29 
     | 
    
         
            +
              end
         
     | 
| 
      
 30 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,196 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require 'async_storage/naming'
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            module AsyncStorage
         
     | 
| 
      
 6 
     | 
    
         
            +
              class Repo
         
     | 
| 
      
 7 
     | 
    
         
            +
                CTRL = {
         
     | 
| 
      
 8 
     | 
    
         
            +
                  enqueued: '0',
         
     | 
| 
      
 9 
     | 
    
         
            +
                  executed: '1',
         
     | 
| 
      
 10 
     | 
    
         
            +
                  missing: nil,
         
     | 
| 
      
 11 
     | 
    
         
            +
                }.freeze
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
                attr_reader :resolver_class
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                # @param resolver_class [Class] A class with the call method
         
     | 
| 
      
 16 
     | 
    
         
            +
                # @param options [Hash] A hash with config
         
     | 
| 
      
 17 
     | 
    
         
            +
                # @option expires_in [Nil, Integer] Time in seconds
         
     | 
| 
      
 18 
     | 
    
         
            +
                # @raise [ArgumentError] When the resolver_class does not respond with `call' instance method
         
     | 
| 
      
 19 
     | 
    
         
            +
                def initialize(resolver_class, **options)
         
     | 
| 
      
 20 
     | 
    
         
            +
                  validate_resolver_class!(resolver_class)
         
     | 
| 
      
 21 
     | 
    
         
            +
                  @resolver_class = resolver_class
         
     | 
| 
      
 22 
     | 
    
         
            +
                  @options = options
         
     | 
| 
      
 23 
     | 
    
         
            +
                end
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                # Store a fresh content into redis. This method is invoked by a background job.
         
     | 
| 
      
 26 
     | 
    
         
            +
                #
         
     | 
| 
      
 27 
     | 
    
         
            +
                # @param options [Hash] List of options to be passed along the initializer
         
     | 
| 
      
 28 
     | 
    
         
            +
                # @option [Class] klass The resolver class
         
     | 
| 
      
 29 
     | 
    
         
            +
                # @option [Array] args An array with the resolver arguments
         
     | 
| 
      
 30 
     | 
    
         
            +
                # @return [Hash, NilClass] the result from class resolver
         
     | 
| 
      
 31 
     | 
    
         
            +
                def self.ack(klass:, args:, **options)
         
     | 
| 
      
 32 
     | 
    
         
            +
                  new(klass, **options).refresh!(*args)
         
     | 
| 
      
 33 
     | 
    
         
            +
                end
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
                # Async get value with a given key
         
     | 
| 
      
 36 
     | 
    
         
            +
                #
         
     | 
| 
      
 37 
     | 
    
         
            +
                # @return [Object, NilClass] Return both stale or fresh object. If does not exist async call the retriever and return nil
         
     | 
| 
      
 38 
     | 
    
         
            +
                def get(*args)
         
     | 
| 
      
 39 
     | 
    
         
            +
                  connection(*args) do |redis, naming|
         
     | 
| 
      
 40 
     | 
    
         
            +
                    raw_head = redis.get(naming.head)
         
     | 
| 
      
 41 
     | 
    
         
            +
                    case raw_head
         
     | 
| 
      
 42 
     | 
    
         
            +
                    when CTRL[:executed], CTRL[:enqueued]
         
     | 
| 
      
 43 
     | 
    
         
            +
                      read(redis, naming.body) # Try to deliver stale content
         
     | 
| 
      
 44 
     | 
    
         
            +
                    when CTRL[:missing]
         
     | 
| 
      
 45 
     | 
    
         
            +
                      return update!(redis, naming, *args) unless async?
         
     | 
| 
      
 46 
     | 
    
         
            +
             
     | 
| 
      
 47 
     | 
    
         
            +
                      perform_async(*args) # Enqueue background job to resolve content
         
     | 
| 
      
 48 
     | 
    
         
            +
                      redis.set(naming.head, CTRL[:enqueued])
         
     | 
| 
      
 49 
     | 
    
         
            +
                      read(redis, naming.body) # Try to deliver stale content
         
     | 
| 
      
 50 
     | 
    
         
            +
                    else
         
     | 
| 
      
 51 
     | 
    
         
            +
                      raise AsyncStorage::Error, format('the key %<k>s have an invalid value. Only "1" or "0" values are expected. And we got %<v>p', v: raw_head, k: naming.head)
         
     | 
| 
      
 52 
     | 
    
         
            +
                    end
         
     | 
| 
      
 53 
     | 
    
         
            +
                  end
         
     | 
| 
      
 54 
     | 
    
         
            +
                end
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
      
 56 
     | 
    
         
            +
                # Sync get value with a given value
         
     | 
| 
      
 57 
     | 
    
         
            +
                #
         
     | 
| 
      
 58 
     | 
    
         
            +
                # @return [Object] Return the result from resolver
         
     | 
| 
      
 59 
     | 
    
         
            +
                def get!(*args)
         
     | 
| 
      
 60 
     | 
    
         
            +
                  connection(*args) do |redis, naming|
         
     | 
| 
      
 61 
     | 
    
         
            +
                    raw_head = redis.get(naming.head)
         
     | 
| 
      
 62 
     | 
    
         
            +
                    case raw_head
         
     | 
| 
      
 63 
     | 
    
         
            +
                    when CTRL[:executed]
         
     | 
| 
      
 64 
     | 
    
         
            +
                      read(redis, naming.body) || begin
         
     | 
| 
      
 65 
     | 
    
         
            +
                        update!(redis, naming, *args) unless redis.exists?(naming.body)
         
     | 
| 
      
 66 
     | 
    
         
            +
                      end
         
     | 
| 
      
 67 
     | 
    
         
            +
                    when CTRL[:missing], CTRL[:enqueued]
         
     | 
| 
      
 68 
     | 
    
         
            +
                      update!(redis, naming, *args)
         
     | 
| 
      
 69 
     | 
    
         
            +
                    else
         
     | 
| 
      
 70 
     | 
    
         
            +
                      raise AsyncStorage::Error, format('the key %<k>s have an invalid value. Only "1" or "0" values are expected. And we got %<v>p', v: raw_head, k: naming.head)
         
     | 
| 
      
 71 
     | 
    
         
            +
                    end
         
     | 
| 
      
 72 
     | 
    
         
            +
                  end
         
     | 
| 
      
 73 
     | 
    
         
            +
                end
         
     | 
| 
      
 74 
     | 
    
         
            +
             
     | 
| 
      
 75 
     | 
    
         
            +
                # Expire object the object with a given key. The stale object will not be removed
         
     | 
| 
      
 76 
     | 
    
         
            +
                #
         
     | 
| 
      
 77 
     | 
    
         
            +
                # @return [Boolean] True or False according to the object existence
         
     | 
| 
      
 78 
     | 
    
         
            +
                def invalidate(*args)
         
     | 
| 
      
 79 
     | 
    
         
            +
                  connection(*args) do |redis, naming|
         
     | 
| 
      
 80 
     | 
    
         
            +
                    redis.del(naming.head) == 1
         
     | 
| 
      
 81 
     | 
    
         
            +
                  end
         
     | 
| 
      
 82 
     | 
    
         
            +
                end
         
     | 
| 
      
 83 
     | 
    
         
            +
             
     | 
| 
      
 84 
     | 
    
         
            +
                # Delete object with a given key.
         
     | 
| 
      
 85 
     | 
    
         
            +
                #
         
     | 
| 
      
 86 
     | 
    
         
            +
                # @return [Boolean] True or False according to the object existence
         
     | 
| 
      
 87 
     | 
    
         
            +
                def invalidate!(*args)
         
     | 
| 
      
 88 
     | 
    
         
            +
                  connection(*args) do |redis, naming|
         
     | 
| 
      
 89 
     | 
    
         
            +
                    redis.multi do |cli|
         
     | 
| 
      
 90 
     | 
    
         
            +
                      cli.del(naming.body)
         
     | 
| 
      
 91 
     | 
    
         
            +
                      cli.del(naming.head)
         
     | 
| 
      
 92 
     | 
    
         
            +
                    end.include?(1)
         
     | 
| 
      
 93 
     | 
    
         
            +
                  end
         
     | 
| 
      
 94 
     | 
    
         
            +
                end
         
     | 
| 
      
 95 
     | 
    
         
            +
             
     | 
| 
      
 96 
     | 
    
         
            +
                # Invalidate object with the given key and update content according to the strategy
         
     | 
| 
      
 97 
     | 
    
         
            +
                #
         
     | 
| 
      
 98 
     | 
    
         
            +
                # @return [Object, NilClass] Stale object or nil when it does not exist
         
     | 
| 
      
 99 
     | 
    
         
            +
                def refresh(*args)
         
     | 
| 
      
 100 
     | 
    
         
            +
                  value = get(*args)
         
     | 
| 
      
 101 
     | 
    
         
            +
                  invalidate(*args)
         
     | 
| 
      
 102 
     | 
    
         
            +
                  value
         
     | 
| 
      
 103 
     | 
    
         
            +
                end
         
     | 
| 
      
 104 
     | 
    
         
            +
             
     | 
| 
      
 105 
     | 
    
         
            +
                # Fetch data from resolver and store it into redis
         
     | 
| 
      
 106 
     | 
    
         
            +
                #
         
     | 
| 
      
 107 
     | 
    
         
            +
                # @return [Object] Return the result from resolver
         
     | 
| 
      
 108 
     | 
    
         
            +
                def refresh!(*args)
         
     | 
| 
      
 109 
     | 
    
         
            +
                  connection(*args) { |redis, naming| update!(redis, naming, *args) }
         
     | 
| 
      
 110 
     | 
    
         
            +
                end
         
     | 
| 
      
 111 
     | 
    
         
            +
             
     | 
| 
      
 112 
     | 
    
         
            +
                # Check if a fresh value exist.
         
     | 
| 
      
 113 
     | 
    
         
            +
                #
         
     | 
| 
      
 114 
     | 
    
         
            +
                # @return [Boolean] True or False according the object existence
         
     | 
| 
      
 115 
     | 
    
         
            +
                def exist?(*args)
         
     | 
| 
      
 116 
     | 
    
         
            +
                  connection(*args) { |redis, naming| redis.exists?(naming.head) && redis.exists?(naming.body) }
         
     | 
| 
      
 117 
     | 
    
         
            +
                end
         
     | 
| 
      
 118 
     | 
    
         
            +
             
     | 
| 
      
 119 
     | 
    
         
            +
                # Check if object with a given key is stale
         
     | 
| 
      
 120 
     | 
    
         
            +
                #
         
     | 
| 
      
 121 
     | 
    
         
            +
                # @return [NilClass, Boolean] Return nil if the object does not exist or true/false according to the object freshness state
         
     | 
| 
      
 122 
     | 
    
         
            +
                def stale?(*args)
         
     | 
| 
      
 123 
     | 
    
         
            +
                  connection(*args) { |redis, naming| redis.exists?(naming.body) && redis.ttl(naming.head) < 0 }
         
     | 
| 
      
 124 
     | 
    
         
            +
                end
         
     | 
| 
      
 125 
     | 
    
         
            +
             
     | 
| 
      
 126 
     | 
    
         
            +
                # Check if a fresh object exists into the storage
         
     | 
| 
      
 127 
     | 
    
         
            +
                #
         
     | 
| 
      
 128 
     | 
    
         
            +
                # @return [Boolean] true/false according to the object existence and freshness
         
     | 
| 
      
 129 
     | 
    
         
            +
                def fresh?(*args)
         
     | 
| 
      
 130 
     | 
    
         
            +
                  connection(*args) { |redis, naming| redis.exists?(naming.body) && redis.ttl(naming.head) > 0 }
         
     | 
| 
      
 131 
     | 
    
         
            +
                end
         
     | 
| 
      
 132 
     | 
    
         
            +
             
     | 
| 
      
 133 
     | 
    
         
            +
                private
         
     | 
| 
      
 134 
     | 
    
         
            +
             
     | 
| 
      
 135 
     | 
    
         
            +
                def async?
         
     | 
| 
      
 136 
     | 
    
         
            +
                  false
         
     | 
| 
      
 137 
     | 
    
         
            +
                end
         
     | 
| 
      
 138 
     | 
    
         
            +
             
     | 
| 
      
 139 
     | 
    
         
            +
                def perform_async(*args)
         
     | 
| 
      
 140 
     | 
    
         
            +
                  # @TODO Enqueue a real background job here. It's only working on sync mode
         
     | 
| 
      
 141 
     | 
    
         
            +
                  # redis.set(name.head, CTRL[:enqueued])
         
     | 
| 
      
 142 
     | 
    
         
            +
                  refresh!(*args)
         
     | 
| 
      
 143 
     | 
    
         
            +
                end
         
     | 
| 
      
 144 
     | 
    
         
            +
             
     | 
| 
      
 145 
     | 
    
         
            +
                def update!(redis, naming, *args)
         
     | 
| 
      
 146 
     | 
    
         
            +
                  payload = resolver_class.new.(*args)
         
     | 
| 
      
 147 
     | 
    
         
            +
             
     | 
| 
      
 148 
     | 
    
         
            +
                  json = AsyncStorage::JSON.dump(payload, mode: :compat)
         
     | 
| 
      
 149 
     | 
    
         
            +
                  naming = build_naming(*args)
         
     | 
| 
      
 150 
     | 
    
         
            +
                  redis.multi do |cli|
         
     | 
| 
      
 151 
     | 
    
         
            +
                    cli.set(naming.body, json)
         
     | 
| 
      
 152 
     | 
    
         
            +
                    cli.set(naming.head, CTRL[:executed])
         
     | 
| 
      
 153 
     | 
    
         
            +
                    cli.expire(naming.head, expires_in) if expires_in
         
     | 
| 
      
 154 
     | 
    
         
            +
                  end
         
     | 
| 
      
 155 
     | 
    
         
            +
                  AsyncStorage::JSON.load(json)
         
     | 
| 
      
 156 
     | 
    
         
            +
                end
         
     | 
| 
      
 157 
     | 
    
         
            +
             
     | 
| 
      
 158 
     | 
    
         
            +
                def read(redis, key)
         
     | 
| 
      
 159 
     | 
    
         
            +
                  return unless key
         
     | 
| 
      
 160 
     | 
    
         
            +
             
     | 
| 
      
 161 
     | 
    
         
            +
                  raw = redis.get(key)
         
     | 
| 
      
 162 
     | 
    
         
            +
                  return unless raw
         
     | 
| 
      
 163 
     | 
    
         
            +
             
     | 
| 
      
 164 
     | 
    
         
            +
                  AsyncStorage::JSON.load(raw)
         
     | 
| 
      
 165 
     | 
    
         
            +
                end
         
     | 
| 
      
 166 
     | 
    
         
            +
             
     | 
| 
      
 167 
     | 
    
         
            +
                def expires_in
         
     | 
| 
      
 168 
     | 
    
         
            +
                  @options[:expires_in] || AsyncStorage.config.expires_in
         
     | 
| 
      
 169 
     | 
    
         
            +
                end
         
     | 
| 
      
 170 
     | 
    
         
            +
             
     | 
| 
      
 171 
     | 
    
         
            +
                def connection(*args)
         
     | 
| 
      
 172 
     | 
    
         
            +
                  return unless block_given?
         
     | 
| 
      
 173 
     | 
    
         
            +
             
     | 
| 
      
 174 
     | 
    
         
            +
                  naming = build_naming(*args)
         
     | 
| 
      
 175 
     | 
    
         
            +
                  AsyncStorage.redis_pool.with { |redis| yield(redis, naming) }
         
     | 
| 
      
 176 
     | 
    
         
            +
                end
         
     | 
| 
      
 177 
     | 
    
         
            +
             
     | 
| 
      
 178 
     | 
    
         
            +
                def build_naming(*args)
         
     | 
| 
      
 179 
     | 
    
         
            +
                  naming = AsyncStorage::Naming.new(resolver_class, *args)
         
     | 
| 
      
 180 
     | 
    
         
            +
                  naming.prefix = @options[:namespace] if @options[:namespace]
         
     | 
| 
      
 181 
     | 
    
         
            +
                  naming
         
     | 
| 
      
 182 
     | 
    
         
            +
                end
         
     | 
| 
      
 183 
     | 
    
         
            +
             
     | 
| 
      
 184 
     | 
    
         
            +
                def validate_resolver_class!(klass)
         
     | 
| 
      
 185 
     | 
    
         
            +
                  unless klass.is_a?(Class)
         
     | 
| 
      
 186 
     | 
    
         
            +
                    raise(ArgumentError, format('%<c>p is not a valid resolver class.', c: klass))
         
     | 
| 
      
 187 
     | 
    
         
            +
                  end
         
     | 
| 
      
 188 
     | 
    
         
            +
             
     | 
| 
      
 189 
     | 
    
         
            +
                  unless klass.instance_methods.include?(:call)
         
     | 
| 
      
 190 
     | 
    
         
            +
                    raise(ArgumentError, format('%<c>p must have call instance method.', c: klass))
         
     | 
| 
      
 191 
     | 
    
         
            +
                  end
         
     | 
| 
      
 192 
     | 
    
         
            +
             
     | 
| 
      
 193 
     | 
    
         
            +
                  true
         
     | 
| 
      
 194 
     | 
    
         
            +
                end
         
     | 
| 
      
 195 
     | 
    
         
            +
              end
         
     | 
| 
      
 196 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,18 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module AsyncStorage
         
     | 
| 
      
 4 
     | 
    
         
            +
              module Util
         
     | 
| 
      
 5 
     | 
    
         
            +
                module Strings
         
     | 
| 
      
 6 
     | 
    
         
            +
                  module_function
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
                  def underscore(string, module_sep = '/')
         
     | 
| 
      
 9 
     | 
    
         
            +
                    string
         
     | 
| 
      
 10 
     | 
    
         
            +
                      .gsub(/::/, module_sep)
         
     | 
| 
      
 11 
     | 
    
         
            +
                      .gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
         
     | 
| 
      
 12 
     | 
    
         
            +
                      .gsub(/([a-z\d])([A-Z])/, '\1_\2')
         
     | 
| 
      
 13 
     | 
    
         
            +
                      .tr('-', '_')
         
     | 
| 
      
 14 
     | 
    
         
            +
                      .downcase
         
     | 
| 
      
 15 
     | 
    
         
            +
                  end
         
     | 
| 
      
 16 
     | 
    
         
            +
                end
         
     | 
| 
      
 17 
     | 
    
         
            +
              end
         
     | 
| 
      
 18 
     | 
    
         
            +
            end
         
     | 
    
        metadata
    ADDED
    
    | 
         @@ -0,0 +1,94 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            --- !ruby/object:Gem::Specification
         
     | 
| 
      
 2 
     | 
    
         
            +
            name: async_storage
         
     | 
| 
      
 3 
     | 
    
         
            +
            version: !ruby/object:Gem::Version
         
     | 
| 
      
 4 
     | 
    
         
            +
              version: 0.0.1
         
     | 
| 
      
 5 
     | 
    
         
            +
            platform: ruby
         
     | 
| 
      
 6 
     | 
    
         
            +
            authors:
         
     | 
| 
      
 7 
     | 
    
         
            +
            - Marcos G. Zimmermann
         
     | 
| 
      
 8 
     | 
    
         
            +
            autorequire: 
         
     | 
| 
      
 9 
     | 
    
         
            +
            bindir: exe
         
     | 
| 
      
 10 
     | 
    
         
            +
            cert_chain: []
         
     | 
| 
      
 11 
     | 
    
         
            +
            date: 2020-11-09 00:00:00.000000000 Z
         
     | 
| 
      
 12 
     | 
    
         
            +
            dependencies:
         
     | 
| 
      
 13 
     | 
    
         
            +
            - !ruby/object:Gem::Dependency
         
     | 
| 
      
 14 
     | 
    
         
            +
              name: redis
         
     | 
| 
      
 15 
     | 
    
         
            +
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
      
 16 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 17 
     | 
    
         
            +
                - - ">"
         
     | 
| 
      
 18 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 19 
     | 
    
         
            +
                    version: 0.0.0
         
     | 
| 
      
 20 
     | 
    
         
            +
              type: :runtime
         
     | 
| 
      
 21 
     | 
    
         
            +
              prerelease: false
         
     | 
| 
      
 22 
     | 
    
         
            +
              version_requirements: !ruby/object:Gem::Requirement
         
     | 
| 
      
 23 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 24 
     | 
    
         
            +
                - - ">"
         
     | 
| 
      
 25 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 26 
     | 
    
         
            +
                    version: 0.0.0
         
     | 
| 
      
 27 
     | 
    
         
            +
            - !ruby/object:Gem::Dependency
         
     | 
| 
      
 28 
     | 
    
         
            +
              name: multi_json
         
     | 
| 
      
 29 
     | 
    
         
            +
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
      
 30 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 31 
     | 
    
         
            +
                - - ">"
         
     | 
| 
      
 32 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 33 
     | 
    
         
            +
                    version: 0.0.0
         
     | 
| 
      
 34 
     | 
    
         
            +
              type: :runtime
         
     | 
| 
      
 35 
     | 
    
         
            +
              prerelease: false
         
     | 
| 
      
 36 
     | 
    
         
            +
              version_requirements: !ruby/object:Gem::Requirement
         
     | 
| 
      
 37 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 38 
     | 
    
         
            +
                - - ">"
         
     | 
| 
      
 39 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 40 
     | 
    
         
            +
                    version: 0.0.0
         
     | 
| 
      
 41 
     | 
    
         
            +
            description: Asynchronous key-value storage system
         
     | 
| 
      
 42 
     | 
    
         
            +
            email:
         
     | 
| 
      
 43 
     | 
    
         
            +
            - mgzmaster@gmail.com
         
     | 
| 
      
 44 
     | 
    
         
            +
            executables: []
         
     | 
| 
      
 45 
     | 
    
         
            +
            extensions: []
         
     | 
| 
      
 46 
     | 
    
         
            +
            extra_rdoc_files: []
         
     | 
| 
      
 47 
     | 
    
         
            +
            files:
         
     | 
| 
      
 48 
     | 
    
         
            +
            - ".gitignore"
         
     | 
| 
      
 49 
     | 
    
         
            +
            - ".rspec"
         
     | 
| 
      
 50 
     | 
    
         
            +
            - ".travis.yml"
         
     | 
| 
      
 51 
     | 
    
         
            +
            - Gemfile
         
     | 
| 
      
 52 
     | 
    
         
            +
            - Gemfile.lock
         
     | 
| 
      
 53 
     | 
    
         
            +
            - LICENSE.txt
         
     | 
| 
      
 54 
     | 
    
         
            +
            - README.md
         
     | 
| 
      
 55 
     | 
    
         
            +
            - Rakefile
         
     | 
| 
      
 56 
     | 
    
         
            +
            - async_storage.gemspec
         
     | 
| 
      
 57 
     | 
    
         
            +
            - bin/console
         
     | 
| 
      
 58 
     | 
    
         
            +
            - bin/setup
         
     | 
| 
      
 59 
     | 
    
         
            +
            - lib/async_storage.rb
         
     | 
| 
      
 60 
     | 
    
         
            +
            - lib/async_storage/bath_actions.rb
         
     | 
| 
      
 61 
     | 
    
         
            +
            - lib/async_storage/config.rb
         
     | 
| 
      
 62 
     | 
    
         
            +
            - lib/async_storage/json.rb
         
     | 
| 
      
 63 
     | 
    
         
            +
            - lib/async_storage/naming.rb
         
     | 
| 
      
 64 
     | 
    
         
            +
            - lib/async_storage/redis_pool.rb
         
     | 
| 
      
 65 
     | 
    
         
            +
            - lib/async_storage/repo.rb
         
     | 
| 
      
 66 
     | 
    
         
            +
            - lib/async_storage/util/strings.rb
         
     | 
| 
      
 67 
     | 
    
         
            +
            - lib/async_storage/version.rb
         
     | 
| 
      
 68 
     | 
    
         
            +
            homepage: https://github.com/marcosgz/async_storage
         
     | 
| 
      
 69 
     | 
    
         
            +
            licenses:
         
     | 
| 
      
 70 
     | 
    
         
            +
            - MIT
         
     | 
| 
      
 71 
     | 
    
         
            +
            metadata:
         
     | 
| 
      
 72 
     | 
    
         
            +
              homepage_uri: https://github.com/marcosgz/async_storage
         
     | 
| 
      
 73 
     | 
    
         
            +
              source_code_uri: https://github.com/marcosgz/async_storage
         
     | 
| 
      
 74 
     | 
    
         
            +
              changelog_uri: https://github.com/marcosgz/async_storage/blob/master/CHANGELOG.md
         
     | 
| 
      
 75 
     | 
    
         
            +
            post_install_message: 
         
     | 
| 
      
 76 
     | 
    
         
            +
            rdoc_options: []
         
     | 
| 
      
 77 
     | 
    
         
            +
            require_paths:
         
     | 
| 
      
 78 
     | 
    
         
            +
            - lib
         
     | 
| 
      
 79 
     | 
    
         
            +
            required_ruby_version: !ruby/object:Gem::Requirement
         
     | 
| 
      
 80 
     | 
    
         
            +
              requirements:
         
     | 
| 
      
 81 
     | 
    
         
            +
              - - ">="
         
     | 
| 
      
 82 
     | 
    
         
            +
                - !ruby/object:Gem::Version
         
     | 
| 
      
 83 
     | 
    
         
            +
                  version: 2.3.0
         
     | 
| 
      
 84 
     | 
    
         
            +
            required_rubygems_version: !ruby/object:Gem::Requirement
         
     | 
| 
      
 85 
     | 
    
         
            +
              requirements:
         
     | 
| 
      
 86 
     | 
    
         
            +
              - - ">="
         
     | 
| 
      
 87 
     | 
    
         
            +
                - !ruby/object:Gem::Version
         
     | 
| 
      
 88 
     | 
    
         
            +
                  version: '0'
         
     | 
| 
      
 89 
     | 
    
         
            +
            requirements: []
         
     | 
| 
      
 90 
     | 
    
         
            +
            rubygems_version: 3.1.2
         
     | 
| 
      
 91 
     | 
    
         
            +
            signing_key: 
         
     | 
| 
      
 92 
     | 
    
         
            +
            specification_version: 4
         
     | 
| 
      
 93 
     | 
    
         
            +
            summary: Asynchronous key-value storage system
         
     | 
| 
      
 94 
     | 
    
         
            +
            test_files: []
         
     |