├── .rspec ├── lib ├── toolbox │ ├── network.rb │ ├── version.rb │ ├── extensions │ │ ├── array.rb │ │ ├── string.rb │ │ └── range.rb │ ├── utils │ │ ├── nested_hash.rb │ │ └── deepstruct.rb │ └── network │ │ └── wait_for_socket.rb └── toolbox.rb ├── Gemfile ├── .travis.yml ├── Rakefile ├── .gitignore ├── spec ├── extensions │ ├── string_spec.rb │ ├── array_spec.rb │ └── range_spec.rb ├── utils │ ├── deepstruct_spec.rb │ └── nested_hash_spec.rb ├── network │ └── wait_for_socket_spec.rb └── spec_helper.rb ├── toolbox.gemspec ├── LICENSE.txt └── README.md /.rspec: -------------------------------------------------------------------------------- 1 | --color 2 | --require spec_helper 3 | -------------------------------------------------------------------------------- /lib/toolbox/network.rb: -------------------------------------------------------------------------------- 1 | require "toolbox/network/wait_for_socket" 2 | -------------------------------------------------------------------------------- /lib/toolbox/version.rb: -------------------------------------------------------------------------------- 1 | module Toolbox 2 | VERSION = "0.0.3" 3 | end 4 | -------------------------------------------------------------------------------- /lib/toolbox.rb: -------------------------------------------------------------------------------- 1 | require "toolbox/version" 2 | 3 | module Toolbox 4 | # Your code goes here... 5 | end 6 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | # Specify your gem's dependencies in toolbox.gemspec 4 | gemspec 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | rvm: 3 | - 2.1.0 4 | - 2.0.0 5 | - jruby-19mode 6 | - rbx-2 7 | - ruby-head 8 | - jruby-head 9 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require "bundler/gem_tasks" 2 | require "rspec/core/rake_task" 3 | 4 | RSpec::Core::RakeTask.new(:spec) 5 | 6 | task :default => :spec 7 | -------------------------------------------------------------------------------- /lib/toolbox/extensions/array.rb: -------------------------------------------------------------------------------- 1 | class Array 2 | 3 | def start_with?(sub_array) 4 | first(sub_array.size) == sub_array 5 | end 6 | 7 | end 8 | -------------------------------------------------------------------------------- /lib/toolbox/extensions/string.rb: -------------------------------------------------------------------------------- 1 | class String 2 | 3 | # Remove color formatting characters 4 | def colorless 5 | gsub(/\033\[\d+m/, "") 6 | end 7 | 8 | end 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | *.rbc 3 | .bundle 4 | .config 5 | .yardoc 6 | Gemfile.lock 7 | InstalledFiles 8 | _yardoc 9 | coverage 10 | doc/ 11 | lib/bundler/man 12 | pkg 13 | rdoc 14 | spec/reports 15 | test/tmp 16 | test/version_tmp 17 | tmp 18 | -------------------------------------------------------------------------------- /spec/extensions/string_spec.rb: -------------------------------------------------------------------------------- 1 | require "toolbox/extensions/string" 2 | 3 | describe String do 4 | 5 | describe "#colorless" do 6 | it "removes colors" do 7 | string = "Hello, World!" 8 | colored_string = "\033[32m#{string}\033[0m" 9 | 10 | expect(colored_string.colorless).to eq(string) 11 | end 12 | end 13 | 14 | end 15 | -------------------------------------------------------------------------------- /lib/toolbox/utils/nested_hash.rb: -------------------------------------------------------------------------------- 1 | module Utils 2 | class NestedHash < Hash 3 | def initialize(dimensions, *arguments, &block) 4 | if dimensions == 1 5 | super(*arguments, &block) 6 | else 7 | super() do |hash, key| 8 | hash[key] = self.class.new(dimensions - 1, *arguments, &block) 9 | end 10 | end 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /lib/toolbox/extensions/range.rb: -------------------------------------------------------------------------------- 1 | class Range 2 | 3 | # Splits a range into several ranges at the points 4 | # passed in. 5 | def split(*points) 6 | ranges = [] 7 | 8 | current_begin = self.begin 9 | 10 | points.each do |point| 11 | ranges << Range.new(current_begin, point, true) 12 | current_begin = point 13 | end 14 | 15 | ranges << Range.new(current_begin, self.end, self.exclude_end?) 16 | end 17 | 18 | end 19 | -------------------------------------------------------------------------------- /spec/utils/deepstruct_spec.rb: -------------------------------------------------------------------------------- 1 | require "toolbox/utils/deepstruct" 2 | 3 | describe Utils::DeepStruct do 4 | subject(:deepstruct) do 5 | Utils::DeepStruct.new({a: {b: {c: {d: [1,2,3,4] }}}}) 6 | end 7 | 8 | it "enables nested access to attributes using method calls" do 9 | expect(deepstruct.a.b.c.d).to eq([1,2,3,4]) 10 | end 11 | 12 | it "generates a hash of members" do 13 | expect(deepstruct.a.b.to_h).to eq(c: {d: [1,2,3,4]}) 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /lib/toolbox/network/wait_for_socket.rb: -------------------------------------------------------------------------------- 1 | require 'socket' 2 | require 'timeout' 3 | 4 | module Network 5 | module_function 6 | 7 | # Sourced from http://stackoverflow.com/questions/517219/ruby-see-if-a-port-is-open 8 | def wait_for_socket(host, port, timeout = 10) 9 | Timeout::timeout(timeout) do 10 | begin 11 | s = TCPSocket.new(host, port) 12 | s.close 13 | return true 14 | rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH 15 | sleep(0.1) 16 | retry 17 | end 18 | end 19 | rescue Timeout::Error 20 | return false 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /lib/toolbox/utils/deepstruct.rb: -------------------------------------------------------------------------------- 1 | # Source: http://andreapavoni.com/blog/2013/4/create-recursive-openstruct-from-a-ruby-hash 2 | require 'ostruct' 3 | 4 | module Utils 5 | class DeepStruct < OpenStruct 6 | def initialize(hash=nil) 7 | @table = {} 8 | @hash_table = {} 9 | 10 | if hash 11 | hash.each do |k,v| 12 | @table[k.to_sym] = (v.is_a?(Hash) ? self.class.new(v) : v) 13 | @hash_table[k.to_sym] = v 14 | 15 | new_ostruct_member(k) 16 | end 17 | end 18 | end 19 | 20 | def to_h 21 | @hash_table 22 | end 23 | 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /spec/utils/nested_hash_spec.rb: -------------------------------------------------------------------------------- 1 | require "toolbox/utils/nested_hash" 2 | 3 | RSpec.describe Utils::NestedHash do 4 | context "with 1 dimension" do 5 | subject(:nested_hash) do 6 | Utils::NestedHash.new(1) 7 | end 8 | 9 | it "works as a normal hash" do 10 | nested_hash[:a] = 1 11 | 12 | expect(nested_hash[:a]).to eq(1) 13 | expect(nested_hash[:b]).to be_nil 14 | end 15 | end 16 | 17 | context "with 2 dimensions" do 18 | subject(:nested_hash) do 19 | Utils::NestedHash.new(2) 20 | end 21 | 22 | it "has 2 dimensions" do 23 | nested_hash[:a][:b] = 1 24 | 25 | expect(nested_hash[:a][:b]).to eq(1) 26 | expect(nested_hash[:a][:c]).to be_nil 27 | 28 | expect(nested_hash[:z]).not_to be_nil 29 | end 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /spec/extensions/array_spec.rb: -------------------------------------------------------------------------------- 1 | require "toolbox/extensions/array" 2 | 3 | describe Array do 4 | 5 | describe "#start_with?" do 6 | LIST = [1,2,3,4,5,6] 7 | 8 | MATCHES = [ 9 | [], 10 | [1], 11 | [1,2], 12 | LIST, 13 | ] 14 | 15 | NON_MATCHES = [ 16 | [2], 17 | [1,3,4], 18 | [6], 19 | LIST + [7,8,9] 20 | ] 21 | 22 | context "for matching lists" do 23 | MATCHES.each do |other_list| 24 | it "#{other_list} returns true" do 25 | expect(LIST.start_with?(other_list)).to be_truthy 26 | end 27 | end 28 | end 29 | 30 | context "for non matching lists" do 31 | NON_MATCHES.each do |other_list| 32 | it "#{other_list} returns false" do 33 | expect(LIST.start_with?(other_list)).to be_falsy 34 | end 35 | end 36 | end 37 | 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /spec/network/wait_for_socket_spec.rb: -------------------------------------------------------------------------------- 1 | require "toolbox/network/wait_for_socket" 2 | 3 | require "socket" 4 | 5 | RSpec.describe Network, "#wait_for_socket" do 6 | let(:host) { "127.0.0.1" } 7 | let(:port) { 28561 } 8 | 9 | it "returns true if socket is open" do 10 | server = TCPServer.new(host, port) 11 | 12 | expect(Network.wait_for_socket(host, port, 1)).to be_truthy 13 | 14 | server.close 15 | end 16 | 17 | it "returns false if socket is closed" do 18 | expect(Network.wait_for_socket(host, port, 0.01)).to be_falsy 19 | end 20 | 21 | it "waits until the socket is open" do 22 | server_thread = Thread.new do 23 | @result = Network.wait_for_socket(host, port, 1) 24 | end 25 | 26 | expect(@result).to be_falsy 27 | server = TCPServer.new(host, port) 28 | sleep 0.11 29 | server.close 30 | 31 | server_thread.join 32 | expect(@result).to be_truthy 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /toolbox.gemspec: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | lib = File.expand_path('../lib', __FILE__) 3 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 4 | require 'toolbox/version' 5 | 6 | Gem::Specification.new do |spec| 7 | spec.name = "toolbox" 8 | spec.version = Toolbox::VERSION 9 | spec.authors = ["David Verhasselt"] 10 | spec.email = ["david@crowdway.com"] 11 | spec.description = %q{Personal gem I use to collect, categorize and easily load useful code snippets} 12 | spec.summary = %q{Personal gem I use to collect, categorize and easily load useful code snippets} 13 | spec.homepage = "" 14 | spec.license = "MIT" 15 | 16 | spec.files = `git ls-files`.split($/) 17 | spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) } 18 | spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) 19 | spec.require_paths = ["lib"] 20 | 21 | spec.add_development_dependency "bundler", "~> 1.3" 22 | spec.add_development_dependency "rake" 23 | spec.add_development_dependency "rspec" 24 | end 25 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 David Verhasselt 2 | 3 | MIT License 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /spec/extensions/range_spec.rb: -------------------------------------------------------------------------------- 1 | require "toolbox/extensions/range" 2 | require "date" 3 | 4 | describe Range do 5 | 6 | describe "#split" do 7 | context "A range of integers" do 8 | subject(:range) { Range.new(0,100) } 9 | 10 | it "splits in three" do 11 | ranges = range.split(30, 60) 12 | 13 | expect(ranges.size).to be == 3 14 | expect(ranges[0]).to eq Range.new( 0, 30, true) 15 | expect(ranges[1]).to eq Range.new(30, 60, true) 16 | expect(ranges[2]).to eq Range.new(60,100, false) 17 | end 18 | end 19 | 20 | context "A range of dates" do 21 | subject(:range) { Range.new(Date.new(2001), Date.new(2002)) } 22 | 23 | it "splits in two" do 24 | split_date = Date.new(2001,6,15) 25 | ranges = range.split(split_date) 26 | 27 | expect(ranges.size).to be == 2 28 | expect(ranges.first.end).to eq(split_date) 29 | expect(ranges.last.begin).to eq(split_date) 30 | end 31 | 32 | it "splits in four" do 33 | split_dates = [Date.new(2001,3,1), Date.new(2001,6,1), Date.new(2001,9,1)] 34 | ranges = range.split(*split_dates) 35 | 36 | expect(ranges.size).to be == 4 37 | expect(ranges[1..-1].map(&:begin)).to eq(split_dates) 38 | expect(ranges[0..-2].map(&:end)).to eq(split_dates) 39 | end 40 | end 41 | end 42 | 43 | end 44 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Toolbox 2 | 3 | Personal gem I use to distill, collect, categorize and easily load useful code snippets. This gem is not public on 'rubygems', but you can easily install it using a github link. 4 | 5 | ## Installation 6 | 7 | Add this line to your application's Gemfile: 8 | 9 | gem 'toolbox', github: 'dv/toolbox' 10 | 11 | And then execute: 12 | 13 | $ bundle 14 | 15 | ## Usage 16 | 17 | None of the code is loaded by default. Select which classes, snippets and extensions you want access to and manually add them in a require statement: 18 | 19 | ```ruby 20 | require "toolbox/extensions/range" 21 | require "toolbox/extensions/string" 22 | require "toolbox/extensions/array" 23 | require "toolbox/utils/deepstruct" 24 | require "toolbox/utils/nested_hash" 25 | require "toolbox/network/wait_for_socket" 26 | ``` 27 | 28 | # Copyright 29 | 30 | Most of the code is written by me, however sometimes I add code from other open source projects, stack overflow answers, or blog posts. These may have then been further edited by me. Wherever possible I link to the inspiration or source materials, and attribute the authors here. 31 | 32 | # Changelog 33 | 34 | v0.0.4 - 13/07/2015 - Added Array#start_with? 35 | v0.0.3 - 29/03/2015 - Added String#colorless 36 | v0.0.2 - 27/02/2015 - Added Network.wait_for_socket 37 | 38 | ## Authors 39 | 40 | * David Verhasselt 41 | * Andrea Pavoni (DeepStruct class) 42 | 43 | ## Contributing 44 | 45 | 1. Fork it 46 | 2. Create your feature branch (`git checkout -b my-new-feature`) 47 | 3. Commit your changes (`git commit -am 'Add some feature'`) 48 | 4. Push to the branch (`git push origin my-new-feature`) 49 | 5. Create new Pull Request 50 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | # This file was generated by the `rspec --init` command. Conventionally, all 2 | # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`. 3 | # The generated `.rspec` file contains `--require spec_helper` which will cause this 4 | # file to always be loaded, without a need to explicitly require it in any files. 5 | # 6 | # Given that it is always loaded, you are encouraged to keep this file as 7 | # light-weight as possible. Requiring heavyweight dependencies from this file 8 | # will add to the boot time of your test suite on EVERY test run, even for an 9 | # individual file that may not need all of that loaded. Instead, consider making 10 | # a separate helper file that requires the additional dependencies and performs 11 | # the additional setup, and require it from the spec files that actually need it. 12 | # 13 | # The `.rspec` file also contains a few flags that are not defaults but that 14 | # users commonly want. 15 | # 16 | # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration 17 | RSpec.configure do |config| 18 | # rspec-expectations config goes here. You can use an alternate 19 | # assertion/expectation library such as wrong or the stdlib/minitest 20 | # assertions if you prefer. 21 | config.expect_with :rspec do |expectations| 22 | # This option will default to `true` in RSpec 4. It makes the `description` 23 | # and `failure_message` of custom matchers include text for helper methods 24 | # defined using `chain`, e.g.: 25 | # be_bigger_than(2).and_smaller_than(4).description 26 | # # => "be bigger than 2 and smaller than 4" 27 | # ...rather than: 28 | # # => "be bigger than 2" 29 | expectations.include_chain_clauses_in_custom_matcher_descriptions = true 30 | end 31 | 32 | # rspec-mocks config goes here. You can use an alternate test double 33 | # library (such as bogus or mocha) by changing the `mock_with` option here. 34 | config.mock_with :rspec do |mocks| 35 | # Prevents you from mocking or stubbing a method that does not exist on 36 | # a real object. This is generally recommended, and will default to 37 | # `true` in RSpec 4. 38 | mocks.verify_partial_doubles = true 39 | end 40 | 41 | # The settings below are suggested to provide a good initial experience 42 | # with RSpec, but feel free to customize to your heart's content. 43 | =begin 44 | # These two settings work together to allow you to limit a spec run 45 | # to individual examples or groups you care about by tagging them with 46 | # `:focus` metadata. When nothing is tagged with `:focus`, all examples 47 | # get run. 48 | config.filter_run :focus 49 | config.run_all_when_everything_filtered = true 50 | 51 | # Limits the available syntax to the non-monkey patched syntax that is recommended. 52 | # For more details, see: 53 | # - http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax 54 | # - http://teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/ 55 | # - http://myronmars.to/n/dev-blog/2014/05/notable-changes-in-rspec-3#new__config_option_to_disable_rspeccore_monkey_patching 56 | config.disable_monkey_patching! 57 | 58 | # This setting enables warnings. It's recommended, but in some cases may 59 | # be too noisy due to issues in dependencies. 60 | config.warnings = true 61 | 62 | # Many RSpec users commonly either run the entire suite or an individual 63 | # file, and it's useful to allow more verbose output when running an 64 | # individual spec file. 65 | if config.files_to_run.one? 66 | # Use the documentation formatter for detailed output, 67 | # unless a formatter has already been configured 68 | # (e.g. via a command-line flag). 69 | config.default_formatter = 'doc' 70 | end 71 | 72 | # Print the 10 slowest examples and example groups at the 73 | # end of the spec run, to help surface which specs are running 74 | # particularly slow. 75 | config.profile_examples = 10 76 | 77 | # Run specs in random order to surface order dependencies. If you find an 78 | # order dependency and want to debug it, you can fix the order by providing 79 | # the seed, which is printed after each run. 80 | # --seed 1234 81 | config.order = :random 82 | 83 | # Seed global randomization in this process using the `--seed` CLI option. 84 | # Setting this allows you to use `--seed` to deterministically reproduce 85 | # test failures related to randomization by passing the same `--seed` value 86 | # as the one that triggered the failure. 87 | Kernel.srand config.seed 88 | =end 89 | end 90 | --------------------------------------------------------------------------------