basket 0.0.1 → 0.0.3
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/Gemfile +6 -2
- data/Gemfile.lock +72 -1
- data/Guardfile +37 -0
- data/README.md +59 -41
- data/basket.gemspec +6 -9
- data/lib/basket/backend_adapter/hash_backend.rb +30 -0
- data/lib/basket/backend_adapter/redis_backend.rb +56 -0
- data/lib/basket/backend_adapter.rb +23 -0
- data/lib/basket/batcher.rb +34 -0
- data/lib/basket/configuration.rb +25 -0
- data/lib/basket/error.rb +5 -0
- data/lib/basket/handle_add.rb +49 -0
- data/lib/basket/queue_collection.rb +32 -0
- data/lib/basket/version.rb +1 -1
- data/lib/basket.rb +27 -6
- metadata +57 -7
- data/lib/basket/hash_backend.rb +0 -24
- data/lib/basket/queue.rb +0 -24
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: '092352c400ad4483af38e9afe1060a39c6162c7318e4c69fb1ecd7649af56344'
|
4
|
+
data.tar.gz: 9a6cb3e5937d7367b6e83bae58e796c71c7a2a7b77cec5bbbbd5178bf4e0107b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a0f510f3e3f9c7b7accff0b76580d426e939d82212d6a4d83979f3043e4d679c8fafe994e58f788cf920110f3094e45cac1e290b50c802a22a5937358cdabd86
|
7
|
+
data.tar.gz: 33798eb722f92da71a396bac9529676f34cde316cdf3824fe5c42e66cfca26444caedb615fdcbad4ff67374a0a60dd646daf93eb349e0ee4d880f22b2186b2be
|
data/Gemfile
CHANGED
@@ -5,8 +5,12 @@ source "https://rubygems.org"
|
|
5
5
|
# Specify your gem's dependencies in basket.gemspec
|
6
6
|
gemspec
|
7
7
|
|
8
|
+
gem "guard"
|
9
|
+
gem "guard-rspec", require: false
|
10
|
+
gem "guard-standardrb", require: false
|
11
|
+
gem "mocktail"
|
8
12
|
gem "rake", "~> 13.0"
|
9
|
-
|
10
13
|
gem "rspec", "~> 3.0"
|
11
|
-
|
14
|
+
gem "simplecov", require: false, group: :test
|
15
|
+
gem "simplecov-json", require: false, group: :test
|
12
16
|
gem "standard", "~> 1.3"
|
data/Gemfile.lock
CHANGED
@@ -1,20 +1,69 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
basket (0.
|
4
|
+
basket (0.0.2)
|
5
|
+
redis
|
6
|
+
redis-namespace
|
5
7
|
|
6
8
|
GEM
|
7
9
|
remote: https://rubygems.org/
|
8
10
|
specs:
|
9
11
|
ast (2.4.2)
|
12
|
+
coderay (1.1.3)
|
13
|
+
connection_pool (2.3.0)
|
10
14
|
diff-lcs (1.5.0)
|
15
|
+
docile (1.4.0)
|
16
|
+
ffi (1.15.5)
|
17
|
+
formatador (1.1.0)
|
18
|
+
guard (2.18.0)
|
19
|
+
formatador (>= 0.2.4)
|
20
|
+
listen (>= 2.7, < 4.0)
|
21
|
+
lumberjack (>= 1.0.12, < 2.0)
|
22
|
+
nenv (~> 0.1)
|
23
|
+
notiffany (~> 0.0)
|
24
|
+
pry (>= 0.13.0)
|
25
|
+
shellany (~> 0.0)
|
26
|
+
thor (>= 0.18.1)
|
27
|
+
guard-compat (1.2.1)
|
28
|
+
guard-rspec (4.7.3)
|
29
|
+
guard (~> 2.1)
|
30
|
+
guard-compat (~> 1.1)
|
31
|
+
rspec (>= 2.99.0, < 4.0)
|
32
|
+
guard-standardrb (0.2.2)
|
33
|
+
guard (>= 2.0.0)
|
34
|
+
guard-compat (~> 1.0)
|
35
|
+
standardrb
|
11
36
|
json (2.6.3)
|
12
37
|
language_server-protocol (3.17.0.3)
|
38
|
+
listen (3.8.0)
|
39
|
+
rb-fsevent (~> 0.10, >= 0.10.3)
|
40
|
+
rb-inotify (~> 0.9, >= 0.9.10)
|
41
|
+
lumberjack (1.2.8)
|
42
|
+
method_source (1.0.0)
|
43
|
+
mock_redis (0.36.0)
|
44
|
+
ruby2_keywords
|
45
|
+
mocktail (1.2.2)
|
46
|
+
nenv (0.3.0)
|
47
|
+
notiffany (0.1.3)
|
48
|
+
nenv (~> 0.1)
|
49
|
+
shellany (~> 0.0)
|
13
50
|
parallel (1.22.1)
|
14
51
|
parser (3.2.1.0)
|
15
52
|
ast (~> 2.4.1)
|
53
|
+
pry (0.14.2)
|
54
|
+
coderay (~> 1.1)
|
55
|
+
method_source (~> 1.0)
|
16
56
|
rainbow (3.1.1)
|
17
57
|
rake (13.0.6)
|
58
|
+
rb-fsevent (0.11.2)
|
59
|
+
rb-inotify (0.10.1)
|
60
|
+
ffi (~> 1.0)
|
61
|
+
redis (5.0.6)
|
62
|
+
redis-client (>= 0.9.0)
|
63
|
+
redis-client (0.14.0)
|
64
|
+
connection_pool
|
65
|
+
redis-namespace (1.10.0)
|
66
|
+
redis (>= 4)
|
18
67
|
regexp_parser (2.7.0)
|
19
68
|
rexml (3.2.5)
|
20
69
|
rspec (3.12.0)
|
@@ -46,21 +95,43 @@ GEM
|
|
46
95
|
rubocop (>= 1.7.0, < 2.0)
|
47
96
|
rubocop-ast (>= 0.4.0)
|
48
97
|
ruby-progressbar (1.11.0)
|
98
|
+
ruby2_keywords (0.0.5)
|
99
|
+
shellany (0.0.1)
|
100
|
+
simplecov (0.22.0)
|
101
|
+
docile (~> 1.1)
|
102
|
+
simplecov-html (~> 0.11)
|
103
|
+
simplecov_json_formatter (~> 0.1)
|
104
|
+
simplecov-html (0.12.3)
|
105
|
+
simplecov-json (0.2.3)
|
106
|
+
json
|
107
|
+
simplecov
|
108
|
+
simplecov_json_formatter (0.1.4)
|
49
109
|
standard (1.24.3)
|
50
110
|
language_server-protocol (~> 3.17.0.2)
|
51
111
|
rubocop (= 1.44.1)
|
52
112
|
rubocop-performance (= 1.15.2)
|
113
|
+
standardrb (1.0.1)
|
114
|
+
standard
|
115
|
+
thor (1.2.1)
|
53
116
|
unicode-display_width (2.4.2)
|
54
117
|
|
55
118
|
PLATFORMS
|
56
119
|
arm64-darwin-22
|
57
120
|
x86_64-darwin-20
|
121
|
+
x86_64-darwin-21
|
58
122
|
x86_64-linux
|
59
123
|
|
60
124
|
DEPENDENCIES
|
61
125
|
basket!
|
126
|
+
guard
|
127
|
+
guard-rspec
|
128
|
+
guard-standardrb
|
129
|
+
mock_redis
|
130
|
+
mocktail
|
62
131
|
rake (~> 13.0)
|
63
132
|
rspec (~> 3.0)
|
133
|
+
simplecov
|
134
|
+
simplecov-json
|
64
135
|
standard (~> 1.3)
|
65
136
|
|
66
137
|
BUNDLED WITH
|
data/Guardfile
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
# A sample Guardfile
|
2
|
+
# More info at https://github.com/guard/guard#readme
|
3
|
+
|
4
|
+
## Uncomment and set this to only include directories you want to watch
|
5
|
+
# directories %w(app lib config test spec features) \
|
6
|
+
# .select{|d| Dir.exist?(d) ? d : UI.warning("Directory #{d} does not exist")}
|
7
|
+
|
8
|
+
## Note: if you are using the `directories` clause above and you are not
|
9
|
+
## watching the project directory ('.'), then you will want to move
|
10
|
+
## the Guardfile to a watched dir and symlink it back, e.g.
|
11
|
+
#
|
12
|
+
# $ mkdir config
|
13
|
+
# $ mv Guardfile config/
|
14
|
+
# $ ln -s config/Guardfile .
|
15
|
+
#
|
16
|
+
# and, you'll have to watch "config/Guardfile" instead of "Guardfile"
|
17
|
+
|
18
|
+
# NOTE: The cmd option is now required due to the increasing number of ways
|
19
|
+
# rspec may be run, below are examples of the most common uses.
|
20
|
+
# * bundler: 'bundle exec rspec'
|
21
|
+
# * bundler binstubs: 'bin/rspec'
|
22
|
+
# * spring: 'bin/rspec' (This will use spring if running and you have
|
23
|
+
# installed the spring binstubs per the docs)
|
24
|
+
# * zeus: 'zeus rspec' (requires the server to be started separately)
|
25
|
+
# * 'just' rspec: 'rspec'
|
26
|
+
|
27
|
+
group "specs", halt_on_fail: true do
|
28
|
+
guard :rspec, all_on_start: true, all_after_pass: true, cmd: "rspec" do
|
29
|
+
watch(%r{^spec/.+_spec\.rb$})
|
30
|
+
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
|
31
|
+
watch("spec/spec_helper.rb") { "spec/" }
|
32
|
+
end
|
33
|
+
|
34
|
+
guard :standardrb, fix: true, all_on_start: true, progress: true do
|
35
|
+
watch(/.+\.rb$/)
|
36
|
+
end
|
37
|
+
end
|
data/README.md
CHANGED
@@ -1,21 +1,25 @@
|
|
1
|
-
|
1
|
+
[](https://badge.fury.io/rb/basket)
|
2
|
+
[](https://github.com/testdouble/standard)
|
3
|
+
[](https://github.com/nicholalexander/basket/actions/workflows/main.yml)
|
4
|
+
[](https://codeclimate.com/github/nicholalexander/basket/maintainability)
|
5
|
+
[](https://codeclimate.com/github/nicholalexander/basket/test_coverage)
|
2
6
|
# Basket
|
3
7
|
|
4
|
-
A farmer doesn't walk down to the chicken coop, grab an egg, go back to the kitchen, go back to the coop, go back to the kitchen
|
8
|
+
A farmer doesn't walk down to the chicken coop, grab an egg, go back to the kitchen, go back to the coop, go back to the kitchen ad infinitum. They take a basket with them, and as the chickens lay their eggs, they fill up the basket and when the basket is full they go make something with them! I would make a quiche, but that's besides the case.
|
5
9
|
|
6
10
|
`Basket` lets you do just that. Collect items until your basket is full and then, when it is, go do something with them!
|
7
11
|
|
8
|
-
|
12
|
+
Basket is very new and under development.
|
9
13
|
|
10
|
-
|
14
|
+
## Installation
|
11
15
|
|
12
16
|
Install the gem and add to the application's Gemfile by executing:
|
13
17
|
|
14
|
-
$ bundle add
|
18
|
+
$ bundle add basket
|
15
19
|
|
16
20
|
If bundler is not being used to manage dependencies, install the gem by executing:
|
17
21
|
|
18
|
-
$ gem install
|
22
|
+
$ gem install basket
|
19
23
|
|
20
24
|
## Usage
|
21
25
|
|
@@ -32,40 +36,71 @@ The item added to the basket can be any data you want! If you are using the in
|
|
32
36
|
|
33
37
|
```ruby
|
34
38
|
class QuicheBasket
|
39
|
+
# Include the Basket::Batcher
|
35
40
|
include Basket::Batcher
|
36
|
-
|
37
|
-
|
41
|
+
|
42
|
+
# Define the size of your basket
|
43
|
+
basket_options size: 15
|
38
44
|
|
39
45
|
def perform
|
40
|
-
eggs = []
|
41
46
|
batch.each do | egg |
|
42
|
-
#
|
43
|
-
egg = Egg.new(egg)
|
44
|
-
egg.wash!
|
45
|
-
eggs << egg
|
47
|
+
# Do some processing on each element of the batch. In this case there will be 15 eggs.
|
46
48
|
end
|
47
49
|
|
48
50
|
# If you want to do something directly inline:
|
49
|
-
Quiche.make(
|
51
|
+
Quiche.make(batch)
|
50
52
|
|
51
53
|
# If you want to do something out of a request response cycle,
|
52
54
|
# call out to your favorite background processing framework:
|
53
55
|
BrunchInviteJob.perform_async
|
54
56
|
end
|
55
57
|
|
56
|
-
# There are
|
57
|
-
# :
|
58
|
+
# There are three callbacks to the lifecycle of a basket.
|
59
|
+
# :on_add, :on_success, and :on_failure.
|
58
60
|
# They can be used like this:
|
59
|
-
on_success
|
61
|
+
def on_success
|
62
|
+
Farm.rest_chickens
|
63
|
+
batch.each do |egg|
|
64
|
+
egg.inspect
|
65
|
+
end
|
66
|
+
end
|
60
67
|
|
61
|
-
def
|
62
|
-
|
68
|
+
def on_add
|
69
|
+
element.wash
|
63
70
|
end
|
64
71
|
|
72
|
+
def on_failure
|
73
|
+
Farm.notify_egg_monitor(error)
|
74
|
+
raise Error
|
75
|
+
end
|
76
|
+
end
|
77
|
+
```
|
78
|
+
|
79
|
+
The perform method will be called after there have been the defined number of elements added to the batch, specified in the `basket_options` size parameter. The elements can be any kind of data, depending on the backend that you are using. The default is just an in-memory hash.
|
80
|
+
|
81
|
+
The callbacks are lifecycle callbacks on the existing batch. `on_add` gives access to a variable called `element` which is equal to the item just added to the batch. `on_add`, `on_success` and `on_failure` also give access to the whole batch through the `batch` variable. `on_success` is called after `perform`.
|
82
|
+
|
83
|
+
The `on_failure` use of `batch` of course may not have a full batch as the error could have been generated during `add` or `on_add`. The `on_failure` callback also has access to an `error` variable which holds the error that was generated.
|
84
|
+
|
85
|
+
Defining `on_add`, `on_failure`, and `on_success` is optional.
|
86
|
+
|
87
|
+
## Configuration
|
88
|
+
|
89
|
+
In an initializer, or somewhere equally appropriate, you might put something like this:
|
90
|
+
|
91
|
+
```ruby
|
92
|
+
Basket.config do |config|
|
93
|
+
config.redis_host = "127.0.0.2"
|
94
|
+
config.redis_port = 6390
|
95
|
+
config.redis_db = 10
|
96
|
+
config.backend = :redis
|
97
|
+
config.namespace = :basket
|
65
98
|
end
|
66
99
|
```
|
67
100
|
|
68
|
-
The
|
101
|
+
The defaults for a redis backend are the standard "127.0.0.1", 6379, 15 with a namespace of :basket.
|
102
|
+
|
103
|
+
The default for the backend is the HashBackend, which can be set by passing `:hash` to `config.backend`, but you don't have to do that. Because it's the default!
|
69
104
|
|
70
105
|
## Development
|
71
106
|
|
@@ -73,9 +108,11 @@ After checking out the repo, run `bin/setup` to install dependencies. Then, run
|
|
73
108
|
|
74
109
|
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 the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
75
110
|
|
111
|
+
This project uses Guard to facilitate local development. You can run it with `bundle exec guard`. It will run specs on change to files and will run `standard --fix` after passing tests.
|
112
|
+
|
76
113
|
## Contributing
|
77
114
|
|
78
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/
|
115
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/nicholalexander/basket. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/nicholalexander/basket/blob/main/CODE_OF_CONDUCT.md).
|
79
116
|
|
80
117
|
## License
|
81
118
|
|
@@ -83,26 +120,7 @@ The gem is available as open source under the terms of the [MIT License](https:/
|
|
83
120
|
|
84
121
|
## Code of Conduct
|
85
122
|
|
86
|
-
Everyone interacting in the Basket project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
## Ideas
|
91
|
-
|
92
|
-
1. Can the callbacks support ActiveSupport Notifications?
|
93
|
-
- ActiveSupport::Callbacks extraction of callbacks
|
94
|
-
2. Batch can be postgres json blob or Redis! - Backend system
|
95
|
-
~~3. Does not execute in line~~For now.
|
96
|
-
~~4. Use ActiveJob for background execution.~~ It's up to you to handle a full basket how you want.
|
97
|
-
~~5. "Buffer", "collection", "queue"~~ Basket.
|
98
|
-
6. Default trigger is just queue length.
|
99
|
-
7. Expose basket_options trigger: :check_some_thing_lambda
|
100
|
-
8. Redis push pop.
|
101
|
-
9. Make queue ephemeral?
|
102
|
-
10. Define gotchas but don't solve them.
|
103
|
-
11. Redis fetch / Super Fetch?
|
104
|
-
12. Configuration.
|
123
|
+
Everyone interacting in the Basket project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/nicholalexander/basket/blob/main/CODE_OF_CONDUCT.md).
|
105
124
|
|
106
|
-
https://api.rubyonrails.org/classes/ActiveSupport/Callbacks.html
|
107
125
|
|
108
126
|
|
data/basket.gemspec
CHANGED
@@ -5,11 +5,11 @@ require_relative "lib/basket/version"
|
|
5
5
|
Gem::Specification.new do |spec|
|
6
6
|
spec.name = "basket"
|
7
7
|
spec.version = Basket::VERSION
|
8
|
-
spec.authors = ["nichol alexander"]
|
9
|
-
spec.email = ["nichol.alexander@gmail.com "]
|
8
|
+
spec.authors = ["nichol alexander", "alec clarke"]
|
9
|
+
spec.email = ["nichol.alexander@gmail.com", "alec.clarke.dev@gmail.com"]
|
10
10
|
|
11
11
|
spec.summary = "Wait until you have a bunch of things, then do something."
|
12
|
-
spec.description = "
|
12
|
+
spec.description = "A simple way of accumulating things and then acting on them."
|
13
13
|
spec.homepage = "https://github.com/nicholalexander/basket"
|
14
14
|
spec.license = "MIT"
|
15
15
|
spec.required_ruby_version = ">= 2.6.0"
|
@@ -18,8 +18,6 @@ Gem::Specification.new do |spec|
|
|
18
18
|
spec.metadata["source_code_uri"] = "https://github.com/nicholalexander/basket"
|
19
19
|
spec.metadata["changelog_uri"] = "https://github.com/nicholalexander/basket/blob/main/CHANGELOG.md"
|
20
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
21
|
spec.files = Dir.chdir(__dir__) do
|
24
22
|
`git ls-files -z`.split("\x0").reject do |f|
|
25
23
|
(f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features)/|\.(?:git|circleci)|appveyor)})
|
@@ -29,9 +27,8 @@ Gem::Specification.new do |spec|
|
|
29
27
|
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
30
28
|
spec.require_paths = ["lib"]
|
31
29
|
|
32
|
-
|
33
|
-
|
30
|
+
spec.add_dependency "redis"
|
31
|
+
spec.add_dependency "redis-namespace"
|
34
32
|
|
35
|
-
|
36
|
-
# guide at: https://bundler.io/guides/creating_gem.html
|
33
|
+
spec.add_development_dependency "mock_redis"
|
37
34
|
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Basket
|
2
|
+
class BackendAdapter
|
3
|
+
class HashBackend < Basket::BackendAdapter
|
4
|
+
def initialize
|
5
|
+
@data = {}
|
6
|
+
end
|
7
|
+
|
8
|
+
attr_reader :data
|
9
|
+
|
10
|
+
def push(queue, data)
|
11
|
+
@data[queue] = [] if @data[queue].nil?
|
12
|
+
@data[queue] <<= data
|
13
|
+
end
|
14
|
+
|
15
|
+
def length(queue)
|
16
|
+
return 0 if @data[queue].nil?
|
17
|
+
|
18
|
+
@data[queue].length
|
19
|
+
end
|
20
|
+
|
21
|
+
def read(queue)
|
22
|
+
@data[queue]
|
23
|
+
end
|
24
|
+
|
25
|
+
def clear(queue)
|
26
|
+
@data[queue] = []
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require "redis-namespace"
|
2
|
+
|
3
|
+
module Basket
|
4
|
+
class BackendAdapter
|
5
|
+
class RedisBackend < Basket::BackendAdapter
|
6
|
+
attr_reader :client
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
redis_connection = Redis.new(
|
10
|
+
host: Basket.config.redis_host,
|
11
|
+
port: Basket.config.redis_port,
|
12
|
+
db: Basket.config.redis_db
|
13
|
+
)
|
14
|
+
|
15
|
+
@client = Redis::Namespace.new(
|
16
|
+
Basket.config.namespace,
|
17
|
+
redis: redis_connection
|
18
|
+
)
|
19
|
+
end
|
20
|
+
|
21
|
+
def data
|
22
|
+
response = {}
|
23
|
+
|
24
|
+
@client.scan_each do |queue|
|
25
|
+
response[queue] = deserialized_queue_data(queue)
|
26
|
+
end
|
27
|
+
|
28
|
+
response
|
29
|
+
end
|
30
|
+
|
31
|
+
def push(queue, data)
|
32
|
+
# TODO: should we use JSON vs Marshal?
|
33
|
+
marshalled_data = Marshal.dump(data)
|
34
|
+
@client.lpush(queue, marshalled_data)
|
35
|
+
end
|
36
|
+
|
37
|
+
def length(queue)
|
38
|
+
@client.llen(queue)
|
39
|
+
end
|
40
|
+
|
41
|
+
def clear(queue)
|
42
|
+
@client.del(queue)
|
43
|
+
end
|
44
|
+
|
45
|
+
def read(queue)
|
46
|
+
deserialized_queue_data(queue)
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def deserialized_queue_data(queue)
|
52
|
+
@client.lrange(queue, 0, -1).reverse.map { |marshalled_data| Marshal.load(marshalled_data) }
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Basket
|
2
|
+
class BackendAdapter
|
3
|
+
def data
|
4
|
+
raise "must implement data"
|
5
|
+
end
|
6
|
+
|
7
|
+
def push(queue, data)
|
8
|
+
raise "must implement push with queue and data params"
|
9
|
+
end
|
10
|
+
|
11
|
+
def length(queue)
|
12
|
+
raise "must implement length with queue param"
|
13
|
+
end
|
14
|
+
|
15
|
+
def read(queue)
|
16
|
+
raise "must implement read with queue param"
|
17
|
+
end
|
18
|
+
|
19
|
+
def clear(queue)
|
20
|
+
raise "must implement clear with queue param"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
data/lib/basket/batcher.rb
CHANGED
@@ -1,4 +1,38 @@
|
|
1
1
|
module Basket
|
2
2
|
module Batcher
|
3
|
+
def self.included(base)
|
4
|
+
base.extend(ClassMethods)
|
5
|
+
end
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
def basket_options(args)
|
9
|
+
@basket_options = args
|
10
|
+
end
|
11
|
+
|
12
|
+
def basket_options_hash
|
13
|
+
raise Basket::Error, "You must specify the size of your basket!" if @basket_options.nil?
|
14
|
+
raise Basket::Error, "You must specify a size greater than 0" if @basket_options[:size] <= 0
|
15
|
+
|
16
|
+
@basket_options
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def batch
|
21
|
+
@batch ||= Basket.queue_collection.read(self.class.name)
|
22
|
+
end
|
23
|
+
|
24
|
+
def perform
|
25
|
+
raise Basket::Error, "You must implement perform in your Basket class."
|
26
|
+
end
|
27
|
+
|
28
|
+
def on_success
|
29
|
+
end
|
30
|
+
|
31
|
+
def on_add
|
32
|
+
end
|
33
|
+
|
34
|
+
def on_failure
|
35
|
+
raise error
|
36
|
+
end
|
3
37
|
end
|
4
38
|
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Basket
|
2
|
+
class Configuration
|
3
|
+
attr_accessor :redis_host, :redis_port, :redis_db, :namespace
|
4
|
+
attr_reader :backend
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
@redis_host = "127.0.0.1"
|
8
|
+
@redis_port = 6379
|
9
|
+
@redis_db = 15
|
10
|
+
@backend = BackendAdapter::HashBackend
|
11
|
+
@namespace = :basket
|
12
|
+
end
|
13
|
+
|
14
|
+
def backend=(backend)
|
15
|
+
case backend
|
16
|
+
when :hash
|
17
|
+
@backend = BackendAdapter::HashBackend
|
18
|
+
when :redis
|
19
|
+
@backend = BackendAdapter::RedisBackend
|
20
|
+
else
|
21
|
+
raise Basket::Error, "Unknown Backend"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/lib/basket/error.rb
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
module Basket
|
2
|
+
class HandleAdd
|
3
|
+
def self.call(queue, data)
|
4
|
+
new(queue, data).call
|
5
|
+
end
|
6
|
+
|
7
|
+
def initialize(queue, data)
|
8
|
+
@queue = queue
|
9
|
+
@data = data
|
10
|
+
@queue_collection = Basket.queue_collection
|
11
|
+
end
|
12
|
+
|
13
|
+
def call(data = @data)
|
14
|
+
queue_length = @queue_collection.push(@queue, data)
|
15
|
+
queue_class = class_for_queue
|
16
|
+
queue_instance = queue_class.new
|
17
|
+
|
18
|
+
queue_instance.define_singleton_method(:element) { data }
|
19
|
+
queue_instance.on_add
|
20
|
+
|
21
|
+
return unless basket_full?(queue_length, queue_class)
|
22
|
+
|
23
|
+
queue_instance.perform
|
24
|
+
queue_instance.on_success
|
25
|
+
@queue_collection.clear(@queue)
|
26
|
+
rescue => e
|
27
|
+
raise e if basket_error?(e)
|
28
|
+
|
29
|
+
queue_instance.define_singleton_method(:error) { e }
|
30
|
+
queue_instance.on_failure
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def class_for_queue
|
36
|
+
Object.const_get(@queue)
|
37
|
+
rescue NameError => e
|
38
|
+
raise Basket::BasketNotFoundError, "We couldn't find that basket anywhere, please make sure it is defined. | #{e.message}"
|
39
|
+
end
|
40
|
+
|
41
|
+
def basket_full?(queue_length, queue_class)
|
42
|
+
queue_length == queue_class.basket_options_hash[:size]
|
43
|
+
end
|
44
|
+
|
45
|
+
def basket_error?(e)
|
46
|
+
e.instance_of?(Basket::Error) || e.instance_of?(Basket::BasketNotFoundError)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Basket
|
2
|
+
class QueueCollection
|
3
|
+
def initialize(backend = Basket.config.backend)
|
4
|
+
@backend = backend.new
|
5
|
+
end
|
6
|
+
|
7
|
+
def push(queue, data)
|
8
|
+
@backend.push(queue, data)
|
9
|
+
length(queue)
|
10
|
+
end
|
11
|
+
|
12
|
+
def length(queue)
|
13
|
+
@backend.length(queue)
|
14
|
+
end
|
15
|
+
|
16
|
+
def read(queue)
|
17
|
+
@backend.read(queue)
|
18
|
+
end
|
19
|
+
|
20
|
+
def clear(queue)
|
21
|
+
@backend.clear(queue)
|
22
|
+
end
|
23
|
+
|
24
|
+
def data
|
25
|
+
@backend.data
|
26
|
+
end
|
27
|
+
|
28
|
+
def reset_backend
|
29
|
+
@backend = Basket.config.backend.new
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
data/lib/basket/version.rb
CHANGED
data/lib/basket.rb
CHANGED
@@ -1,18 +1,39 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative "basket/backend_adapter"
|
4
|
+
require_relative "basket/backend_adapter/hash_backend"
|
5
|
+
require_relative "basket/backend_adapter/redis_backend"
|
3
6
|
require_relative "basket/batcher"
|
4
|
-
require_relative "basket/
|
5
|
-
require_relative "basket/
|
7
|
+
require_relative "basket/configuration"
|
8
|
+
require_relative "basket/error"
|
9
|
+
require_relative "basket/handle_add"
|
10
|
+
require_relative "basket/queue_collection"
|
6
11
|
require_relative "basket/version"
|
7
12
|
|
8
13
|
module Basket
|
9
14
|
class Error < StandardError; end
|
10
15
|
|
16
|
+
def self.config
|
17
|
+
@config ||= Configuration.new
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.configure
|
21
|
+
yield(config)
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.contents
|
25
|
+
@queue_collection.data
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.queue_collection
|
29
|
+
@queue_collection ||= Basket::QueueCollection.new
|
30
|
+
end
|
31
|
+
|
11
32
|
def self.add(queue, data)
|
12
|
-
|
13
|
-
|
14
|
-
return unless queue_length == queue_class.batcher.options.queue_length
|
33
|
+
HandleAdd.call(queue, data)
|
34
|
+
end
|
15
35
|
|
16
|
-
|
36
|
+
def self.clear_all
|
37
|
+
queue_collection.reset_backend
|
17
38
|
end
|
18
39
|
end
|
metadata
CHANGED
@@ -1,18 +1,62 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: basket
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- nichol alexander
|
8
|
+
- alec clarke
|
8
9
|
autorequire:
|
9
10
|
bindir: exe
|
10
11
|
cert_chain: []
|
11
|
-
date: 2023-
|
12
|
-
dependencies:
|
13
|
-
|
12
|
+
date: 2023-03-26 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: redis
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - ">="
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: '0'
|
21
|
+
type: :runtime
|
22
|
+
prerelease: false
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - ">="
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: '0'
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: redis-namespace
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - ">="
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: '0'
|
35
|
+
type: :runtime
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - ">="
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '0'
|
42
|
+
- !ruby/object:Gem::Dependency
|
43
|
+
name: mock_redis
|
44
|
+
requirement: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - ">="
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '0'
|
49
|
+
type: :development
|
50
|
+
prerelease: false
|
51
|
+
version_requirements: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '0'
|
56
|
+
description: A simple way of accumulating things and then acting on them.
|
14
57
|
email:
|
15
|
-
-
|
58
|
+
- nichol.alexander@gmail.com
|
59
|
+
- alec.clarke.dev@gmail.com
|
16
60
|
executables: []
|
17
61
|
extensions: []
|
18
62
|
extra_rdoc_files: []
|
@@ -23,14 +67,20 @@ files:
|
|
23
67
|
- CODE_OF_CONDUCT.md
|
24
68
|
- Gemfile
|
25
69
|
- Gemfile.lock
|
70
|
+
- Guardfile
|
26
71
|
- LICENSE.txt
|
27
72
|
- README.md
|
28
73
|
- Rakefile
|
29
74
|
- basket.gemspec
|
30
75
|
- lib/basket.rb
|
76
|
+
- lib/basket/backend_adapter.rb
|
77
|
+
- lib/basket/backend_adapter/hash_backend.rb
|
78
|
+
- lib/basket/backend_adapter/redis_backend.rb
|
31
79
|
- lib/basket/batcher.rb
|
32
|
-
- lib/basket/
|
33
|
-
- lib/basket/
|
80
|
+
- lib/basket/configuration.rb
|
81
|
+
- lib/basket/error.rb
|
82
|
+
- lib/basket/handle_add.rb
|
83
|
+
- lib/basket/queue_collection.rb
|
34
84
|
- lib/basket/version.rb
|
35
85
|
- sig/basket.rbs
|
36
86
|
homepage: https://github.com/nicholalexander/basket
|
data/lib/basket/hash_backend.rb
DELETED
@@ -1,24 +0,0 @@
|
|
1
|
-
module Basket
|
2
|
-
class HashBackend
|
3
|
-
attr_reader :data
|
4
|
-
|
5
|
-
def initialize
|
6
|
-
@data = {}
|
7
|
-
end
|
8
|
-
|
9
|
-
def push(queue, data)
|
10
|
-
@data[queue] = [] if @data[queue].nil?
|
11
|
-
@data[queue] <<= data
|
12
|
-
end
|
13
|
-
|
14
|
-
def length(queue)
|
15
|
-
return 0 if @data[queue].nil?
|
16
|
-
|
17
|
-
@data[queue].length
|
18
|
-
end
|
19
|
-
|
20
|
-
def pop_all(queue)
|
21
|
-
@data.delete(queue)
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
data/lib/basket/queue.rb
DELETED
@@ -1,24 +0,0 @@
|
|
1
|
-
module Basket
|
2
|
-
class Queue
|
3
|
-
def initialize(backend = HashBackend.new)
|
4
|
-
@backend = backend
|
5
|
-
end
|
6
|
-
|
7
|
-
def push(queue, data)
|
8
|
-
@backend.push(queue, data)
|
9
|
-
length(queue)
|
10
|
-
end
|
11
|
-
|
12
|
-
def length(queue)
|
13
|
-
@backend.length(queue)
|
14
|
-
end
|
15
|
-
|
16
|
-
def pop_all(queue)
|
17
|
-
@backend.pop_all(queue)
|
18
|
-
end
|
19
|
-
|
20
|
-
def data
|
21
|
-
@backend.data
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|