duplo 0.1.0

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 93bf9116a547102fde94dcfc60589763f00f53e0
4
+ data.tar.gz: 84d1ce7ffe10cc5cf596236e648faa9d4b92eca0
5
+ SHA512:
6
+ metadata.gz: 0fed2f92b20b3bfb3261cb91573e60b56260c9fcddd2a8b9c91838a0c6f5b2bcc1f9be4a03b14aa389709cbfe24a5960f836c8a75b36f4d4da8cd923ab72e88e
7
+ data.tar.gz: fcf4f4e86ab76a7bc060c64033f9f5f5f7a842490e68796f26b39ec89e55b1f595fd59c0d63b2e1d4842e3a5dbc11d93a4c99a5a9fa09ae20d9f3d0e08d0a970
data/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ # Duplo changelog
2
+
3
+ ### 0.1.0 (2015-05-09)
4
+
5
+ * First release
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 Nikola Topalović
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,160 @@
1
+ Duplo
2
+ =====
3
+
4
+ Generating nested collections with minimum effort.
5
+
6
+ ```
7
+ .-===============-.
8
+ | ( ) ( ) ( ) ( ) |
9
+ | ( ) ( ) ( ) ( ) |
10
+ '-----------------' ndt.
11
+ ```
12
+
13
+
14
+ ## Usage
15
+
16
+ Say you like matrices (bear with me), but not rolling them out by hand
17
+ or writing nested loops to populate them. You might need a nested hash
18
+ to test something real quick in your console, but generating it can be
19
+ a royal PITA.
20
+
21
+ So how about this:
22
+
23
+ ```ruby
24
+ require "duplo"
25
+ include Duplo
26
+
27
+ a3a4
28
+ # => [[0, 1, 2, 3], [0, 1, 2, 3], [0, 1, 2, 3]]
29
+ ```
30
+
31
+ Bam. A 3x4 matrix.
32
+
33
+ Want it random? Sure. Pass it a block to populate the entries:
34
+
35
+ ```ruby
36
+ a3a4 { rand -5..5 }
37
+ # => [[1, 9, 8, 3], [3, 0, -1, -2], [2, 0, 5, -7]]
38
+ a3a4 { rand }
39
+ # => [[0.6222777300676433, 0.5613390139342668, 0.37293736375203324, 0.7319666374054961],
40
+ # [0.3798588109025701, 0.33483069318178915, 0.8779112502970073, 0.22476545143154103],
41
+ # [0.37651300630626683, 0.5035024403835663, 0.8237420938739567, 0.7611012983149591]]
42
+ ```
43
+
44
+ Accessing the current entry path is easy peasy:
45
+
46
+ ```ruby
47
+ a3a3a2 { |i,j,k| [i,j,k].join(":") }
48
+ # => [[["0:0:0", "0:0:1"], ["0:1:0", "0:1:1"], ["0:2:0", "0:2:1"]],
49
+ # [["1:0:0", "1:0:1"], ["1:1:0", "1:1:1"], ["1:2:0", "1:2:1"]],
50
+ # [["2:0:0", "2:0:1"], ["2:1:0", "2:1:1"], ["2:2:0", "2:2:1"]]]
51
+ ```
52
+
53
+ Have I mentioned that you can go up to an arbitrary number of
54
+ dimensions? Just watch out, it might get sluggish with higher dims due
55
+ to the recursive approach under the hood.
56
+
57
+ So how 'bout them Hashes:
58
+
59
+ ```ruby
60
+ h3h2h2 { |path| "I'm a #{path.join}" }
61
+ # => {0=>{0=>{0=>"I'm a 000", 1=>"I'm a 001"}, 1=>{0=>"I'm a 010", 1=>"I'm a 011"}},
62
+ # 1=>{0=>{0=>"I'm a 100", 1=>"I'm a 101"}, 1=>{0=>"I'm a 110", 1=>"I'm a 111"}},
63
+ # 2=>{0=>{0=>"I'm a 200", 1=>"I'm a 201"}, 1=>{0=>"I'm a 210", 1=>"I'm a 211"}}}
64
+ ```
65
+
66
+ You can also use `s` for Sets, and mix and match collection types to
67
+ your little heart's desire:
68
+
69
+ ```ruby
70
+ ah2s3 { abc.sample(2).join }
71
+ # => [{0=>#<Set: {"kx", "by", "fi"}>, 1=>#<Set: {"uz", "ow", "tx"}>},
72
+ # {0=>#<Set: {"tp", "ch", "ba"}>, 1=>#<Set: {"nu", "mn", "ve"}>},
73
+ # {0=>#<Set: {"nc", "dh", "dc"}>, 1=>#<Set: {"le", "ks", "th"}>},
74
+ # {0=>#<Set: {"ca", "xj", "lm"}>, 1=>#<Set: {"hg", "xg", "rz"}>},
75
+ # {0=>#<Set: {"oq", "vb", "ed"}>, 1=>#<Set: {"gq", "px", "sv"}>}]
76
+ ```
77
+
78
+ You get the picture. If you're *really* bored, you can spell those out
79
+ for fun:
80
+
81
+ ```ruby
82
+ Duplo.spell "ah2s0"
83
+ # => "5-element Array containing 2-element Hashes containing empty Sets"
84
+ ```
85
+
86
+ Note that I've omitted a dim for the root array in that last
87
+ example. It defaults to 5, so `as2h` means the same thing as
88
+ `a5s2h5`. You can easily change the default size like this:
89
+
90
+ ```ruby
91
+ Duplo.default_size = 3
92
+ ```
93
+
94
+ Also, I sneaked in a cute little `abc` method that returns the
95
+ alphabet as an array (as seen in the last example).
96
+
97
+
98
+ ## Installation
99
+
100
+ You know the drill. Add this line to your application's Gemfile
101
+ (presumably in the "development" or "test" group):
102
+
103
+ ```ruby
104
+ gem "duplo"
105
+ ```
106
+
107
+ or install it yourself as:
108
+
109
+ ```console
110
+ $ gem install duplo
111
+ ```
112
+
113
+ It's not exactly necessary to include the module, so you can work like
114
+ this:
115
+
116
+ ```ruby
117
+ Duplo.a2a2
118
+ ```
119
+
120
+ If you do include it, don't worry about monkey patching - the gem
121
+ works its magic by utilizing `method_missing`, so there should be no
122
+ name clashes.
123
+
124
+ My personal preference is to drop this in `.pryrc` (or `.irbrc`):
125
+
126
+
127
+ ```ruby
128
+ begin
129
+ require "duplo"
130
+ include Duplo
131
+ rescue LoadError
132
+ end
133
+ ```
134
+
135
+ and have those handy shortcuts available in every session, `a` and `h`
136
+ in particular.
137
+
138
+
139
+ ## Testing
140
+
141
+ To test the gem, clone the repo and run:
142
+
143
+ ```
144
+ $ bundle
145
+ $ bundle exec rake
146
+ ```
147
+
148
+
149
+ ## Contributing
150
+
151
+ 1. [Fork it](https://github.com/topalovic/duplo/fork)
152
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
153
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
154
+ 4. Push to the branch (`git push origin my-new-feature`)
155
+ 5. Create a new pull request
156
+
157
+
158
+ ## License
159
+
160
+ This gem is released under the [MIT License](http://www.opensource.org/licenses/MIT).
@@ -0,0 +1,3 @@
1
+ module Duplo
2
+ VERSION = "0.1.0"
3
+ end
data/lib/duplo.rb ADDED
@@ -0,0 +1,85 @@
1
+ require "duplo/version"
2
+ require "set"
3
+
4
+ module Duplo
5
+
6
+ TOYS = {
7
+ "a" => Array,
8
+ "s" => Set,
9
+ "h" => Hash
10
+ }
11
+
12
+ BRICK = /[#{TOYS.keys.join}]\d*/
13
+
14
+ class << self
15
+
16
+ attr_writer :default_size
17
+
18
+ def default_size
19
+ @default_size || 5
20
+ end
21
+
22
+ def add(toy, part)
23
+ case toy
24
+ when Array, Set
25
+ toy << part
26
+ when Hash
27
+ toy.store(toy.size, part)
28
+ end
29
+ end
30
+
31
+ def can_build?(toy)
32
+ !!(toy.strip =~ /\A#{BRICK}+\z/)
33
+ end
34
+
35
+ def build(part, path = [], &block)
36
+ part = part.dup
37
+ brick = part.slice! BRICK
38
+ type, size = crack brick
39
+ toy = type.new
40
+
41
+ if part.empty?
42
+ _block = block_given? ? block : proc { |path| path.last }
43
+ size.times { |i| add toy, _block.call(path + [i]) }
44
+ else
45
+ size.times { |i| add toy, build(part, path + [i], &block) }
46
+ end
47
+
48
+ toy
49
+ end
50
+
51
+ def smash(toy)
52
+ toy.scan(BRICK).map { |brick| crack brick }
53
+ end
54
+
55
+ def crack(brick)
56
+ type, size = brick.split("", 2)
57
+ size = size.empty? ? default_size : size.to_i
58
+ [TOYS[type], size]
59
+ end
60
+
61
+ def spell(toy)
62
+ smash(toy).map.with_index do |(type, size), idx|
63
+ plural = idx == 0 ? "" : type == Hash ? "es" : "s"
64
+ size.zero? ? "empty #{type}#{plural}" : "#{size}-element #{type}#{plural}"
65
+ end.join(" containing ")
66
+ end
67
+ end
68
+
69
+ module_function
70
+
71
+ def abc(n = nil)
72
+ range = "a".."z"
73
+ n ? range.take(n) : range.to_a
74
+ end
75
+
76
+ def method_missing(method_name, *arguments, &block)
77
+ toy = method_name.to_s
78
+
79
+ if Duplo.can_build? toy
80
+ Duplo.build toy, *arguments, &block
81
+ else
82
+ super
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,88 @@
1
+ require "spec_helper"
2
+
3
+ describe Duplo do
4
+
5
+ let(:default_size) { Duplo.default_size }
6
+
7
+ it "has a version number" do
8
+ expect(Duplo::VERSION).not_to be nil
9
+ end
10
+
11
+ describe ".abc" do
12
+ it "provides alphabet" do
13
+ expect(Duplo.abc).to eq ("a".."z").to_a
14
+ expect(Duplo.abc 4).to eq ("a".."d").to_a
15
+ end
16
+ end
17
+
18
+ describe ".can_build?" do
19
+ let(:valid_bricks) { %w[a aa s ss h hh a0 a1h a2s33h44] }
20
+ let(:invalid_bricks) { %w[A n 0 11 1s a2s33t44] }
21
+
22
+ it "can build valid bricks" do
23
+ valid_bricks.each do |brick|
24
+ expect(Duplo.can_build? brick).to be true
25
+ end
26
+ end
27
+
28
+ it "cannot build invalid bricks" do
29
+ invalid_bricks.each do |brick|
30
+ expect(Duplo.can_build? brick).to be false
31
+ end
32
+ end
33
+ end
34
+
35
+ describe ".build" do
36
+ let(:bricks) do
37
+ {
38
+ "a" => [0, 1, 2, 3, 4],
39
+ "h" => { 0 => 0, 1 => 1, 2 => 2, 3 => 3, 4 => 4 },
40
+ "a2a3" => [[0, 1, 2]] * 2,
41
+ "a3s2" => [[0, 1].to_set] * 3,
42
+ "a2h3" => [{ 0 => 0, 1 => 1, 2 => 2 }, { 0 => 0, 1 => 1, 2 => 2 }],
43
+ "a2h3s4" => [{ 0 => (0..3).to_set, 1 => (0..3).to_set, 2 => (0..3).to_set }] * 2
44
+ }
45
+ end
46
+
47
+ it "builds toys from bricks" do
48
+ bricks.each do |brick, toy|
49
+ expect(Duplo.build brick).to eq toy
50
+ end
51
+ end
52
+ end
53
+
54
+ describe ".smash" do
55
+ let(:bricks) do
56
+ {
57
+ "a" => [[Array, default_size]],
58
+ "aa" => [[Array, default_size], [Array, default_size]],
59
+ "a2a" => [[Array, 2], [Array, 5]],
60
+ "a2h0" => [[Array, 2], [Hash, 0]],
61
+ "a22h3s4" => [[Array, 22], [Hash, 3], [Set, 4]]
62
+ }
63
+ end
64
+
65
+ it "smashes bricks, rawr!" do
66
+ bricks.each do |brick, parts|
67
+ expect(Duplo.smash brick).to eq parts
68
+ end
69
+ end
70
+ end
71
+
72
+ describe ".spell" do
73
+ let(:bricks) do
74
+ {
75
+ "a" => "5-element Array",
76
+ "aa" => "5-element Array containing 5-element Arrays",
77
+ "a2h0" => "2-element Array containing empty Hashes",
78
+ "a22h3s4" => "22-element Array containing 3-element Hashes containing 4-element Sets"
79
+ }
80
+ end
81
+
82
+ it "spells bricks properly" do
83
+ bricks.each do |brick, description|
84
+ expect(Duplo.spell brick).to eq description
85
+ end
86
+ end
87
+ end
88
+ end
metadata ADDED
@@ -0,0 +1,80 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: duplo
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Nikola Topalović
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-05-09 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.8'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.8'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ description: Generating nested collections with minimum effort.
42
+ email:
43
+ - nikola.topalovic@gmail.com
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - CHANGELOG.md
49
+ - LICENSE
50
+ - README.md
51
+ - lib/duplo.rb
52
+ - lib/duplo/version.rb
53
+ - spec/duplo_spec.rb
54
+ homepage: https://github.com/topalovic/duplo
55
+ licenses:
56
+ - MIT
57
+ metadata: {}
58
+ post_install_message:
59
+ rdoc_options: []
60
+ require_paths:
61
+ - lib
62
+ required_ruby_version: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ version: 1.9.2
67
+ required_rubygems_version: !ruby/object:Gem::Requirement
68
+ requirements:
69
+ - - ">="
70
+ - !ruby/object:Gem::Version
71
+ version: '0'
72
+ requirements: []
73
+ rubyforge_project:
74
+ rubygems_version: 2.4.5
75
+ signing_key:
76
+ specification_version: 4
77
+ summary: Generating nested collections with minimum effort.
78
+ test_files:
79
+ - spec/duplo_spec.rb
80
+ has_rdoc: