dlinked 0.1.8 → 0.1.9

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: de491a4898093da5aac0b68079ebc40d001a60234ae752ec39250fa3d56d09d2
4
- data.tar.gz: cdaf5870bba85269d949a0471a8f4481ad1786a18cf1e1f14159439b5d6235f4
3
+ metadata.gz: 8215705b10ca404e892cc7b1d6571d96ee66aa8521941f42d6e7051582d2372a
4
+ data.tar.gz: bff1d797d11300cdae48d6f7a3623e17bb7e996db7457b270884e5e9312192b6
5
5
  SHA512:
6
- metadata.gz: 57666858cfbccc763cc732022f017fe3739292ec72b58c4c87d699bb65ea25154c5ba04458346fed4fdf53c7fa522801e2ba1c29e8bc53ea880d6d4f31109cea
7
- data.tar.gz: 2ba4f2c83fff58be2e103968482f402ac8d6c6ce1f60ca5b4256e2e9337b30248afb1de181227b2a102872307d6ea60e4faaa3d30254942b65b9bf9bb6cb92da
6
+ metadata.gz: 69e363bbc4d74898aa65673c0f46250633f9448d7c406ecfe089860a2005142905a170f0da5724d3a1766fe4ff70ddbbd2a77653be86b9fab08efd8b23a538a9
7
+ data.tar.gz: 129ab2caf81b5d8fb6d24c67ccad979fff1ce928811341f28104f5b0f851f1d05106783935d2e17bddd1bc9edf953f539332d4cc01131286495bd529a90dc65d
@@ -0,0 +1,74 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ In the interest of fostering an open and welcoming environment, we as
6
+ contributors and maintainers pledge to making participation in our project and
7
+ our community a harassment-free experience for everyone, regardless of age, body
8
+ size, disability, ethnicity, sex characteristics, gender identity and expression,
9
+ level of experience, education, socio-economic status, nationality, personal
10
+ appearance, race, religion, or sexual identity and orientation.
11
+
12
+ ## Our Standards
13
+
14
+ Examples of behavior that contributes to creating a positive environment
15
+ include:
16
+
17
+ * Using welcoming and inclusive language
18
+ * Being respectful of differing viewpoints and experiences
19
+ * Gracefully accepting constructive criticism
20
+ * Focusing on what is best for the community
21
+ * Showing empathy towards other community members
22
+
23
+ Examples of unacceptable behavior by participants include:
24
+
25
+ * The use of sexualized language or imagery and unwelcome sexual attention or
26
+ advances
27
+ * Trolling, insulting/derogatory comments, and personal or political attacks
28
+ * Public or private harassment
29
+ * Publishing others' private information, such as a physical or electronic
30
+ address, without explicit permission
31
+ * Other conduct which could reasonably be considered inappropriate in a
32
+ professional setting
33
+
34
+ ## Our Responsibilities
35
+
36
+ Project maintainers are responsible for clarifying the standards of acceptable
37
+ behavior and are expected to take appropriate and fair corrective action in
38
+ response to any instances of unacceptable behavior.
39
+
40
+ Project maintainers have the right and responsibility to remove, edit, or
41
+ reject comments, commits, code, wiki edits, issues, and other contributions
42
+ that are not aligned to this Code of Conduct, or to ban temporarily or
43
+ permanently any contributor for other behaviors that they deem inappropriate,
44
+ threatening, offensive, or harmful.
45
+
46
+ ## Scope
47
+
48
+ This Code of Conduct applies both within project spaces and in public spaces
49
+ when an individual is representing the project or its community. Examples of
50
+ representing a project or community include using an official project e-mail
51
+ address, posting via an official social media account, or acting as an appointed
52
+ representative at an online or offline event. Representation of a project may be
53
+ further defined and clarified by project maintainers.
54
+
55
+ ## Enforcement
56
+
57
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
58
+ reported by contacting the project team. All
59
+ complaints will be reviewed and investigated and will result in a response that
60
+ is deemed necessary and appropriate to the circumstances. The project team is
61
+ obligated to maintain confidentiality with regard to the reporter of an incident.
62
+ Further details of specific enforcement policies may be posted separately.
63
+
64
+ Project maintainers who do not follow or enforce the Code of Conduct in good
65
+ faith may face temporary or permanent repercussions as determined by other
66
+ members of the project's leadership.
67
+
68
+ ## Attribution
69
+
70
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.0,
71
+ available at [https://www.contributor-covenant.org/version/2/0/code_of_conduct.html][v2.0].
72
+
73
+ [homepage]: https://www.contributor-covenant.org
74
+ [v2.0]: https://www.contributor-covenant.org/version/2/0/code_of_conduct.html
data/CONTRIBUTING.md ADDED
@@ -0,0 +1,88 @@
1
+ # Contributing to DLinked
2
+
3
+ First off, thank you for considering contributing to DLinked! It's people like you that make open source such a great community.
4
+
5
+ We welcome any type of contribution, not just code. You can help with:
6
+ * **Reporting a bug**
7
+ * **Discussing the current state of the code**
8
+ * **Submitting a fix**
9
+ * **Proposing new features**
10
+ * **Becoming a maintainer**
11
+
12
+ ## Getting Started
13
+
14
+ ### Development Environment
15
+
16
+ To get started with the development environment, you'll need:
17
+ * Ruby (see `.ruby-version` for the required version)
18
+ * Bundler
19
+
20
+ 1. **Fork the repository** on GitHub.
21
+ 2. **Clone your fork** locally:
22
+ ```bash
23
+ git clone https://github.com/your-username/dlinked.git
24
+ cd dlinked
25
+ ```
26
+ 3. **Install dependencies** using Bundler:
27
+ ```bash
28
+ bundle install
29
+ ```
30
+
31
+ ### Running Tests
32
+
33
+ This project uses Minitest for testing. To run the full test suite, use the following command:
34
+
35
+ ```bash
36
+ bundle exec rake test
37
+ ```
38
+
39
+ This will also run SimpleCov to generate a code coverage report in the `coverage/` directory. When submitting a pull request, please ensure that your changes are covered by tests and that you maintain or increase the test coverage.
40
+
41
+ ### Code Style
42
+
43
+ This project uses RuboCop to enforce a consistent code style. To check your code for style violations, run:
44
+
45
+ ```bash
46
+ bundle exec rubocop
47
+ ```
48
+
49
+ Many style violations can be automatically fixed by running:
50
+
51
+ ```bash
52
+ bundle exec rubocop -A
53
+ ```
54
+
55
+ Please ensure your code follows the project's style guidelines before submitting a pull request.
56
+
57
+ ## How to Contribute
58
+
59
+ ### Reporting Bugs
60
+
61
+ If you find a bug, please open an issue on GitHub. Please include:
62
+ * A clear and descriptive title.
63
+ * A detailed description of the problem, including steps to reproduce it.
64
+ * The expected behavior and what actually happened.
65
+ * The version of DLinked and Ruby you are using.
66
+
67
+ ### Submitting Changes (Pull Requests)
68
+
69
+ 1. Create a new branch for your changes:
70
+ ```bash
71
+ git checkout -b your-feature-branch-name
72
+ ```
73
+ 2. Make your changes, and add or update tests as needed.
74
+ 3. Ensure the test suite passes (`bundle exec rake test`).
75
+ 4. Ensure your code passes the RuboCop checks (`bundle exec rubocop`).
76
+ 5. Commit your changes with a clear and descriptive commit message.
77
+ 6. Push your branch to your fork on GitHub:
78
+ ```bash
79
+ git push origin your-feature-branch-name
80
+ ```
81
+ 7. Open a pull request from your fork to the main DLinked repository.
82
+ 8. In the pull request description, please explain the problem you are solving and the changes you have made.
83
+
84
+ ## Code of Conduct
85
+
86
+ By participating in this project, you are expected to uphold our [Code of Conduct](CODE_OF_CONDUCT.md).
87
+
88
+ Thank you for your contribution!
data/README.md CHANGED
@@ -28,9 +28,18 @@ gem install dlinked
28
28
 
29
29
  ```bash
30
30
  bundle exec rake test
31
+ ```
32
+
33
+ ## Documentation
34
+
35
+ To generate the YARD documentation for this project, run:
31
36
 
37
+ ```bash
38
+ bundle exec yard doc
32
39
  ```
33
40
 
41
+ This will create a `doc/` directory containing the full HTML documentation. You can view it by opening `doc/index.html` in your browser.
42
+
34
43
  ## Usage
35
44
  ### 1. Basic Initialization and O(1) Operations
36
45
  Demonstrate creating the list, adding elements to both ends, and removing them quickly.
@@ -241,6 +250,57 @@ lru_list.clear
241
250
  lru_list.size # => 0
242
251
 
243
252
  ```
253
+
254
+ ### Real-World Example: A Complete LRU Cache
255
+ While `DLinked::CacheList` provides the low-level, high-performance key tracking, you can easily build a complete, practical `LRUCache` class around it.
256
+
257
+ The following example demonstrates how to combine `DLinked::CacheList` with a `Hash` for data storage to create a fully functional LRU cache.
258
+
259
+ ```ruby
260
+ # A complete, working implementation of a Least Recently Used (LRU) Cache
261
+ # built on top of DLinked::CacheList.
262
+ class LRUCache
263
+ attr_reader :capacity, :size
264
+
265
+ def initialize(capacity)
266
+ raise ArgumentError, 'Capacity must be a positive integer' unless capacity.is_a?(Integer) && capacity > 0
267
+
268
+ @capacity = capacity
269
+ @size = 0
270
+ @list = DLinked::CacheList.new
271
+ @data = {}
272
+ end
273
+
274
+ def get(key)
275
+ return nil unless @data.key?(key)
276
+ @list.move_to_head_by_key(key)
277
+ @data[key]
278
+ end
279
+
280
+ def set(key, value)
281
+ if @data.key?(key)
282
+ @list.move_to_head_by_key(key)
283
+ else
284
+ @list.prepend_key(key, value)
285
+ @size += 1
286
+ evict if @size > @capacity
287
+ end
288
+ @data[key] = value
289
+ end
290
+
291
+ private
292
+
293
+ def evict
294
+ lru_key = @list.pop_key
295
+ return unless lru_key
296
+ @data.delete(lru_key)
297
+ @size -= 1
298
+ end
299
+ end
300
+ ```
301
+
302
+ For a complete, runnable demonstration of this `LRUCache` class in action, see the [lru_cache_example.rb](./lru_cache_example.rb) file.
303
+
244
304
  ## ⚡ Performance Characteristics
245
305
  This library is designed to offer the guaranteed performance benefits of a Doubly Linked List over a standard Ruby `Array` for certain operations.
246
306
 
@@ -254,6 +314,34 @@ This library is designed to offer the guaranteed performance benefits of a Doubl
254
314
 
255
315
 
256
316
 
317
+ ## ⚡ Performance Benchmarks
318
+
319
+ To quantify the performance benefits of `DLinked::List` over Ruby's `Array`, a benchmark suite is available in `benchmark.rb`. It uses the `benchmark-ips` gem to compare the performance of single operations on a pre-filled data structure of 10,000 items.
320
+
321
+ The benchmark measures a single operation (e.g., `shift`) and immediately performs the inverse operation (e.g., `push`) to ensure the list size remains constant for every measurement. This provides a more accurate comparison of how each data structure handles these calls on a large collection.
322
+
323
+ You can run the benchmark yourself:
324
+
325
+ ```bash
326
+ bundle install
327
+ bundle exec ruby benchmark.rb
328
+ ```
329
+
330
+ **Results:**
331
+
332
+ The results below were generated on Ruby 3.1.4. They demonstrate that for a list of 10,000 items, the performance of Ruby's native, C-implemented `Array` is significantly faster than `DLinked::List`'s pure Ruby implementation, even for operations where the linked list has a better theoretical time complexity.
333
+
334
+ | Operation | Comparison | Analysis |
335
+ | :--- | :--- | :--- |
336
+ | `append` / `push` | `Array` is ~5.2x faster | `Array` is faster. This is expected as it's a highly optimized C implementation, while `DLinked::List` has the overhead of Ruby method calls and `Node` object allocation. |
337
+ | `prepend` / `unshift`| `Array` is ~5.1x faster | Surprisingly, `Array#unshift` is still faster at this scale. The cost of memory shifting in C for 10,000 items is lower than the overhead of `DLinked::List`'s Ruby implementation. |
338
+ | `pop` | `Array` is ~5.3x faster | Similar to `push`, the native `Array` implementation is faster for this O(1) operation. |
339
+ | `shift` | `Array` is ~5.4x faster | Like `unshift`, `Array#shift`'s O(n) operation in C is faster than `DLinked::List`'s O(1) operation in Ruby at this list size, due to the overhead of the Ruby implementation. |
340
+
341
+ **Conclusion:**
342
+
343
+ These benchmarks show that the raw speed of the underlying C implementation of `Array` outweighs the Big-O algorithmic advantages of a pure Ruby linked list for collections of this size. `DLinked::List` is better suited for educational purposes or for algorithms where the explicit node structure and pointer manipulation are more important than raw wall-clock performance against `Array`.
344
+
257
345
  ## 💾 Memory Usage
258
346
 
259
347
  While memory usage is highly dependent on the objects stored, the overhead of the list structure itself is minimal and highly efficient:
data/benchmark.rb CHANGED
@@ -1,54 +1,78 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # benchmark.rb - Compare Struct vs Class performance
4
- # Run with: ruby benchmark.rb
5
- require 'benchmark'
3
+ require 'benchmark/ips'
4
+ require 'dlinked'
6
5
 
7
- # Struct version
8
- NodeStruct = Struct.new(:value, :prev, :next)
6
+ puts "Ruby version: #{RUBY_VERSION}"
7
+ puts "DLinked version: #{DLinked::VERSION}"
9
8
 
10
- # Class version
11
- class NodeClass
12
- attr_accessor :value, :prev, :next
9
+ LIST_SIZE = 10_000
10
+ puts "\n--- Benchmarking single operations on a list of #{LIST_SIZE} items ---"
11
+ puts "A single operation is performed, and then undone to maintain list size."
13
12
 
14
- def initialize(value, prev_node, next_node)
15
- @value = value
16
- @prev = prev_node
17
- @next = next_node
13
+ # --- Setup ---
14
+ array = (0...LIST_SIZE).to_a
15
+ list = DLinked::List.new
16
+ array.each { |i| list.append(i) }
17
+
18
+ # --- Benchmark Suite ---
19
+
20
+ puts "\nAppend/Push at the end:"
21
+ Benchmark.ips do |x|
22
+ x.report("Array#push") do
23
+ array.push(0)
24
+ array.pop
18
25
  end
19
- end
20
26
 
21
- N = 2_000_000
27
+ x.report("DLinked::List#append") do
28
+ list.append(0)
29
+ list.pop
30
+ end
22
31
 
23
- puts "Creating and accessing #{N} nodes:\n\n"
32
+ x.compare!
33
+ end
24
34
 
25
- Benchmark.bm(20) do |x|
26
- x.report('Class creation:') do
27
- N.times { |i| NodeClass.new(i, nil, nil) }
35
+ puts "\nPrepend/Unshift at the beginning:"
36
+ Benchmark.ips do |x|
37
+ x.report("Array#unshift") do
38
+ array.unshift(0)
39
+ array.shift
28
40
  end
29
41
 
30
- x.report('Struct creation:') do
31
- N.times { |i| NodeStruct.new(i, nil, nil) }
42
+ x.report("DLinked::List#prepend") do
43
+ list.prepend(0)
44
+ list.shift
32
45
  end
33
46
 
34
- # Test access speed (the important part!)
35
- struct_nodes = Array.new(1000) { |i| NodeStruct.new(i, nil, nil) }
36
- class_nodes = Array.new(1000) { |i| NodeClass.new(i, nil, nil) }
47
+ x.compare!
48
+ end
37
49
 
38
- x.report('Class access:') do
39
- N.times do
40
- node = class_nodes[rand(1000)]
41
- v = node.value
42
- node.value = v + 1
43
- end
50
+ puts "\nPop from the end:"
51
+ Benchmark.ips do |x|
52
+ x.report("Array#pop") do
53
+ el = array.pop
54
+ array.push(el)
44
55
  end
45
- x.report('Struct access:') do
46
- N.times do
47
- node = struct_nodes[rand(1000)]
48
- v = node.value
49
- node.value = v + 1
50
- end
56
+
57
+ x.report("DLinked::List#pop") do
58
+ el = list.pop
59
+ list.append(el)
51
60
  end
61
+
62
+ x.compare!
52
63
  end
53
64
 
54
- puts "\nConclusion: Run this benchmark on your target Ruby version to decide!"
65
+ puts "\nShift from the beginning:"
66
+ Benchmark.ips do |x|
67
+ x.report("Array#shift") do
68
+ el = array.shift
69
+ array.push(el)
70
+ end
71
+
72
+ x.report("DLinked::List#shift") do
73
+ el = list.shift
74
+ list.append(el)
75
+ end
76
+
77
+ x.compare!
78
+ end
data/dlinked.gemspec CHANGED
@@ -38,4 +38,5 @@ Gem::Specification.new do |spec|
38
38
  spec.add_development_dependency "rubocop-minitest", "~> 0.16.0"
39
39
  spec.add_development_dependency "simplecov", "~> 0.22"
40
40
  spec.add_development_dependency "yard", "~> 0.9"
41
+ spec.add_development_dependency "benchmark-ips", "~> 2.8"
41
42
  end