attribute_struct 0.1.2 → 0.1.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,3 +1,10 @@
1
+ ## v0.1.4
2
+ * Add `_array` helper
3
+ * Ensure all helpers are using underscore prefix
4
+ * Inherit from BasicObject
5
+ * Allow struct discovery in enumerable objects
6
+ * Add more helpers
7
+
1
8
  ## v0.1.2
2
9
  * Fix naming in require
3
10
 
data/Gemfile CHANGED
@@ -1,3 +1,5 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
+ gem 'minitest'
4
+
3
5
  gemspec
@@ -1,16 +1,18 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- attribute_struct (0.1.0)
4
+ attribute_struct (0.1.3)
5
5
  hashie (~> 2.0.5)
6
6
 
7
7
  GEM
8
8
  remote: https://rubygems.org/
9
9
  specs:
10
10
  hashie (2.0.5)
11
+ minitest (5.0.6)
11
12
 
12
13
  PLATFORMS
13
14
  ruby
14
15
 
15
16
  DEPENDENCIES
16
17
  attribute_struct!
18
+ minitest
data/README.md CHANGED
@@ -4,6 +4,10 @@ This is a helper library that essentially builds hashes. It
4
4
  wraps hash building with a nice DSL to make it slightly cleaner,
5
5
  more robust, and provide extra features.
6
6
 
7
+ ## Build Status
8
+
9
+ * [![Build Status](https://api.travis-ci.org/chrisroberts/attribute_struct.png)](https://travis-ci.org/chrisroberts/attribute_struct)
10
+
7
11
  ### Usage
8
12
 
9
13
  ```ruby
@@ -1,2 +1,2 @@
1
1
  require 'attribute_struct/version'
2
- require 'attribute_struct/attribute_struct'
2
+
@@ -2,8 +2,8 @@ require 'hashie/extensions/deep_merge'
2
2
  require 'hashie/extensions/indifferent_access'
3
3
 
4
4
  class AttributeStruct
5
- class AttributeHash < Hash
6
- include Hashie::Extensions::DeepMerge
7
- include Hashie::Extensions::IndifferentAccess
5
+ class AttributeHash < ::Hash
6
+ include ::Hashie::Extensions::DeepMerge
7
+ include ::Hashie::Extensions::IndifferentAccess
8
8
  end
9
9
  end
@@ -1,15 +1,20 @@
1
- class AttributeStruct
1
+ class AttributeStruct < BasicObject
2
2
 
3
3
  class << self
4
-
4
+
5
+ # Global flag for camel cased keys
5
6
  attr_reader :camel_keys
7
+ # Force tooling from Chef
6
8
  attr_accessor :force_chef
7
9
 
10
+ # val:: bool
11
+ # Automatically converts keys to camel case
8
12
  def camel_keys=(val)
9
13
  load_the_camels if val
10
14
  @camel_keys = !!val
11
15
  end
12
16
 
17
+ # Loads helpers for camel casing
13
18
  def load_the_camels
14
19
  unless(@camels_loaded)
15
20
  require 'attribute_struct/monkey_camels'
@@ -17,6 +22,7 @@ class AttributeStruct
17
22
  end
18
23
  end
19
24
 
25
+ # Determines what hash library to load based on availability
20
26
  def load_the_hash
21
27
  unless(@hash_loaded)
22
28
  if(defined?(Chef) || force_chef)
@@ -25,34 +31,46 @@ class AttributeStruct
25
31
  else
26
32
  require 'attribute_struct/attribute_hash'
27
33
  end
28
- @hash_loaded
34
+ @hash_loaded = true
29
35
  end
30
36
  end
31
37
 
32
38
  end
33
39
 
40
+ # Flag for camel cased keys
34
41
  attr_reader :_camel_keys
35
42
 
36
43
  def initialize(*args, &block)
37
- self.class.load_the_hash
38
- @_camel_keys = self.class.camel_keys
44
+ _klass.load_the_hash
45
+ @_camel_keys = _klass.camel_keys
39
46
  @table = __hashish.new
40
47
  unless(args.empty?)
41
- if(args.size == 1 && args.first.is_a?(Hash))
48
+ if(args.size == 1 && args.first.is_a?(::Hash))
42
49
  _load(args.first)
43
50
  end
44
51
  end
52
+ if(block)
53
+ self.instance_exec(&block)
54
+ end
45
55
  end
46
56
 
57
+ # val:: bool
58
+ # Turn camel cased keys on/off
47
59
  def _camel_keys=(val)
48
- self.class.load_the_camels if val
60
+ _klass.load_the_camels if val
49
61
  @_camel_keys = !!val
50
62
  end
51
-
63
+
64
+ # key:: Object
65
+ # Access data directly
52
66
  def [](key)
53
67
  _data[_process_key(key)]
54
68
  end
55
69
 
70
+ # key:: Object
71
+ # val:: Object
72
+ # Directly set val into struct. Useful when key is not valid syntax
73
+ # for a ruby method
56
74
  def _set(key, val=nil, &block)
57
75
  if(val)
58
76
  self.method_missing(key, val, &block)
@@ -60,14 +78,15 @@ class AttributeStruct
60
78
  self.method_missing(key, &block)
61
79
  end
62
80
  end
63
-
81
+
82
+ # Dragons and unicorns all over in here
64
83
  def method_missing(sym, *args, &block)
65
84
  if((s = sym.to_s).end_with?('='))
66
85
  s.slice!(-1, s.length)
67
86
  sym = s
68
87
  end
69
88
  sym = _process_key(sym)
70
- @table[sym] ||= AttributeStruct.new
89
+ @table[sym] ||= _klass_new
71
90
  if(!args.empty? || block)
72
91
  if(args.empty? && block)
73
92
  base = @table[sym]
@@ -79,13 +98,13 @@ class AttributeStruct
79
98
  @table[sym] = base
80
99
  elsif(!args.empty? && block)
81
100
  base = @table[sym]
82
- base = self.class.new unless base.is_a?(self.class)
101
+ base = _klass_new unless base.is_a?(_klass)
83
102
  @table[sym] = base
84
103
  leaf = base
85
104
  args.each do |arg|
86
105
  leaf = base[arg]
87
- unless(leaf.is_a?(self.class))
88
- leaf = self.class.new
106
+ unless(leaf.is_a?(_klass))
107
+ leaf = _klass_new
89
108
  base._set(arg, leaf)
90
109
  base = leaf
91
110
  end
@@ -96,36 +115,81 @@ class AttributeStruct
96
115
  leaf.instance_exec(leaf, &block)
97
116
  end
98
117
  else
99
- @table[sym] = args.first
118
+ if(args.size > 1)
119
+ @table[sym] = _klass_new unless @table[sym].is_a?(_klass)
120
+ endpoint = args.inject(@table[sym]) do |memo, k|
121
+ unless(memo[k].is_a?(_klass))
122
+ memo._set(k, _klass_new)
123
+ end
124
+ memo[k]
125
+ end
126
+ return endpoint # custom break out
127
+ else
128
+ @table[sym] = args.first
129
+ end
100
130
  end
101
131
  end
102
132
  @table[sym]
103
133
  end
104
134
 
135
+ # Returns if this struct is considered nil (empty data)
105
136
  def nil?
106
137
  _data.empty?
107
138
  end
108
139
 
140
+ # klass:: Class
141
+ # Returns if this struct is a klass
142
+ def is_a?(klass)
143
+ klass.ancestors.include?(_klass)
144
+ end
145
+ alias_method :kind_of?, :is_a?
146
+
147
+ # Returns current keys within struct
109
148
  def _keys
110
149
  _data.keys
111
150
  end
112
-
151
+
152
+ # Returns underlying data hash
113
153
  def _data
114
154
  @table
115
155
  end
116
156
 
157
+ # key:: Object
158
+ # Delete entry in struct with key
159
+ def _delete(key)
160
+ _data.delete(_process_key(key))
161
+ end
162
+
163
+ # Dumps the current instance to a Hash
117
164
  def _dump
118
- __hashish[
119
- *(@table.map{|key, value|
120
- [key, value.is_a?(self.class) ? value._dump : value]
121
- }.flatten(1))
122
- ]
165
+ processed = @table.map do |key, value|
166
+ if(value.is_a?(::Enumerable))
167
+ flat = value.map do |v|
168
+ v.is_a?(_klass) ? v._dump : v
169
+ end
170
+ val = value.is_a?(::Hash) ? __hashish[*flat.flatten(1)] : flat
171
+ elsif(value.is_a?(_klass))
172
+ val = value._dump
173
+ else
174
+ val = value
175
+ end
176
+ [key, val]
177
+ end
178
+ __hashish[*processed.flatten(1)]
123
179
  end
124
180
 
181
+ # hashish:: Hash type object
182
+ # Clears current instance data and replaces with provided hash
125
183
  def _load(hashish)
126
184
  @table.clear
127
185
  hashish.each do |key, value|
128
- if(value.is_a?(Hash))
186
+ if(value.is_a?(::Enumerable))
187
+ flat = value.map do |v|
188
+ v.is_a?(::Hash) ? _klass.new(v) : v
189
+ end
190
+ value = value.is_a?(::Hash) ? __hashish[*flat.flatten(1)] : flat
191
+ end
192
+ if(value.is_a?(::Hash))
129
193
  self._set(key)._load(value)
130
194
  else
131
195
  self._set(key, value)
@@ -134,60 +198,124 @@ class AttributeStruct
134
198
  self
135
199
  end
136
200
 
201
+ # target:: AttributeStruct
202
+ # Performs a deep merge and returns the resulting AttributeStruct
137
203
  def _merge(target)
138
- source = deep_copy
139
- dest = target.deep_copy
140
- if(defined?(Mash))
141
- result = Chef::Mixin::DeepMerge.merge(source, dest)
204
+ source = _deep_copy
205
+ dest = target._deep_copy
206
+ if(defined?(::Mash))
207
+ result = ::Chef::Mixin::DeepMerge.merge(source, dest)
142
208
  else
143
209
  result = source.deep_merge(dest)
144
210
  end
145
- AttributeStruct.new(result)
211
+ _klass.new(result)
146
212
  end
147
213
 
214
+ # target:: AttributeStruct
215
+ # Performs a deep merge and updates the current instance with the
216
+ # resulting value
148
217
  def _merge!(target)
149
218
  result = _merge(target)._dump
150
219
  _load(result)
151
220
  self
152
221
  end
153
-
222
+
223
+ # Returns a new Hash type instance based on what is available
154
224
  def __hashish
155
- defined?(Mash) ? Mash : AttributeHash
225
+ defined?(::Mash) ? ::Mash : ::AttributeStruct::AttributeHash
156
226
  end
157
-
158
- def do_dup(v)
227
+
228
+ # Returns dup of value. Converts Symbol objects to strings
229
+ def _do_dup(v)
159
230
  begin
160
231
  v.dup
161
232
  rescue
162
- v.is_a?(Symbol) ? v.to_s : v
233
+ v.is_a?(::Symbol) ? v.to_s : v
163
234
  end
164
235
  end
165
236
 
166
- def deep_copy(thing=nil)
237
+ # thing:: Object
238
+ # Returns a proper deep copy
239
+ def _deep_copy(thing=nil)
167
240
  thing ||= _dump
168
- if(thing.is_a?(Enumerable))
169
- val = thing.map{|v| v.is_a?(Enumerable) ? deep_copy(v) : do_dup(v) }
241
+ if(thing.is_a?(::Enumerable))
242
+ val = thing.map{|v| v.is_a?(::Enumerable) ? _deep_copy(v) : _do_dup(v) }
170
243
  else
171
- val = do_dup(thing)
244
+ val = _do_dup(thing)
172
245
  end
173
- if(thing.is_a?(Hash))
246
+ if(thing.is_a?(::Hash))
174
247
  val = __hashish[*val.flatten(1)]
175
248
  end
176
249
  val
177
250
  end
178
251
 
179
- def _process_key(key)
252
+ # key:: String or Symbol
253
+ # Processes the key and returns value based on current settings
254
+ def _process_key(key, *args)
180
255
  key = key.to_s
181
- if(_camel_keys && key._camel?)
256
+ if(_camel_keys && _camel_keys_action)
257
+ case _camel_keys_action
258
+ when :auto_disable
259
+ key._no_hump
260
+ when :auto_enable
261
+ key._hump
262
+ end
263
+ end
264
+ if((_camel_keys && key._camel?) || args.include?(:force))
182
265
  key.to_s.split('_').map do |part|
183
266
  "#{part[0,1].upcase}#{part[1,part.size]}"
184
267
  end.join.to_sym
185
268
  else
186
269
  if(_camel_keys)
187
270
  # Convert so Hash doesn't make a new one and lose the meta
188
- key = CamelString.new(key) unless key.is_a?(CamelString)
271
+ key = ::CamelString.new(key) unless key.is_a?(::CamelString)
189
272
  end
190
273
  key
191
274
  end
192
275
  end
276
+
277
+ # Helper to return class of current instance
278
+ def _klass
279
+ ::AttributeStruct
280
+ end
281
+ alias_method :class, :_klass
282
+
283
+ # Helper to return new instance of current instance type
284
+ def _klass_new
285
+ n = _klass.new
286
+ n._camel_keys_set(_camel_keys_action)
287
+ n
288
+ end
289
+
290
+ # v:: Symbol (:auto_disable, :auto_enable)
291
+ # Sets custom rule for processed keys
292
+ def _camel_keys_set(v)
293
+ @_camel_keys_set = v
294
+ end
295
+
296
+ # Returns value set via #_camel_keys_set
297
+ def _camel_keys_action
298
+ @_camel_keys_set
299
+ end
300
+
301
+ # args:: Objects
302
+ # Helper to create Arrays with nested AttributeStructs. Proc
303
+ # instances are automatically executed into new AttributeStruct
304
+ # instances
305
+ def _array(*args)
306
+ args.map do |maybe_block|
307
+ if(maybe_block.is_a?(::Proc))
308
+ klass = _klass_new
309
+ if(maybe_block.arity > 0)
310
+ klass.instance_exec(klass, &maybe_block)
311
+ else
312
+ klass.instance_exec(&maybe_block)
313
+ end
314
+ klass
315
+ else
316
+ maybe_block
317
+ end
318
+ end
319
+ end
320
+
193
321
  end
@@ -1,6 +1,8 @@
1
+ require 'attribute_struct/attribute_struct'
2
+
1
3
  class AttributeStruct
2
- class Version < Gem::Version
4
+ class Version < ::Gem::Version
3
5
  end
4
6
 
5
- VERSION = Version.new('0.1.2')
7
+ VERSION = Version.new('0.1.4')
6
8
  end
@@ -0,0 +1,3 @@
1
+ Dir.glob(File.join(File.expand_path(File.dirname(__FILE__)), 'specs/*.rb')).each do |path|
2
+ require path
3
+ end
@@ -0,0 +1,169 @@
1
+ require 'minitest/autorun'
2
+
3
+ describe AttributeStruct do
4
+ describe 'Basic usage' do
5
+ describe 'instance based population' do
6
+ before do
7
+ @struct = AttributeStruct.new
8
+ @struct.method.based.access true
9
+ @struct.method('and', 'parameter', 'based').access true
10
+ @struct.block do
11
+ based.access true
12
+ end
13
+ @struct.block('and', 'parameter', 'based') do
14
+ access true
15
+ end
16
+ @struct.block_only do
17
+ access true
18
+ end
19
+ @struct._set('1') do
20
+ value true
21
+ end
22
+ end
23
+
24
+ it 'allows method based access' do
25
+ @struct.method.based.access.must_equal true
26
+ end
27
+
28
+ it 'allows method and parameter based access' do
29
+ @struct.method.and.parameter.based.access.must_equal true
30
+ end
31
+
32
+ it 'allows block based access' do
33
+ @struct.block.based.access.must_equal true
34
+ end
35
+
36
+ it 'allows block and parameter based access' do
37
+ @struct.block.and.parameters.based.access.must_equal true
38
+ end
39
+
40
+ it 'allows block only access' do
41
+ @struct.block_only.access.must_equal true
42
+ end
43
+
44
+ it 'allows hash style access' do
45
+ @struct['method'][:based].access.must_equal true
46
+ end
47
+
48
+ it 'allows _set for invalid method names' do
49
+ @struct['1'].value.must_equal true
50
+ end
51
+ end
52
+
53
+ describe 'block based creation' do
54
+ before do
55
+ @struct = AttributeStruct.new do
56
+ enable true
57
+ access.user true
58
+ access do
59
+ port 80
60
+ transport :udp
61
+ end
62
+ end
63
+ end
64
+
65
+ it 'creates struct with block content' do
66
+ @struct.enable.must_equal true
67
+ @struct.access.user.must_equal true
68
+ @struct.access.port.must_equal 80
69
+ @struct.access.transport.must_equal :udp
70
+ end
71
+
72
+ end
73
+
74
+ describe 'nil behavior' do
75
+ before do
76
+ @struct = AttributeStruct.new
77
+ end
78
+
79
+ it 'should return as nil' do
80
+ @struct.must_be_nil
81
+ end
82
+ end
83
+
84
+ describe 'array helper' do
85
+ before do
86
+ @struct = AttributeStruct.new do
87
+ my_array _array(
88
+ -> {
89
+ working true
90
+ },
91
+ :item
92
+ )
93
+ end
94
+ end
95
+
96
+ it 'should contain an array at my_array' do
97
+ @struct.my_array.must_be_kind_of Array
98
+ end
99
+
100
+ it 'should contain symbol in array' do
101
+ @struct.my_array.must_include :item
102
+ end
103
+
104
+ it 'should contain an AttrubuteStruct instance in array' do
105
+ assert @struct.my_array.detect{|i| i.is_a?(AttributeStruct)}
106
+ end
107
+
108
+ it 'should contain working attribute in array struct' do
109
+ @struct.my_array.detect{|i| i.is_a?(AttributeStruct)}.working.must_equal true
110
+ end
111
+ end
112
+
113
+ describe 'entry deletion' do
114
+ before do
115
+ @struct = AttributeStruct.new do
116
+ value1 true
117
+ value2 true
118
+ end
119
+ @struct._delete(:value2)
120
+ end
121
+
122
+ it 'should contain value1' do
123
+ @struct.value1.must_equal true
124
+ end
125
+
126
+ it 'should not contain value2 in keys' do
127
+ @struct._keys.wont_include 'value2'
128
+ end
129
+
130
+ it 'should report nil for value2' do
131
+ @struct.value2.must_be_nil
132
+ end
133
+ end
134
+
135
+ describe 'dumps' do
136
+ before do
137
+ @struct = AttributeStruct.new do
138
+ value1 true
139
+ value2 do
140
+ nested true
141
+ end
142
+ end
143
+ end
144
+
145
+ it 'should dump to a hash type value' do
146
+ @struct._dump.must_be_kind_of Hash
147
+ end
148
+
149
+ it 'should include all defined values' do
150
+ dump = @struct._dump
151
+ dump['value1'].must_equal true
152
+ dump['value2'].must_be_kind_of Hash
153
+ dump['value2']['nested'].must_equal true
154
+ end
155
+ end
156
+
157
+ describe 'loads' do
158
+ before do
159
+ @hash = {'value1' => true, 'value2' => {'nested' => true}}
160
+ @struct = AttributeStruct.new(@hash)
161
+ end
162
+
163
+ it 'should include all values defined in hash' do
164
+ @struct.value1.must_equal true
165
+ @struct.value2.nested.must_equal true
166
+ end
167
+ end
168
+ end
169
+ end
@@ -0,0 +1,43 @@
1
+ require 'minitest/autorun'
2
+
3
+ describe AttributeStruct do
4
+ describe 'Camel usage' do
5
+ before do
6
+ AttributeStruct.camel_keys = true
7
+ @struct = AttributeStruct.new do
8
+ value_one true
9
+ value_two do
10
+ nesting true
11
+ end
12
+ end
13
+ end
14
+ after do
15
+ AttributeStruct.camel_keys = false
16
+ end
17
+
18
+ it 'should camel case keys when dumped' do
19
+ @struct._dump.keys.must_include 'ValueOne'
20
+ @struct._dump.keys.must_include 'ValueTwo'
21
+ end
22
+
23
+ it 'should allow explicit disable on keys' do
24
+ @struct._set('not_camel'._no_hump, true)
25
+ @struct._dump.keys.must_include 'not_camel'
26
+ end
27
+
28
+ it 'should allow implicit disable on nested structs' do
29
+ @struct.disable_camel do |struct|
30
+ struct._camel_keys_set(:auto_disable)
31
+ not_camel true
32
+ not_camel_nest do
33
+ no_camel_here true
34
+ end
35
+ end
36
+ dump = @struct._dump
37
+ dump['DisableCamel'][:not_camel].must_equal true
38
+ dump['DisableCamel'][:not_camel_nest][:no_camel_here].must_equal true
39
+ dump['ValueOne'].must_equal true
40
+ dump['ValueTwo']['Nesting'].must_equal true
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,55 @@
1
+ require 'minitest/autorun'
2
+
3
+ describe AttributeStruct do
4
+ describe 'Merging usage' do
5
+ before do
6
+ @struct1 = AttributeStruct.new do
7
+ value1 true
8
+ value2 do
9
+ nesting true
10
+ end
11
+ end
12
+ @struct2 = AttributeStruct.new do
13
+ value2 do
14
+ nesting false
15
+ squashing true
16
+ end
17
+ end
18
+ end
19
+
20
+ describe 'new struct from merge' do
21
+ before do
22
+ @struct = @struct1._merge(@struct2)
23
+ end
24
+
25
+ it 'should have correct value for value1' do
26
+ @struct.value1.must_equal true
27
+ end
28
+
29
+ it 'should have correct value for squashing' do
30
+ @struct.value2.squashing.must_equal true
31
+ end
32
+
33
+ it 'should have correct value for nesting' do
34
+ @struct.value2.nesting.must_equal false
35
+ end
36
+ end
37
+
38
+ describe 'update struct from merge' do
39
+ before do
40
+ @struct = AttributeStruct.new do
41
+ test_value true
42
+ end
43
+ @struct._merge!(@struct1)
44
+ end
45
+
46
+ it 'should contain test_value' do
47
+ @struct.test_value.must_equal true
48
+ end
49
+
50
+ it 'should contain value1' do
51
+ @struct.value1.must_equal true
52
+ end
53
+ end
54
+ end
55
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: attribute_struct
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.1.4
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-07-08 00:00:00.000000000 Z
12
+ date: 2013-07-13 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: hashie
@@ -38,9 +38,12 @@ files:
38
38
  - lib/attribute_struct/version.rb
39
39
  - lib/attribute_struct/attribute_hash.rb
40
40
  - lib/attribute_struct/monkey_camels.rb
41
+ - test/spec.rb
42
+ - test/specs/camel.rb
43
+ - test/specs/merging.rb
44
+ - test/specs/basic.rb
41
45
  - Gemfile
42
46
  - README.md
43
- - attribute_struct-0.1.0.gem
44
47
  - attribute_struct.gemspec
45
48
  - CHANGELOG.md
46
49
  - Gemfile.lock
Binary file