rspec_normalized_hash 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,9 @@
1
+ pkg/*
2
+ *.gem
3
+ .bundle
4
+ Gemfile.lock
5
+ .rvmrc
6
+ *.DS_Store
7
+ doc
8
+ .yardoc
9
+ .tmp
@@ -0,0 +1,4 @@
1
+ lib/**/*.rb
2
+ spec/**/*.rb
3
+ -
4
+ NormalizedHash.md
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in *.gemspec
4
+ gemspec
@@ -0,0 +1,8 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ guard 'rspec', :version => 2, :cli => "--color --format nested" do
5
+ watch(%r{^spec/[^\.]+_spec\.rb})
6
+ watch(%r{^lib/\w(.+:w)\.rb}) { |m| "spec/lib/#{m[1]}_spec.rb" }
7
+ watch('spec/spec_helper.rb') { "spec" }
8
+ end
data/LICENSE ADDED
@@ -0,0 +1,17 @@
1
+ #
2
+ # Author:: Dmytro Kovalov (<dmytro.kovalov@gmail.com>)
3
+ # Copyright:: Copyright (c) 2012, Dmytro Kovalov
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
@@ -0,0 +1,134 @@
1
+ # @title Normalized Hash Data Structure Standard
2
+
3
+ Normalized Hash Data Structure Standard
4
+ ---------------------------------------
5
+
6
+ Specification below describes hash data structure, main goal of which is to make data produced by Uliska parser easy to use by software that does not know about internal data structure, i.e. data driven and schema-less. Data structures should be built in such a way as to make data self-documenting, easy adaptable and "software-friendly".
7
+
8
+ ### Hashes
9
+
10
+ * Uliska output data structure is multi-level Hash
11
+
12
+ * Hash key is one of the following classes: String, Symbol
13
+
14
+ * Hash keys should be descriptive and programmatically convertible to human readable form:
15
+
16
+ GOOD:
17
+
18
+ ````
19
+ :filesystem_size.humanize
20
+ ````
21
+
22
+ BAD:
23
+
24
+ ````
25
+ :fs_s.humanize
26
+ ````
27
+
28
+ * Hash values belong to one of the classes: Numeric, String, Hash, Array
29
+
30
+ * On the lowest level of the Hash only String, Numeric and simple Array are allowed as Hash values. Simple array here is an array containing only scalar values -- String or Numeric.
31
+
32
+
33
+ ### Arrays
34
+
35
+ * Array can consist of Numeric, String, Hash values
36
+
37
+ * All elements of each Array should be of the same class
38
+
39
+ * Use of Arrays of Hashes acceptable, although discouraged:
40
+
41
+ OK:
42
+
43
+ ````ruby
44
+ { :local_users =>
45
+ [ { :name => "nobody",
46
+ :password => "*",
47
+ :uid => -2,
48
+ :gid => -2,
49
+ :gecos => "Unprivileged User",
50
+ :homedir => "/var/empty",
51
+ :shell => "/usr/bin/false"
52
+ },
53
+ { :name => "root",
54
+ :password => "*",
55
+ :uid => 0,
56
+ :gid => 0,
57
+ :gecos => "System Administrator",
58
+ :homedir => "/var/root",
59
+ :shell => "/bin/sh"
60
+ }
61
+ ]
62
+ }
63
+ ````
64
+
65
+ BETTER:
66
+
67
+ ````ruby
68
+ { :local_users =>
69
+ {
70
+ :nobody =>
71
+ { :name => "nobody",
72
+ :password => "*",
73
+ :uid => -2,
74
+ :gid => -2,
75
+ :gecos => "Unprivileged User",
76
+ :homedir => "/var/empty",
77
+ :shell => "/usr/bin/false"
78
+ },
79
+ :root =>
80
+ { :name => "root",
81
+ :password => "*",
82
+ :uid => 0,
83
+ :gid => 0,
84
+ :gecos => "System Administrator",
85
+ :homedir => "/var/root",
86
+ :shell => "/bin/sh"
87
+ }
88
+ }
89
+ }
90
+ ````
91
+
92
+ * When Arrays of Hashes are used additional conditions must be satisfied:
93
+
94
+ - each Hash in an Array should have key `name` or
95
+
96
+ - if name of the Hash enclosing Array-of-Hashes is English noun in plural form, each Hash should have key which is singular form of the same noun:
97
+
98
+ **Example 1** -- Array of Hashes with `"name"` key:
99
+
100
+ ````ruby
101
+ { :local_users=>
102
+ [{:name=>"root",
103
+ :password=>"x",
104
+ :uid=>0,
105
+ :gid=>0,
106
+ :gecos=>"root",
107
+ :homedir=>"/root",
108
+ :shell=>"/bin/bash"},
109
+ {:name=>"daemon",
110
+ :password=>"x",
111
+ :uid=>1,
112
+ :gid=>1,
113
+ :gecos=>"daemon",
114
+ :homedir=>"/usr/sbin",
115
+ :shell=>"/bin/sh"}
116
+ ]
117
+ }
118
+ ````
119
+
120
+ **Example 2** -- Enclosing collection `"groups"` is an Array of Hashes. Each Hash has key `"group"`:
121
+
122
+ ````ruby
123
+ {:groups=>
124
+ [ {:group=>"root", :password=>"x", :gid=>0, :members=>[]},
125
+ {:group=>"daemon", :password=>"x", :gid=>1, :members=>[]},
126
+ {:group=>"bin", :password=>"x", :gid=>2, :members=>[]},
127
+ {:group=>"sys", :password=>"x", :gid=>3, :members=>[]}
128
+ ]
129
+ }
130
+ ````
131
+
132
+ Local Variables:
133
+ fill-column: 9999
134
+ End:
@@ -0,0 +1,44 @@
1
+
2
+ Normalized Hash tests for RSpec
3
+ ===============================
4
+
5
+ These are RSpec tests for Normalized Hash data structure standard. Standard itself and rationale why we (I) need it, is described in NormalizedHash.md file.
6
+
7
+
8
+ RSpec deep hash tests were insired by https://github.com/vitalish/rspec-deep-matchers specs.
9
+
10
+ Usage
11
+ -----
12
+
13
+
14
+ ````ruby
15
+
16
+ require 'rspec_normalized_hash'
17
+
18
+ describe "Good data structure" do
19
+
20
+ before(:each) { subject @data }
21
+
22
+ it { should have_keys_in_class [String, Symbol] }
23
+ it { should have_values_in_class [Fixnum, String, Numeric, Hash, Array] }
24
+ it { should have_array_values_in_class [String,Numeric,Hash] }
25
+ it { should have_array_values_of_the_same_class }
26
+
27
+ it { NOT IMPLEMENTED: enclosed arrays in Hash }
28
+ end
29
+
30
+ ````
31
+
32
+ License
33
+ =======
34
+
35
+ Apache 2
36
+
37
+ Author
38
+ ======
39
+
40
+ Dmytro Kovalov
41
+
42
+ dmytro.kovalov@gmail.com
43
+
44
+ Aug,Sept 2012
@@ -0,0 +1,6 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
3
+
4
+ task :doc do
5
+ sh "yardoc"
6
+ end
@@ -0,0 +1,124 @@
1
+ module NormalizedHash
2
+ module Matchers
3
+
4
+ # Test that array has only elements of expeted class(es).
5
+ #
6
+ # Array is a value in deep (multilevel) hash. Check done
7
+ # hierarchically for all arrays' values.
8
+ #
9
+ # @param [Array, Class] expected class(es) allowed
10
+ #
11
+ # = Usage
12
+ # it { @hash.should have_array_values_in_class [String,Numeric,Hash] }
13
+ # it { @hash.should have_array_values_in_class Numeric }
14
+ #
15
+ def have_array_values_in_class(expected)
16
+ ArrayValues.new(expected)
17
+ end
18
+
19
+ # Test that array elements are all in the same class.
20
+ #
21
+ # Array is a value in deep (multilevel) hash. Check done
22
+ # hierarchically for all arrays' values.
23
+ #
24
+ # @param none
25
+ #
26
+ # = Usage
27
+ # it { @hash.should have_array_values_of_the_same_class }
28
+ #
29
+ def have_array_values_of_the_same_class
30
+ SameArrayValues.new Object # dummy class, not used but needs to
31
+ # be here because of constructor
32
+ end
33
+
34
+ # RSpec::Matchers class for testing hierarchically arrays, that
35
+ # are elements of deep hash.
36
+ #
37
+ # SameArrayValues class tests that all values of the Array are on
38
+ # the same class.
39
+ class SameArrayValues < HashMatchers
40
+ def description
41
+ "have all values of the same class"
42
+ end
43
+
44
+ # Test that all values of the tested Array are of the same class
45
+ #
46
+ # @param [Array] target tested array
47
+ #
48
+ # @return true if pass, false if fail
49
+ def matches? target
50
+ result = true
51
+ case target
52
+ when Array
53
+ @actual = target.map(&:class).uniq
54
+ result = !(@actual.count > 1)
55
+ when Hash
56
+
57
+ target.each_value do |val|
58
+ if val.is_a? Array
59
+ result &&= SameArrayValues.new(Object).matches? val
60
+ @actual = val.map(&:class).uniq unless result
61
+ end
62
+ end
63
+
64
+ end
65
+ result
66
+ end
67
+
68
+ def failure_message_for_should
69
+ "expected #{@actual.inspect} to be one class"
70
+ end
71
+
72
+ def failure_message_for_should_not
73
+ "expected #{@actual.inspect} be at least 2 different classes"
74
+ end
75
+
76
+ end
77
+
78
+
79
+ # RSpec::Matchers class for testing hierarchically arrays, that
80
+ # are elements of deep hash.
81
+ #
82
+ # ArrayValues class ensures that all values of the tested
83
+ # Array belong to expected classes.
84
+ class ArrayValues < HashMatchers
85
+
86
+ def description
87
+ "have every value one of class: #{@expectation.join ','}"
88
+ end
89
+
90
+ # Test that all values of the tested Array belong to expecetd
91
+ # class(es).
92
+ #
93
+ # @param [Array] target array to test
94
+ #
95
+ # @return true if pass, false if fail
96
+ def matches?(target)
97
+ result = true
98
+ @target, @actual = target, target.map(&:class).uniq
99
+
100
+ case @target
101
+ when Array
102
+ result &&= (@actual - @expectation).empty?
103
+ when Hash
104
+ @target.each_value do |val|
105
+ result &&= ArrayValues.new(@expectation).matches?(val) if [Hash, Array].include? val.class
106
+ @target = val unless result
107
+ end
108
+ end
109
+
110
+ result
111
+ end
112
+
113
+
114
+ def failure_message_for_should
115
+ "expected #{@target.inspect} to have values of classes #{@expectation.inspect}"
116
+ end
117
+
118
+ def failure_message_for_should_not
119
+ "expected #{@target.inspect} differ from #{@expectation.inspect}"
120
+ end
121
+ end
122
+
123
+ end
124
+ end
@@ -0,0 +1,142 @@
1
+
2
+ require 'active_support/all'
3
+
4
+ module NormalizedHash
5
+ module Matchers
6
+
7
+ # Test that array has only elements of expeted class(es).
8
+ #
9
+ # @param TODO
10
+ #
11
+ # = Usage
12
+ # it { @TODO.should TODO }
13
+ #
14
+ def have_all_keys_be_word_name_or_singular_of (expected)
15
+ KeyNamesOfEnclosedHash.new(expected)
16
+ end
17
+
18
+ def all_arrays_have_hash_with_key_word_name_or_singular_of_hash_key
19
+ HashEnclosingArray.new
20
+ end
21
+
22
+ # Simple class to be inherited by other matcher classes that take
23
+ # String expectation.
24
+ class KeyNameMatchers
25
+ def initialize(expectation)
26
+ expectation = expectation.to_s.downcase
27
+ @expectation = expectation.singularize
28
+ raise ArgumentError, "#{expectation} must have singular form. Got #{@expectation}" if @expectation == expectation
29
+ end
30
+ end
31
+
32
+ # RSpec::Matchers class for testing hashes enclosed in an array.
33
+ #
34
+ # Array of Hashes: each Hash should have key :name or :<collection.singularize>
35
+ #
36
+ # Hash here (subject) is an instance of Hash, which is an element
37
+ # of an Array, and Array itself is a value of a Hash:
38
+ #
39
+ # { :foos =>
40
+ # [ <--- subject
41
+ # { :foo (or :name) => ...,
42
+ # :bar => ...,
43
+ # },
44
+ # { :foo (or :name) => ...,
45
+ # :bar => ...,
46
+ # },
47
+ # ]
48
+ # }
49
+ class KeyNamesOfEnclosedHash < KeyNameMatchers
50
+ def description
51
+ "have key :name or '#{@expectation.singularize}' in every enclosed Hash "
52
+ end
53
+
54
+ # Test that all values of the tested Array are of the same class
55
+ #
56
+ # @param [Array] target tested array
57
+ #
58
+ # @return true if pass, false if fail
59
+ def matches? target
60
+ @target = target
61
+ result = true
62
+
63
+ target.each do |hash|
64
+ next unless hash.is_a? Hash
65
+ keys = hash.keys.map(&:to_s).map(&:downcase)
66
+ result &&= false unless (keys.include? @expectation || keys.include?("name"))
67
+ end
68
+
69
+ result
70
+ end
71
+
72
+ def failure_message_for_should
73
+ "expected each of #{@target.map(&:keys)} include '#{@expectation.singularize}'"
74
+ end
75
+
76
+ def failure_message_for_should_not
77
+ "expected #{@target.map(&:keys)} not include '#{@expectation.singularize}'"
78
+ end
79
+ end # class KeyNamesOfEnclosedHash < HashMatchers
80
+
81
+ # Hash (subject) ancloses Array wit ha bunch of Hases inside. Each
82
+ # aHas is then tested by KeyNamesOfEnclosedHash
83
+ #
84
+ #
85
+ # { :foos => <--- subject
86
+ # [
87
+ # { :foo (or :name) => ...,
88
+ # :bar => ...,
89
+ # },
90
+ # { :foo (or :name) => ...,
91
+ # :bar => ...,
92
+ # },
93
+ # ]
94
+ # }
95
+ class HashEnclosingArray
96
+ def description
97
+ @description
98
+ end
99
+
100
+ def failure_message_for_should
101
+ [@target.inspect, description, @failure].join ' '
102
+ end
103
+
104
+ def failure_message_for_should_not
105
+ [@target.inspect, 'not', description, @failure].join ' '
106
+ end
107
+
108
+
109
+ # Run matcher only if target is Hash with Array embedded at the
110
+ # first depth level. Not recursive, recursion should be
111
+ # implemented on level higher.
112
+ def matches? target
113
+
114
+ result = true
115
+ @target = target
116
+
117
+ if (target.is_a?(Hash) && target.values.map(&:class).include?(Array))
118
+
119
+ @description = "enclose Arrays of Hashes with key #{@target.keys}"
120
+ @failure = ":\n"
121
+
122
+ target.each do |key,array|
123
+ if array.is_a? Array
124
+ @checked = array
125
+ res = KeyNamesOfEnclosedHash.new(key).matches?(array)
126
+ result &&= res
127
+ @failure << { key => array}.inspect unless res
128
+ end
129
+ end
130
+ else
131
+ @description = "*** spec did not run *** "
132
+ result = false
133
+ end
134
+
135
+ result
136
+ end
137
+
138
+ end # ArrayEnclosingHashes < KeyNameMatchers
139
+
140
+ end
141
+ end
142
+
@@ -0,0 +1,68 @@
1
+ module NormalizedHash
2
+ module Matchers
3
+
4
+ # Test that hash has only keys of expected class(es).
5
+ #
6
+ # Check done hierarchically for all Hashes.
7
+ #
8
+ # @param [Array, Class] expected class(es) allowed
9
+ #
10
+ # == Usage
11
+ # it { @hash.should have_keys_in_class [String,Numeric,Hash,Array] }
12
+ # it { @hash.should have_keys_in_class Numeric }
13
+ #
14
+ def have_keys_in_class(expected)
15
+ HashKeys.new(expected)
16
+ end
17
+
18
+ # RSpec::Matchers class for testing hierarchically hashes, that
19
+ # are elements of deep hash.
20
+ #
21
+ # HashKeys class tests that all keys of the Hash belong to list of
22
+ # Classes.
23
+ #
24
+ # == Usage
25
+ # it { @hash.should have_keys_in_class [String, Symbol] }
26
+ # it { @hash.should have_keys_in_class String }
27
+ class HashKeys < HashMatchers
28
+
29
+ def description
30
+ "have (recursively) every key one of class: #{[@expectation].flatten.join ','}"
31
+ end
32
+
33
+ # Test that all keys of the tested Hash belong to provided list
34
+ # of classes
35
+ #
36
+ # @param [Hash] target hash under test
37
+ #
38
+ # @return true if pass, false if fail
39
+ def matches?(target)
40
+ result = true
41
+
42
+ if target.is_a? Hash
43
+
44
+ @target, @actual = target, target.keys.map(&:class).uniq
45
+
46
+ result &&= (@actual - @expectation).empty?
47
+
48
+ @target.each_value do |val|
49
+ result &&= HashKeys.new(@expectation).matches?(val) if val.is_a? Hash
50
+ @target = val unless result
51
+ end
52
+ end
53
+
54
+ result
55
+ end
56
+
57
+
58
+ def failure_message_for_should
59
+ "expected #{@target.keys.inspect} #{@actual} to be one of #{@expectation.inspect}"
60
+ end
61
+
62
+ def failure_message_for_should_not
63
+ "expected #{@target.keys.inspect} differ from #{@expectation.inspect}"
64
+ end
65
+ end
66
+
67
+ end
68
+ end
@@ -0,0 +1,69 @@
1
+ module NormalizedHash
2
+ module Matchers
3
+
4
+ # Test that hash has only values of expected class(es).
5
+ #
6
+ # Check done hierarchically for all Hashes.
7
+ #
8
+ # @param [Array, Class] expected class(es) allowed
9
+ #
10
+ # == Usage
11
+ #
12
+ # it { @hash.should have_values_in_class [String,Numeric,Hash,Array] }
13
+ # it { @hash.should have_values_in_class Numeric }
14
+ #
15
+ def have_values_in_class(expected)
16
+ HashValues.new(expected)
17
+ end
18
+
19
+ # RSpec::Matchers class for testing hierarchically hashes, that
20
+ # are elements of deep hash.
21
+ #
22
+ # HashValues class tests that all values of the Hash belong to
23
+ # list of Classes.
24
+ #
25
+ # == Usage
26
+ # it { @hash.should have_values_in_class [String, Symbol] }
27
+ # it { @hash.should have_values_in_class String }
28
+ #
29
+ class HashValues < HashMatchers
30
+
31
+ def description
32
+ "have (recursively) every value one of class: #{[@expectation].flatten.join ','}"
33
+ end
34
+
35
+ # Test that all values of the tested Hash belong to provided
36
+ # list of classes
37
+ #
38
+ # @param [Hash] target hash under test
39
+ #
40
+ # @return true if pass, false if fail
41
+ def matches?(target)
42
+ result = true
43
+
44
+ if target.is_a? Hash
45
+ @target, @actual = target, target.values.map(&:class).uniq
46
+
47
+ result &&= (@actual - @expectation).empty?
48
+
49
+ @target.each_value do |val|
50
+ result &&= HashValues.new(@expectation).matches?(val) if val.is_a?(Hash)
51
+ @target = val unless result
52
+ end
53
+ end
54
+
55
+ result
56
+ end
57
+
58
+
59
+ def failure_message_for_should
60
+ "expected #{@target.values.inspect} to have values of classes #{@expectation.inspect}"
61
+ end
62
+
63
+ def failure_message_for_should_not
64
+ "expected #{@target.values.inspect} differ from #{@expectation.inspect}"
65
+ end
66
+ end
67
+
68
+ end
69
+ end
@@ -0,0 +1,26 @@
1
+ module NormalizedHash
2
+ module Matchers
3
+
4
+ # All numeric classes we want to consider
5
+ NUMBERS = [Fixnum, Bignum, Numeric, Float]
6
+
7
+ class HashMatchers
8
+
9
+ def initialize(expectation)
10
+ # Convert to array for simpler checks
11
+ @expectation = [expectation].flatten
12
+
13
+ # All expectionations should be Class, no other classes
14
+ unless @expectation.map(&:class).uniq == [Class]
15
+ raise ArgumentError,
16
+ "#{@expectation.inspect}: Expectation should be Class or Array[of Classes], got #{@expectation.class}"
17
+ end
18
+
19
+ # Expand expectation with all numeric classes if at least one
20
+ # numeric present.
21
+ (@expectation += NUMBERS).uniq! unless (@expectation & NUMBERS).empty?
22
+
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,10 @@
1
+ $: << File.dirname(__FILE__)
2
+ require 'rspec'
3
+ require "normalized_hash/matchers"
4
+ require "normalized_hash/hash_keys"
5
+ require "normalized_hash/hash_values"
6
+ require "normalized_hash/array_values"
7
+ require "normalized_hash/hash_enclosed_in_array"
8
+ module RSpec::Matchers
9
+ include NormalizedHash::Matchers
10
+ end
@@ -0,0 +1,5 @@
1
+ module NormalizedHash
2
+ module Matchers
3
+ VERSION="0.0.1"
4
+ end
5
+ end
@@ -0,0 +1,32 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "rspec_normalized_hash"
7
+ s.version = NormalizedHash::Matchers::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["dmytro"]
10
+ s.email = ["dmytro.kovalov@gmail.com"]
11
+ s.homepage = "http://github.com/dmytro/rspec_normalized_hash"
12
+ s.summary = %q{Recursive Hash structure matcher for rspec.}
13
+ s.description = " Specification of Normalized Hash data structure main goal is to make data produced by parsers easy to use by software that does not know about internal data structure, i.e. data driven and schema-less. Data structures should be built in such a way as to make data self-documenting, easy adaptable and 'software-friendly'.
14
+ Gem contains RSpec tests for testing Hash data structure for compliance with the requirements.
15
+ See README.md and NormalizedHash.md files in gem's root directory."
16
+
17
+ s.rubyforge_project = "normalized-hash"
18
+
19
+ s.files = `git ls-files`.split("\n")
20
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
21
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
22
+ s.require_paths = ["lib"]
23
+
24
+ s.add_dependency 'rspec', '>= 2.0.0'
25
+ s.add_dependency 'active_support'
26
+ s.add_dependency 'i18n'
27
+
28
+
29
+ s.add_development_dependency('rb-fsevent')
30
+ s.add_development_dependency('guard-rspec')
31
+ s.add_development_dependency('growl')
32
+ end
@@ -0,0 +1,46 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Normalized Hash - Array' do
4
+
5
+ before(:each) { @hash = GOOD_HASH }
6
+
7
+ context "simple" do
8
+ context "Numeric array" do
9
+ subject { @hash[:n_array].dup } # Call dup here since I'm modifying data
10
+ it { should have_array_values_in_class Numeric }
11
+ it { should have_array_values_of_the_same_class }
12
+ it "with different values should not have same class" do
13
+ subject << 'a'
14
+ should_not have_array_values_of_the_same_class
15
+ end
16
+ end
17
+
18
+ context "String array" do
19
+ subject { @hash[:s_array] }
20
+ it { should have_array_values_in_class String }
21
+ end
22
+
23
+ context "mixed array" do
24
+ subject { @hash[:mix_array] }
25
+ it { should have_array_values_in_class [String,Numeric,Hash] }
26
+ it { should_not have_array_values_in_class IO }
27
+ end
28
+
29
+ context "array of Hash'es" do
30
+ subject { @hash[:array_hash] }
31
+ it { should have_array_values_in_class [String,Numeric,Hash] }
32
+ it { should_not have_array_values_in_class IO }
33
+ end
34
+ end
35
+
36
+ context "multi level" do
37
+ subject { @hash }
38
+ it { should have_array_values_in_class [String,Numeric,Hash] }
39
+ it { should_not have_array_values_in_class IO }
40
+ it { should_not have_array_values_of_the_same_class }
41
+ it "when removed mixed array should all be in the same class" do
42
+ subject.delete :mix_array
43
+ should have_array_values_of_the_same_class
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,47 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Normalized Hash - enclosing array' do
4
+
5
+ before(:each) { @hash = GOOD_HASH }
6
+
7
+ context "Hash enclosed in Array " do
8
+ context "Hash-> Array-> [Hash]" do
9
+ context :simple do
10
+ subject { @hash[:names] }
11
+ it { should have_all_keys_be_word_name_or_singular_of :names }
12
+ it { @hash[:people].should have_all_keys_be_word_name_or_singular_of :people}
13
+
14
+ it { should_not have_all_keys_be_word_name_or_singular_of :last_names }
15
+ it { @hash[:people].should have_all_keys_be_word_name_or_singular_of :people}
16
+ end
17
+
18
+ context :simple_changed do
19
+ subject { @hash[:names] << { :not_really_name => 'some string', :name => 'name' } }
20
+ it { should have_all_keys_be_word_name_or_singular_of :names }
21
+ it { should_not have_all_keys_be_word_name_or_singular_of :last_names }
22
+ end
23
+ end # context "keys of Hash enclosed in an Array" do
24
+
25
+ context "Hash-> [Array]-> Hash " do
26
+ context "one level" do
27
+ subject { {:people => @hash[:people]} }
28
+ it { should all_arrays_have_hash_with_key_word_name_or_singular_of_hash_key }
29
+ end
30
+
31
+ context '2 levels' do
32
+ subject {
33
+ {
34
+ :deep =>
35
+ { :two_levels =>
36
+ [
37
+ { :name => :one},
38
+ { :name => :two},
39
+ ]
40
+ }
41
+ }
42
+ }
43
+ it { should_not all_arrays_have_hash_with_key_word_name_or_singular_of_hash_key }
44
+ end
45
+ end # context "Hash-> [Array]-> Hash" do
46
+ end # context "enclosed in Array Hash" do
47
+ end
@@ -0,0 +1,78 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Normalized Hash - keys and values' do
4
+
5
+ before(:each) { @hash = GOOD_HASH }
6
+ # ------------------------------------------------------------
7
+ context 'Hash keys and values' do
8
+ context "simple hash" do
9
+
10
+ context "single Symbol key" do
11
+ subject { @hash[:symbol] }
12
+
13
+ it { should have_keys_in_class Symbol }
14
+ it { should_not have_keys_in_class String }
15
+
16
+ it { should have_values_in_class [String, Numeric, Array, Hash] }
17
+ it { should_not have_values_in_class IO }
18
+
19
+ it { should have_array_values_in_class [Numeric, String, Hash] }
20
+
21
+ end
22
+
23
+ context "single String key" do
24
+ subject { @hash[:string] }
25
+
26
+ it { should have_keys_in_class String }
27
+ it { should_not have_keys_in_class Symbol }
28
+
29
+ it { should have_values_in_class [String, Numeric, Array, Hash] }
30
+ it { should_not have_values_in_class IO }
31
+ end
32
+
33
+ context "Symbol and String keys" do
34
+ subject { @hash[:sym_str] }
35
+
36
+ it { should have_keys_in_class [Symbol, String] }
37
+ it { should have_keys_in_class [Symbol, String, File] }
38
+ it { should_not have_keys_in_class [Symbol, File] }
39
+ it { should_not have_keys_in_class Symbol }
40
+ it { should_not have_keys_in_class String }
41
+ end
42
+
43
+ context "nested Hash" do
44
+
45
+ context "2 levels" do
46
+ subject { @hash[:l2] }
47
+ it { should have_keys_in_class [Symbol, String] }
48
+ it { subject.merge({[1,2] => 1 }).should_not have_keys_in_class [Symbol, String] }
49
+
50
+ it { should have_values_in_class [String, Numeric, Array, Hash] }
51
+ it { should_not have_values_in_class IO }
52
+
53
+ end
54
+
55
+ context "3 levels" do
56
+ subject { @hash }
57
+ it { should have_keys_in_class [Symbol, String] }
58
+
59
+
60
+ it {
61
+ bad = { :a => { :b => { :bbb => [1,2,3], 'ccc' => "string", 42 => 42 }}}
62
+ bad.should_not have_keys_in_class [Symbol, String]
63
+ }
64
+
65
+ it { should have_values_in_class [String, Numeric, Array, Hash] }
66
+
67
+ it {
68
+ subject.merge(
69
+ { :file => File.new('.tmp','w') }
70
+ ).should_not have_values_in_class [String, Numeric, Array, Hash]
71
+ }
72
+ end # 3 levels
73
+ end # nested Hash
74
+ end # Hash
75
+
76
+
77
+ end # context 'Hash keys and values' do
78
+ end
@@ -0,0 +1,3 @@
1
+ require 'rspec'
2
+ require 'rspec_normalized_hash'
3
+ require 'test_data'
@@ -0,0 +1,63 @@
1
+
2
+ #
3
+ # TEST DATA: Good normalized hash for use in tests
4
+ # ================================================
5
+ GOOD_HASH = {
6
+ :symbol => { :str => 'str', :num => 42, :arr => [1,2,3] }, # single symbol key
7
+ :string => { 'str' => 'str', 'num' => 42, 'arr' => [1,2,3] }, # single string key
8
+ :sym_str => {
9
+ :a => 1, :aa => 'a',
10
+ 'b' => 2, 'bb' => 'bb'
11
+ },
12
+ :l2 => { # level 2 hash
13
+ :aa => 1,
14
+ :bb => { :bbb => [1,2,3], 'ccc' => "string", :ddd => 42 }, # symbol and string keys
15
+ 'cc' => 3
16
+ },
17
+ :b => {
18
+ :ba => 1,
19
+ :bb => 2,
20
+ 'bc' => { :caa => 1, :cbb => 'asdfas', 'dd' => 'file'},
21
+ 'abc' => 'a'
22
+ },
23
+ :n_array => [1,2,3,4],
24
+ :s_array => ["a","b","c"],
25
+ :mix_array => [1, "a", {} ],
26
+ :array_hash =>
27
+ [
28
+ { :name =>'a' },
29
+ { :name =>'b' },
30
+ { :name =>'c' },
31
+ ],
32
+
33
+ :names =>
34
+ [
35
+ { :name =>'a' },
36
+ { :name =>'b' },
37
+ { :name =>'c' },
38
+ ],
39
+
40
+ :people =>
41
+ [
42
+ { :person =>'a' },
43
+ { :person =>'b' },
44
+ { :person =>'c' },
45
+ ]
46
+ }
47
+
48
+ # ============================================================
49
+ @enclosed_hash = {
50
+ :simple => [
51
+ { },
52
+ { },
53
+ { },
54
+ ],
55
+ :nested => {
56
+ :level2 => [
57
+ { },
58
+ { },
59
+ { },
60
+ ]
61
+ }
62
+ }
63
+ # ============================================================
metadata ADDED
@@ -0,0 +1,176 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rspec_normalized_hash
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - dmytro
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-09-06 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rspec
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: 2.0.0
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: 2.0.0
30
+ - !ruby/object:Gem::Dependency
31
+ name: active_support
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: i18n
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: rb-fsevent
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ - !ruby/object:Gem::Dependency
79
+ name: guard-rspec
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ - !ruby/object:Gem::Dependency
95
+ name: growl
96
+ requirement: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ! '>='
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ type: :development
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ description: ! ' Specification of Normalized Hash data structure main goal is to make
111
+ data produced by parsers easy to use by software that does not know about internal
112
+ data structure, i.e. data driven and schema-less. Data structures should be built
113
+ in such a way as to make data self-documenting, easy adaptable and ''software-friendly''.
114
+
115
+ Gem contains RSpec tests for testing Hash data structure for compliance with the
116
+ requirements.
117
+
118
+ See README.md and NormalizedHash.md files in gem''s root directory.'
119
+ email:
120
+ - dmytro.kovalov@gmail.com
121
+ executables: []
122
+ extensions: []
123
+ extra_rdoc_files: []
124
+ files:
125
+ - .gitignore
126
+ - .yardopts
127
+ - Gemfile
128
+ - Guardfile
129
+ - LICENSE
130
+ - NormalizedHash.md
131
+ - README.md
132
+ - Rakefile
133
+ - lib/normalized_hash/array_values.rb
134
+ - lib/normalized_hash/hash_enclosed_in_array.rb
135
+ - lib/normalized_hash/hash_keys.rb
136
+ - lib/normalized_hash/hash_values.rb
137
+ - lib/normalized_hash/matchers.rb
138
+ - lib/rspec_normalized_hash.rb
139
+ - lib/version.rb
140
+ - rspec_normalized_hash.gemspec
141
+ - spec/array_spec.rb
142
+ - spec/hash_enclosing_array_spec.rb
143
+ - spec/hash_keys_and_values_spec.rb
144
+ - spec/spec_helper.rb
145
+ - spec/test_data.rb
146
+ homepage: http://github.com/dmytro/rspec_normalized_hash
147
+ licenses: []
148
+ post_install_message:
149
+ rdoc_options: []
150
+ require_paths:
151
+ - lib
152
+ required_ruby_version: !ruby/object:Gem::Requirement
153
+ none: false
154
+ requirements:
155
+ - - ! '>='
156
+ - !ruby/object:Gem::Version
157
+ version: '0'
158
+ required_rubygems_version: !ruby/object:Gem::Requirement
159
+ none: false
160
+ requirements:
161
+ - - ! '>='
162
+ - !ruby/object:Gem::Version
163
+ version: '0'
164
+ requirements: []
165
+ rubyforge_project: normalized-hash
166
+ rubygems_version: 1.8.24
167
+ signing_key:
168
+ specification_version: 3
169
+ summary: Recursive Hash structure matcher for rspec.
170
+ test_files:
171
+ - spec/array_spec.rb
172
+ - spec/hash_enclosing_array_spec.rb
173
+ - spec/hash_keys_and_values_spec.rb
174
+ - spec/spec_helper.rb
175
+ - spec/test_data.rb
176
+ has_rdoc: