basket 0.0.5 → 0.0.7

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d504bc414e4f443644bd06aa327dbeaabd175ff27b0ec29ef29a9c39a699f62f
4
- data.tar.gz: 11ad15d5ce03c2876d4ae11bb555df30497c5901474eeceeaf7e12a3ccedbb7b
3
+ metadata.gz: 8ac7e6fe22a043a420bf43a2dddcf0a9eee9f9fc647624e6aeecadb0f6ffb0e6
4
+ data.tar.gz: 043aa8c1033f8b58cf28ca41609397eb7872846ef139e9fb10654700ef61cefc
5
5
  SHA512:
6
- metadata.gz: 243ffd026cdf35b1e8a284c6c7f4f518f0ebb552ec89cc7fa6eca98ae481d1122db2f67d442a7e0b39e0efe89ada816f7d1d5ad2a4ae5f8eba7bde070c1dad5a
7
- data.tar.gz: e290079ae4dd146465549cb5d891eb7739dbb9e3b807f23a4d2406d4a74dbc52b1de4305dd276b98312ec7be1b1c9feb92373e47dc8d71c4ee7017d3f00258d0
6
+ metadata.gz: 8075bdd1180e9a58b5debec7a9925b1b1a08c1fdec6935e36fba4f1442cb6372c21b5d4ea02215a78a5710d87787c4cfe826ed2f8a0cd0328f25bdab08f93b52
7
+ data.tar.gz: a91e1571c99a3a20432186464eaeb4f57068cda33aeddbc912b5e358c83106a11c698fa902e20b1c4dc2809558d1d650381982d61464c900750caf15380c44ab
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 3.1.0
data/CONTRIBUTING.md ADDED
@@ -0,0 +1,10 @@
1
+ # Contributing to Basket
2
+
3
+ This is a small project. We welcome ideas, contributions, thoughts, issues, bug reports, questions and any other thing you might like to add.
4
+
5
+ If you do want to add something, open and issue and we will respond! Our current list of issues is sort of short hand between to the two authors, but we'd be happy to clarify anything if you are so interested.
6
+
7
+ We use `standardrb` for linting.
8
+ We use `rspec`.
9
+
10
+ If this is your first contribution to any open source project, we'd be happy to help you get something merged! If you have an idea or question and are new to Ruby, we would love to help you!
data/Gemfile CHANGED
@@ -5,6 +5,7 @@ source "https://rubygems.org"
5
5
  # Specify your gem's dependencies in basket.gemspec
6
6
  gemspec
7
7
 
8
+ gem "pry"
8
9
  gem "guard"
9
10
  gem "guard-rspec", require: false
10
11
  gem "guard-standardrb", require: false
@@ -13,4 +14,4 @@ gem "rake", "~> 13.0"
13
14
  gem "rspec", "~> 3.0"
14
15
  gem "simplecov", require: false, group: :test
15
16
  gem "simplecov-json", require: false, group: :test
16
- gem "standard", "~> 1.3"
17
+ gem "standard", "~> 1.27"
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- basket (0.0.5)
4
+ basket (0.0.7)
5
5
  redis
6
6
  redis-namespace
7
7
 
@@ -47,8 +47,8 @@ GEM
47
47
  notiffany (0.1.3)
48
48
  nenv (~> 0.1)
49
49
  shellany (~> 0.0)
50
- parallel (1.22.1)
51
- parser (3.2.1.0)
50
+ parallel (1.23.0)
51
+ parser (3.2.2.0)
52
52
  ast (~> 2.4.1)
53
53
  pry (0.14.2)
54
54
  coderay (~> 1.1)
@@ -64,7 +64,7 @@ GEM
64
64
  connection_pool
65
65
  redis-namespace (1.10.0)
66
66
  redis (>= 4)
67
- regexp_parser (2.7.0)
67
+ regexp_parser (2.8.0)
68
68
  rexml (3.2.5)
69
69
  rspec (3.12.0)
70
70
  rspec-core (~> 3.12.0)
@@ -79,22 +79,22 @@ GEM
79
79
  diff-lcs (>= 1.2.0, < 2.0)
80
80
  rspec-support (~> 3.12.0)
81
81
  rspec-support (3.12.0)
82
- rubocop (1.44.1)
82
+ rubocop (1.50.2)
83
83
  json (~> 2.3)
84
84
  parallel (~> 1.10)
85
85
  parser (>= 3.2.0.0)
86
86
  rainbow (>= 2.2.2, < 4.0)
87
87
  regexp_parser (>= 1.8, < 3.0)
88
88
  rexml (>= 3.2.5, < 4.0)
89
- rubocop-ast (>= 1.24.1, < 2.0)
89
+ rubocop-ast (>= 1.28.0, < 2.0)
90
90
  ruby-progressbar (~> 1.7)
91
91
  unicode-display_width (>= 2.4.0, < 3.0)
92
- rubocop-ast (1.26.0)
92
+ rubocop-ast (1.28.0)
93
93
  parser (>= 3.2.1.0)
94
- rubocop-performance (1.15.2)
94
+ rubocop-performance (1.16.0)
95
95
  rubocop (>= 1.7.0, < 2.0)
96
96
  rubocop-ast (>= 0.4.0)
97
- ruby-progressbar (1.11.0)
97
+ ruby-progressbar (1.13.0)
98
98
  ruby2_keywords (0.0.5)
99
99
  shellany (0.0.1)
100
100
  simplecov (0.22.0)
@@ -106,10 +106,10 @@ GEM
106
106
  json
107
107
  simplecov
108
108
  simplecov_json_formatter (0.1.4)
109
- standard (1.24.3)
109
+ standard (1.27.0)
110
110
  language_server-protocol (~> 3.17.0.2)
111
- rubocop (= 1.44.1)
112
- rubocop-performance (= 1.15.2)
111
+ rubocop (~> 1.50.2)
112
+ rubocop-performance (~> 1.16.0)
113
113
  standardrb (1.0.1)
114
114
  standard
115
115
  thor (1.2.1)
@@ -128,11 +128,12 @@ DEPENDENCIES
128
128
  guard-standardrb
129
129
  mock_redis
130
130
  mocktail
131
+ pry
131
132
  rake (~> 13.0)
132
133
  rspec (~> 3.0)
133
134
  simplecov
134
135
  simplecov-json
135
- standard (~> 1.3)
136
+ standard (~> 1.27)
136
137
 
137
138
  BUNDLED WITH
138
139
  2.4.6
data/README.md CHANGED
@@ -22,6 +22,7 @@ If bundler is not being used to manage dependencies, install the gem by executin
22
22
  $ gem install basket
23
23
 
24
24
  ## Usage
25
+ ### Adding
25
26
 
26
27
  Add items to your basket as they come along. They might come along quickly, or there might be a delay between them. Regardless, you want to collect items into your basket before going and doing something with them.
27
28
 
@@ -32,7 +33,11 @@ while chicken.laying? do
32
33
  end
33
34
  ```
34
35
 
35
- The item added to the basket can be any data you want! If you are using the in memory Queue, it is fine to store Ruby objects, but if you have a different backend, it might be better to stick to easily serializable objects.
36
+ The item added to the basket can be any data you want! If you are using the in memory Queue, it is fine to store Ruby objects, but if you have a different backend, must be JSON serializable via `to_json`.
37
+
38
+ ### Your basket
39
+
40
+ When a basket has become full after you have added a bunch of things to it, it performs actions! See below for the full definition of a basket.
36
41
 
37
42
  ```ruby
38
43
  class QuicheBasket
@@ -84,6 +89,51 @@ The `on_failure` use of `batch` of course may not have a full batch as the error
84
89
 
85
90
  Defining `on_add`, `on_failure`, and `on_success` is optional.
86
91
 
92
+ ### Search
93
+
94
+ You may search through your basket, if for example, you need to see if you've accidentally collected a robin egg and not a chicken egg!
95
+
96
+ ```ruby
97
+ search_results = Basket.search("QuicheBasket") do |egg|
98
+ egg.color == "blue"
99
+ end
100
+ ```
101
+
102
+ The block you pass will match against the objects in your basket. If you have ruby objects in your basket, you can match against their properties just as if you were accessing them one at a time. If you have json objects in your basket, you will be searching through a hash thusly:
103
+
104
+ ```ruby
105
+ search_results = Basket.search("PlaylistBasket") do |song|
106
+ song[:artist] == "Vansire"
107
+ end
108
+ ```
109
+
110
+ The search results will be a fully qualified basket element which will contain an id attribute and a data attribute. In the case of using the MemoryBackend, you might see something like this:
111
+
112
+ ```ruby
113
+ # ...continued from above
114
+ search_results.first #=> #<Basket::Element:0x00000001075d9c80
115
+ # @data=#<Egg color="blue", size="smol">
116
+ # @id="5fe3df9e-4063-4b67-a08f-e36b847087c7">
117
+ ```
118
+
119
+ You'll note that the result of a search is an array of basket elements. An element consists of the data that you put in and ID. What is the id for? Glad you asked.
120
+
121
+ ### Remove
122
+
123
+ You can also remove something from your basket. Perhaps it is deleted in the database and no longer contains a valid reference to data? Perhaps you found that robin egg and don't actually want to use it to make a quiche, because who would? Either way, removing the element is easy!
124
+
125
+ ```ruby
126
+ # ...continued from above
127
+ element_id_to_remove = search_results.first
128
+ removed_egg = Basket.remove('Quiche', element_id_to_remove)
129
+ removed_egg #=> #<Egg color="blue", size="smol">
130
+ ```
131
+
132
+ Voila!
133
+
134
+ ### A Note of Warning
135
+
136
+ Searching for and removing elements from your basket is an inherently tricky process as your basket may fill up and execute the `perform` action while searching and removing.
87
137
  ## Configuration
88
138
 
89
139
  In an initializer, or somewhere equally appropriate, you might put something like this:
@@ -100,7 +150,7 @@ end
100
150
 
101
151
  The defaults for a redis backend are the standard `"127.0.0.1"`, `6379`, `15` with a namespace of `:basket`.
102
152
 
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!
153
+ The default for the backend is the MemoryBackend, which can be set by passing `:memory` to `config.backend`, but you don't have to do that. Because it's the default!
104
154
 
105
155
  For the redis configuration, you can alternatively pass a url, thusly:
106
156
 
@@ -132,5 +182,11 @@ The gem is available as open source under the terms of the [MIT License](https:/
132
182
 
133
183
  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).
134
184
 
185
+ ## Thanks
186
+
187
+ This project has been a fun and educational use of Growth Time at [Test Double](https://testdouble.com/) where consultants are given 4hrs per week to grow their skills and work on projects that further the mission of building software, better.
135
188
 
189
+ ## Authors
136
190
 
191
+ * [Nichol Alexander](https://github.com/nicholalexander/)
192
+ * [Alec Clarke](https://github.com/alecclarke)
data/SECURITY.md ADDED
@@ -0,0 +1,16 @@
1
+ # Security Policy
2
+
3
+ ## Supported Versions
4
+
5
+ We support Ruby versions as below:
6
+
7
+ | Version | Supported |
8
+ | ------- | ------------------ |
9
+ | 3.2.2 | :white_check_mark: |
10
+ | 3.1.4 | :white_check_mark: |
11
+ | 3.0.6 | :white_check_mark: |
12
+ | < 4.0 | :x: |
13
+
14
+ ## Reporting a Vulnerability
15
+
16
+ If you discover a security related bug, please don't file an issue for it and instead let us know directly!
@@ -1,6 +1,6 @@
1
1
  module Basket
2
2
  class BackendAdapter
3
- class HashBackend < Basket::BackendAdapter
3
+ class MemoryBackend < Basket::BackendAdapter
4
4
  def initialize
5
5
  @data = {}
6
6
  end
@@ -22,6 +22,17 @@ module Basket
22
22
  @data[queue]
23
23
  end
24
24
 
25
+ def search(queue, &block)
26
+ @data[queue].select { |element| block.call(element.data) }
27
+ end
28
+
29
+ def remove(queue, id)
30
+ index_of_element_to_delete = @data[queue].index { |element| element.id == id }
31
+ @data[queue].delete_at(index_of_element_to_delete)
32
+ rescue
33
+ nil
34
+ end
35
+
25
36
  def clear(queue)
26
37
  @data[queue] = []
27
38
  end
@@ -24,10 +24,20 @@ module Basket
24
24
  response
25
25
  end
26
26
 
27
+ def search(queue, &block)
28
+ deserialized_queue_data(queue).select { |raw_element| block.call(raw_element["data"]) }
29
+ end
30
+
31
+ def remove(queue, element_id)
32
+ element = deserialized_queue_data(queue).find { |raw_element| raw_element["id"] == element_id }
33
+
34
+ @client.lrem(queue, 1, element.to_json)
35
+
36
+ element
37
+ end
38
+
27
39
  def push(queue, data)
28
- # TODO: should we use JSON vs Marshal?
29
- marshalled_data = Marshal.dump(data)
30
- @client.lpush(queue, marshalled_data)
40
+ @client.lpush(queue, serialize_data(data))
31
41
  end
32
42
 
33
43
  def length(queue)
@@ -44,8 +54,12 @@ module Basket
44
54
 
45
55
  private
46
56
 
57
+ def serialize_data(data)
58
+ JSON.generate(data)
59
+ end
60
+
47
61
  def deserialized_queue_data(queue)
48
- @client.lrange(queue, 0, -1).reverse.map { |marshalled_data| Marshal.load(marshalled_data) }
62
+ @client.lrange(queue, 0, -1).reverse.map { |serialized_data| JSON.parse(serialized_data) }
49
63
  end
50
64
 
51
65
  def select_redis_connection
@@ -7,15 +7,15 @@ module Basket
7
7
  @redis_host = "127.0.0.1"
8
8
  @redis_port = 6379
9
9
  @redis_db = 15
10
- @backend = BackendAdapter::HashBackend
10
+ @backend = BackendAdapter::MemoryBackend
11
11
  @namespace = :basket
12
12
  @redis_url = nil
13
13
  end
14
14
 
15
15
  def backend=(backend)
16
16
  case backend
17
- when :hash
18
- @backend = BackendAdapter::HashBackend
17
+ when :memory
18
+ @backend = BackendAdapter::MemoryBackend
19
19
  when :redis
20
20
  @backend = BackendAdapter::RedisBackend
21
21
  else
@@ -0,0 +1,38 @@
1
+ require "securerandom"
2
+
3
+ module Basket
4
+ class Element
5
+ class InvalidElement < StandardError; end
6
+
7
+ attr_reader :data, :id
8
+
9
+ def self.from_queue(element)
10
+ if element.is_a?(Element)
11
+ element
12
+ elsif element.is_a?(Hash)
13
+ new(element["data"], element["id"])
14
+ else
15
+ raise InvalidElement, "element must be a hash or a Basket::Element"
16
+ end
17
+ end
18
+
19
+ def initialize(data, id = SecureRandom.uuid)
20
+ raise InvalidElement, "both data and id must be present" unless data && id
21
+
22
+ @data = data
23
+ @id = id
24
+ end
25
+
26
+ def to_h
27
+ {data: data, id: id}
28
+ end
29
+
30
+ def to_json(*)
31
+ to_h.to_json
32
+ end
33
+
34
+ def ==(other)
35
+ to_h == other.to_h
36
+ end
37
+ end
38
+ end
data/lib/basket/error.rb CHANGED
@@ -2,4 +2,8 @@ module Basket
2
2
  class Error < StandardError; end
3
3
 
4
4
  class BasketNotFoundError < Error; end
5
+
6
+ class EmptyBasketError < Error; end
7
+
8
+ class ElementNotFoundError < Error; end
5
9
  end
@@ -5,7 +5,7 @@ module Basket
5
5
  end
6
6
 
7
7
  def push(queue, data)
8
- @backend.push(queue, data)
8
+ @backend.push(queue, Element.new(data))
9
9
  length(queue)
10
10
  end
11
11
 
@@ -14,7 +14,22 @@ module Basket
14
14
  end
15
15
 
16
16
  def read(queue)
17
- @backend.read(queue)
17
+ check_for_basket(queue)
18
+ raw_queue = @backend.read(queue)
19
+ raw_queue.map { |element| Element.from_queue(element).data }
20
+ end
21
+
22
+ def search(queue, query)
23
+ check_for_basket(queue)
24
+ check_for_zero_length(queue)
25
+ raw_search_results = @backend.search(queue, &query)
26
+ raw_search_results.map { |raw_search_result| Element.from_queue(raw_search_result) }
27
+ end
28
+
29
+ def remove(queue, id)
30
+ raw_removed_element = @backend.remove(queue, id)
31
+ check_for_raw_removed_element(raw_removed_element)
32
+ Element.from_queue(raw_removed_element).data
18
33
  end
19
34
 
20
35
  def clear(queue)
@@ -28,5 +43,19 @@ module Basket
28
43
  def reset_backend
29
44
  @backend = Basket.config.backend.new
30
45
  end
46
+
47
+ private
48
+
49
+ def check_for_basket(queue)
50
+ raise Basket::BasketNotFoundError unless Object.const_defined?(queue)
51
+ end
52
+
53
+ def check_for_zero_length(queue)
54
+ raise Basket::EmptyBasketError, "The basket #{queue} is empty." if length(queue).zero?
55
+ end
56
+
57
+ def check_for_raw_removed_element(raw_removed_element)
58
+ raise Basket::ElementNotFoundError if raw_removed_element.nil?
59
+ end
31
60
  end
32
61
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Basket
4
- VERSION = "0.0.5"
4
+ VERSION = "0.0.7"
5
5
  end
data/lib/basket.rb CHANGED
@@ -1,15 +1,18 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "basket/backend_adapter"
4
- require_relative "basket/backend_adapter/hash_backend"
4
+ require_relative "basket/backend_adapter/memory_backend"
5
5
  require_relative "basket/backend_adapter/redis_backend"
6
6
  require_relative "basket/batcher"
7
7
  require_relative "basket/configuration"
8
+ require_relative "basket/element"
8
9
  require_relative "basket/error"
9
10
  require_relative "basket/handle_add"
10
11
  require_relative "basket/queue_collection"
11
12
  require_relative "basket/version"
12
13
 
14
+ require "json"
15
+
13
16
  module Basket
14
17
  class Error < StandardError; end
15
18
 
@@ -25,6 +28,10 @@ module Basket
25
28
  @queue_collection.data
26
29
  end
27
30
 
31
+ def self.peek(queue)
32
+ queue_collection.read(queue)
33
+ end
34
+
28
35
  def self.queue_collection
29
36
  @queue_collection ||= Basket::QueueCollection.new
30
37
  end
@@ -33,6 +40,14 @@ module Basket
33
40
  HandleAdd.call(queue, data)
34
41
  end
35
42
 
43
+ def self.search(queue, &query)
44
+ queue_collection.search(queue, query)
45
+ end
46
+
47
+ def self.remove(queue, id)
48
+ queue_collection.remove(queue, id)
49
+ end
50
+
36
51
  def self.clear_all
37
52
  queue_collection.reset_backend
38
53
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: basket
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.5
4
+ version: 0.0.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - nichol alexander
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: exe
11
11
  cert_chain: []
12
- date: 2023-04-04 00:00:00.000000000 Z
12
+ date: 2023-04-24 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: redis
@@ -62,22 +62,26 @@ extensions: []
62
62
  extra_rdoc_files: []
63
63
  files:
64
64
  - ".rspec"
65
+ - ".ruby-version"
65
66
  - ".standard.yml"
66
67
  - CHANGELOG.md
67
68
  - CODE_OF_CONDUCT.md
69
+ - CONTRIBUTING.md
68
70
  - Gemfile
69
71
  - Gemfile.lock
70
72
  - Guardfile
71
73
  - LICENSE.txt
72
74
  - README.md
73
75
  - Rakefile
76
+ - SECURITY.md
74
77
  - basket.gemspec
75
78
  - lib/basket.rb
76
79
  - lib/basket/backend_adapter.rb
77
- - lib/basket/backend_adapter/hash_backend.rb
80
+ - lib/basket/backend_adapter/memory_backend.rb
78
81
  - lib/basket/backend_adapter/redis_backend.rb
79
82
  - lib/basket/batcher.rb
80
83
  - lib/basket/configuration.rb
84
+ - lib/basket/element.rb
81
85
  - lib/basket/error.rb
82
86
  - lib/basket/handle_add.rb
83
87
  - lib/basket/queue_collection.rb
@@ -105,7 +109,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
105
109
  - !ruby/object:Gem::Version
106
110
  version: '0'
107
111
  requirements: []
108
- rubygems_version: 3.4.7
112
+ rubygems_version: 3.3.3
109
113
  signing_key:
110
114
  specification_version: 4
111
115
  summary: Wait until you have a bunch of things, then do something.