hashmodel 0.4.0.beta1 → 0.4.0.beta2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. data/Gemfile.lock +1 -1
  2. data/LICENSE.txt +1 -1
  3. data/README.markdown +65 -27
  4. data/hashmodel.gemspec +1 -1
  5. data/lib/hash_model/hash_model.rb +13 -105
  6. data/lib/hash_model/hash_model_create_object.rb +62 -0
  7. data/lib/hash_model/hash_model_delete.rb +13 -0
  8. data/lib/hash_model/hash_model_filter.rb +37 -0
  9. data/lib/hash_model/hash_model_flatten.rb +47 -0
  10. data/lib/hash_model/hash_model_group.rb +18 -0
  11. data/lib/hash_model/hash_model_update.rb +118 -0
  12. data/lib/hash_model/hash_model_where.rb +27 -0
  13. data/lib/hash_model/version.rb +1 -1
  14. data/lib/hashmodel.rb +1 -0
  15. data/lib/monkey_patch/deep_clone.rb +27 -0
  16. data/spec/hash_model/_current_spec.rb +6 -0
  17. data/spec/hash_model/hash_model_adding_records_spec.rb +338 -0
  18. data/spec/hash_model/hash_model_array_methods_spec.rb +132 -0
  19. data/spec/hash_model/hash_model_comparisons_spec.rb +55 -0
  20. data/spec/hash_model/hash_model_delete_spec.rb +51 -0
  21. data/spec/hash_model/hash_model_flattening_spec.rb +92 -0
  22. data/spec/hash_model/hash_model_group_spec.rb +67 -0
  23. data/spec/hash_model/hash_model_searching_spec.rb +245 -0
  24. data/spec/hash_model/hash_model_spec.rb +1 -1
  25. data/spec/hash_model/hash_model_unflattening_spec.rb +34 -0
  26. data/spec/hash_model/hash_model_update_spec.rb +147 -0
  27. data/spec/hash_model/hash_model_where_spec.rb +287 -0
  28. data/spec/support/configuration.rb +50 -0
  29. data/spec/support/debug_print.rb +13 -0
  30. data/spec/support/proc_tester.rb +17 -0
  31. metadata +49 -27
  32. data/_brainstorm/StrangeMarshal.txt +0 -0
  33. data/_brainstorm/_readme +0 -1
  34. data/_brainstorm/block_wheres.rb +0 -80
  35. data/_brainstorm/clone.rb +0 -19
  36. data/_brainstorm/hash_model_examples.rb +0 -57
  37. data/_brainstorm/hash_test.rb +0 -169
  38. data/_brainstorm/inspect.rb +0 -26
  39. data/_brainstorm/instance_vars.rb +0 -24
  40. data/_brainstorm/proc_tests.rb +0 -14
  41. data/_brainstorm/ref_val.rb +0 -16
  42. data/_brainstorm/regex_captures.rb +0 -18
  43. data/_brainstorm/spliting.rb +0 -46
  44. data/_brainstorm/test.rb +0 -27
  45. data/_brainstorm/unflat.rb +0 -16
@@ -0,0 +1,13 @@
1
+ class HashModel
2
+
3
+ # Returns a copy of the HashModel with raw data deleted based on the search criteria
4
+ def delete(index_search=:DontSearchForThis_195151c48a254db2949ed102c81ec579, &block_search)
5
+ self.clone.delete!(index_search, &block_search)
6
+ end
7
+
8
+ # Deletes the raw data records based on the search criteria
9
+ def delete!(index_search=:DontSearchForThis_195151c48a254db2949ed102c81ec579, &block_search)
10
+ parents(index_search, &block_search).each{|parent| @raw_data.delete(parent)}
11
+ end
12
+
13
+ end
@@ -0,0 +1,37 @@
1
+ class HashModel
2
+
3
+
4
+ # Filter the flattened records based on the value of the
5
+ # default index or by using a boolean logic block:
6
+ # filter("x")
7
+ # filter{:x==4 and :y!="potato"}
8
+ def filter(index_search=:DontSearchForThis_195151c48a254db2949ed102c81ec579, &block_search)
9
+ # Parameter checks
10
+ raise SyntaxError, "You may only provide a parameter or a block but not both" unless index_search==:DontSearchForThis_195151c48a254db2949ed102c81ec579 or block_search.nil?
11
+ return self if @raw_data.empty?
12
+
13
+ # Allow clearing the filter and returning the entire recordset if nothing is given
14
+ if index_search == :DontSearchForThis_195151c48a254db2949ed102c81ec579 && block_search.nil?
15
+ @filter_proc = nil
16
+ @filter_binding = nil
17
+ return flatten
18
+ end
19
+
20
+ # If given a parameter make our own search based on the flatten index
21
+ unless index_search == :DontSearchForThis_195151c48a254db2949ed102c81ec579
22
+ string_search = "(:#{@flatten_index} == #{index_search.inspect})".to_s # << the to_s makes sure it's evaluated now
23
+ @filter_binding = nil
24
+ else
25
+ # Convert the proc to a string so it can have :'s turned into @'s
26
+ # We only want the internals so we can write the proc the way we want
27
+ string_search = block_search.to_source(:strip_enclosure => true)
28
+ @filter_binding = block_search.binding
29
+ end
30
+
31
+ # Set and process the filter
32
+ @filter_proc = string_search
33
+ flatten
34
+ end
35
+
36
+
37
+ end
@@ -0,0 +1,47 @@
1
+ class HashModel
2
+
3
+ # Processes the raw_data and flattens the records based on the filter (set by filter or a where search)
4
+ def flatten
5
+ # Don't flatten the data if we don't need to
6
+ return self unless dirty?
7
+
8
+ id = -1
9
+ group_id = -1
10
+ @modified_data.clear
11
+ # set the flatten index if this is the first time the function is called
12
+ @flatten_index = @raw_data[0].keys[0] if @raw_data != [] && @flatten_index.nil?
13
+ flatten_index = @flatten_index.to_s
14
+ # Change the filter so it looks for variables instead of symbols
15
+ unless @filter_proc.nil?
16
+ proc_filter = @filter_proc.clone
17
+ proc_filter.scan(/(:\S+) ==/).each {|match| proc_filter.sub!(match[0], match[0].sub(":","@"))}
18
+ proc_filter.sub!(":_group_id", "@_group_id")
19
+ end
20
+
21
+ # Flatten and filter the raw data
22
+ @raw_data.each do |record|
23
+ new_records, duplicate_data = flatten_hash(record, flatten_index)
24
+ # catch raw data records that don't have the flatten index
25
+ new_records << {@flatten_index.to_sym=>nil} if new_records.empty?
26
+ group_id += 1
27
+ new_records.collect! do |new_record|
28
+ # Double bangs aren't needed but are they more efficient?
29
+ new_record.merge!( duplicate_data.merge!( { :_id=>(id+=1), :_group_id=>group_id } ) )
30
+ end
31
+
32
+ # Add the records to modified data if they pass the filter
33
+ new_records.each do |new_record|
34
+ unless @filter_proc.nil?
35
+ variables_string, remove_string = create_value_string(new_record)
36
+ @modified_data << new_record if eval("#{variables_string}return_value=#{proc_filter};#{remove_string}return_value", @filter_binding)
37
+ else
38
+ @modified_data << new_record
39
+ end
40
+ end
41
+
42
+ end # raw_data.each
43
+ set_dirty_hash
44
+ self
45
+ end # flatten
46
+
47
+ end
@@ -0,0 +1,18 @@
1
+ class HashModel
2
+
3
+ # Return the other records created from the same raw data record as the one(s) searched for
4
+ def group(index_search=:DontSearchForThis_195151c48a254db2949ed102c81ec579, &block_search)
5
+ group_hm = self.clone
6
+ # Filter the recordset if applicable
7
+ if !index_search.nil? || !block_search.nil?
8
+ group_hm.filter(index_search, &block_search)
9
+ end
10
+ # Get all the unique group id's
11
+ group_ids = group_hm.collect {|hash| hash[:_group_id]}.uniq
12
+ # reset the filter
13
+ group_hm.filter
14
+ group_hm.filter_proc = "#{group_ids.to_s}.include? :_group_id"
15
+ group_hm.flatten
16
+ end
17
+
18
+ end
@@ -0,0 +1,118 @@
1
+ class HashModel
2
+
3
+ # Returns a copy of the HashModel that matches the search criteria
4
+ # updated using the update_hash. This affects the raw_data.
5
+ def update(index_search=:DontSearchForThis_195151c48a254db2949ed102c81ec579, update_hash, &block_search)
6
+ self.clone.update!(index_search, update_hash, &block_search)
7
+ end
8
+
9
+ # Destructively updates the raw data for the records that match the
10
+ # search criteria. Since this affects the raw_data so if more than one record
11
+ # is based on the raw_data that's changed that record will be changed too.
12
+ # Returns the records that were updated.
13
+ def update!(index_search=:DontSearchForThis_195151c48a254db2949ed102c81ec579, update_hash, &block_search)
14
+
15
+ # only filter if they sent something to be filter otherwise leave the filter alone
16
+ unless index_search == :DontSearchForThis_195151c48a254db2949ed102c81ec579 && block_search.nil?
17
+ old_filter = @filter_proc
18
+ old_binding = @filter_binding
19
+ filter(index_search, &block_search)
20
+ end
21
+
22
+ changed = recursive_update_root(update_hash)
23
+
24
+ if old_filter
25
+ @filter_proc = old_filter
26
+ @filter_binding = old_binding
27
+ flatten
28
+ end
29
+
30
+ changed
31
+ end
32
+
33
+ # Updates existing hashes with a given value and also
34
+ # adds the full root to the key if it doesn't already exist.
35
+ # Returns a new instance of the original HashModel
36
+ def update_and_add(index_search=:DontSearchForThis_195151c48a254db2949ed102c81ec579, update_hash, &block_search)
37
+ self.clone.update_and_add!(index_search, update_hash, &block_search)
38
+ end
39
+
40
+ # Updates existing hashes with a given value and also
41
+ # adds the full root to the key if it doesn't already exist.
42
+ # Destructively updated the existing HashModel
43
+ def update_and_add!(index_search=:DontSearchForThis_195151c48a254db2949ed102c81ec579, update_hash, &block_search)
44
+
45
+ # only filter if they sent something to be filter otherwise leave the filter alone
46
+ unless index_search == :DontSearchForThis_195151c48a254db2949ed102c81ec579 && block_search.nil?
47
+ old_filter = @filter_proc
48
+ old_binding = @filter_binding
49
+ filter(index_search, &block_search)
50
+ end
51
+
52
+ changed = recursive_update_root(update_hash, true)
53
+
54
+ if old_filter
55
+ @filter_proc = old_filter
56
+ @filter_binding = old_binding
57
+ flatten
58
+ end
59
+
60
+ changed
61
+ end
62
+
63
+ protected
64
+
65
+ # Loops through the filtered recordset and sends each record off to be recursivly changed
66
+ def recursive_update_root(update_hash, add=false)
67
+ changed_record_ids = []
68
+ each do |record|
69
+ save_change = []
70
+ update_hash.each do |key,value|
71
+ unflat = unflatten(key=>value)
72
+ recursive_update_worker(@raw_data[record[:_group_id]], unflat, key, nil, add, record[:_id], save_change)
73
+ end
74
+ changed_record_ids << save_change.uniq[0] unless save_change.empty?
75
+ end
76
+ # clear the filter so we can get changed records if the filter is
77
+ # the field that changed (also causes records to update)
78
+ # This looks ripe for a refactor
79
+ if @filter_proc
80
+ old_filter = @filter_proc
81
+ old_binding = @filter_binding
82
+ @filter_proc = nil
83
+ @filter_binding = nil
84
+ end
85
+ # now that we've changed the raw data update the values in the flattened records
86
+ filter
87
+ # Grab the changed records
88
+ changed = @modified_data.select{|record| changed_record_ids.include?(record[:_id])}
89
+ if old_filter
90
+ @filter_proc = old_filter
91
+ @filter_binding = old_binding
92
+ flatten
93
+ end
94
+ changed
95
+ end
96
+
97
+ # Loops through the hash that represents the changes to make and recurses into as needed
98
+ def recursive_update_worker(target_hash, source_hash, terminal_key, parent_key, add, record_id, save_change)
99
+ source_hash.each do |source_hash_key, source_hash_value|
100
+ current_key = "#{parent_key}#{"__" if parent_key}#{source_hash_key}".to_sym
101
+ if current_key == terminal_key
102
+ return unless target_hash[source_hash_key] or add
103
+ target_hash[source_hash_key] = source_hash_value
104
+ save_change << record_id
105
+ else
106
+ if target_hash.is_a? Hash
107
+ recursive_update_worker(target_hash[source_hash_key], source_hash[source_hash_key], terminal_key, current_key, add, record_id, save_change)
108
+ else
109
+ if add
110
+ target_hash = source_hash
111
+ save_change << record_id
112
+ end
113
+ end
114
+ end
115
+ end
116
+ end
117
+
118
+ end
@@ -0,0 +1,27 @@
1
+ class HashModel
2
+
3
+ # Search creating a new instance of HashModel based on this one
4
+ def where(index_search=:DontSearchForThis_195151c48a254db2949ed102c81ec579, &block_search)
5
+ self.clone.where!(index_search, &block_search)
6
+ end
7
+
8
+ # Search the flattened records using the default flatten index or a boolean block
9
+ def where!(index_search=:DontSearchForThis_195151c48a254db2949ed102c81ec579, &block_search)
10
+ filter(index_search, &block_search)
11
+
12
+ # Delete the raw records that don't have matches
13
+ good_group_ids = unflatten(@modified_data).collect{|record| record[:_group_id]}.uniq
14
+ index = -1;
15
+ @raw_data.delete_if {|record| !good_group_ids.include?(index+=1)}
16
+
17
+ # Remove the non-matching values from raw data for the filter_index
18
+ good_values = self.collect{|record| record[@flatten_index]}
19
+ @raw_data.each do |record|
20
+ if record[@flatten_index].is_a? Array
21
+ record[@flatten_index].delete_if{|value| !good_values.include?(value) }
22
+ end
23
+ end
24
+ self
25
+ end
26
+
27
+ end
@@ -3,7 +3,7 @@ class HashModel
3
3
  MAJOR = 0
4
4
  MINOR = 4
5
5
  TINY = 0
6
- PRE = "beta1"
6
+ PRE = "beta2"
7
7
 
8
8
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')
9
9
 
@@ -1,5 +1,6 @@
1
1
  $: << '.'
2
2
  require 'sourcify'
3
+ require 'monkey_patch/deep_clone'
3
4
  require 'hash_model/version'
4
5
  require 'hash_model/exceptions'
5
6
  require 'hash_model/hash_model'
@@ -0,0 +1,27 @@
1
+ class Object
2
+
3
+ # Make sure there are no references left over
4
+ def deep_clone
5
+ Marshal.load(Marshal.dump(self))
6
+ end
7
+
8
+ # It's annoying to raise an error if an object can't
9
+ # be cloned, like in the case of symbols, It is much
10
+ # more friendly, and less surprising too, just to
11
+ # return the same object so you can go about your work.
12
+ # The only reason I clone is to protect the values, if
13
+ # the values don't need to be protected I don't want an
14
+ # annoying error message hosing up my whole day. </rant>
15
+ #
16
+ # Note: this is needed so deep_clone can work properly... don't get me started.
17
+ alias :__stupid_clone__ :clone
18
+ def clone
19
+ # I wonder what this will break...
20
+ begin
21
+ self.__stupid_clone__
22
+ rescue
23
+ self
24
+ end
25
+ end
26
+
27
+ end
@@ -0,0 +1,6 @@
1
+ require 'spec_helper'
2
+
3
+ describe HashModel do
4
+
5
+
6
+ end
@@ -0,0 +1,338 @@
1
+ require 'spec_helper'
2
+
3
+ describe HashModel do
4
+
5
+ context "when adding records" do
6
+
7
+ it "should allow a hash of values to be added" do
8
+ proc { @hm << {:switch => ["-x", "--xtended"], :description => "Xish stuff"} }.should_not raise_error
9
+ end
10
+
11
+ it "should allow a single hash to be added with :raw_data" do
12
+ hash = {:switch => ["-x", "--xtended"], :description => "Xish stuff"}
13
+ @hm = HashModel.new(:raw_data => hash)
14
+ @hm.raw_data.should == [{:switch=>["-x", "--xtended"], :description=>"Xish stuff"}]
15
+ end
16
+
17
+ it "should allow a hash of values to be added using the keyword 'add'" do
18
+ proc { @hm.add(:switch => ["-x", "--xtended"], :description => "Xish stuff") }.should_not raise_error
19
+ end
20
+
21
+ it "should allow an array of hashes to be added as if they were multiple records" do
22
+ proc { @hm << @records }.should_not raise_error
23
+ end
24
+
25
+ it "should allow another HashModel to be added" do
26
+ @hm.add(@hm2).should == @flat_records_all
27
+ end
28
+
29
+ it "should add a hash with a symbol as a value" do
30
+ @hm = HashModel.new
31
+ @hm << {:switch => :default}
32
+ @hm.should == [{:switch=>:default, :_id=>0, :_group_id=>0}]
33
+ end
34
+
35
+ it "should add an array with mixed value types" do
36
+ @records = [
37
+ {:switch => ["-x", "--xtended", :default], :parameter => {:type => String, :required => true}, :description => "Xish stuff"},
38
+ {:switch => ["-y", "--why"], :description => "lucky what?"},
39
+ {:switch => "-z", :parameter => {:type => String}, :description => "zee svitch zu moost calz"},
40
+ ]
41
+ @hm = HashModel.new(:raw_data=>@records)
42
+ @hm.should == [
43
+ {:switch=>"-x", :parameter=>{:type=>String, :required=>true}, :description=>"Xish stuff", :_id=>0, :_group_id=>0},
44
+ {:switch=>"--xtended", :parameter=>{:type=>String, :required=>true}, :description=>"Xish stuff", :_id=>1, :_group_id=>0},
45
+ {:switch=>:default, :parameter=>{:type=>String, :required=>true}, :description=>"Xish stuff", :_id=>2, :_group_id=>0},
46
+ {:switch=>"-y", :description=>"lucky what?", :_id=>3, :_group_id=>1},
47
+ {:switch=>"--why", :description=>"lucky what?", :_id=>4, :_group_id=>1},
48
+ {:switch=>"-z", :parameter=>{:type=>String}, :description=>"zee svitch zu moost calz", :_id=>5, :_group_id=>2}
49
+ ]
50
+ end
51
+
52
+ it "should add an array of arrays as values and not recurse into them" do
53
+ @records = [
54
+ { :switch => [ [5, 6], [1, 2] ] }
55
+ ]
56
+ @hm = HashModel.new(:raw_data=>@records)
57
+ @hm.should == [{:switch=>[5, 6], :_id=>0, :_group_id=>0}, {:switch=>[1, 2], :_id=>1, :_group_id=>0}]
58
+ end
59
+
60
+ it "shouldn't recurse into arrays with hash values" do
61
+ @records = [
62
+ { :switch => [ [5, 6], [1, :blah=>2] ] }
63
+ ]
64
+ @hm = HashModel.new(:raw_data=>@records)
65
+ @hm.should == [{:switch=>[5, 6], :_id=>0, :_group_id=>0}, {:switch=>[1, :blah=>2], :_id=>1, :_group_id=>0}]
66
+ end
67
+
68
+ it "should allow an array of HashModels to be added" do
69
+ @hm.add([@hm, @hm2])
70
+ @hm.should == [
71
+ {:switch=>"-x", :parameter=>{:type=>String, :required=>true}, :description=>"Xish stuff", :_id=>0, :_group_id=>0},
72
+ {:switch=>"--xtended", :parameter=>{:type=>String, :required=>true}, :description=>"Xish stuff", :_id=>1, :_group_id=>0},
73
+ {:switch=>"-y", :description=>"lucky what?", :_id=>2, :_group_id=>1},
74
+ {:switch=>"--why", :description=>"lucky what?", :_id=>3, :_group_id=>1},
75
+ {:switch=>"-z", :parameter=>{:type=>String}, :description=>"zee svitch zu moost calz", :_id=>4, :_group_id=>2},
76
+ {:switch=>"-x", :parameter=>{:type=>String, :required=>true}, :description=>"Xish stuff", :_id=>5, :_group_id=>3},
77
+ {:switch=>"--xtended", :parameter=>{:type=>String, :required=>true}, :description=>"Xish stuff", :_id=>6, :_group_id=>3},
78
+ {:switch=>"-y", :description=>"lucky what?", :_id=>7, :_group_id=>4},
79
+ {:switch=>"--why", :description=>"lucky what?", :_id=>8, :_group_id=>4},
80
+ {:switch=>"-z", :parameter=>{:type=>String}, :description=>"zee svitch zu moost calz", :_id=>9, :_group_id=>5},
81
+ {:switch=>"-p", :parameter=>{:type=>Hash, :required=>false}, :description=>"Pea soup", :_id=>10, :_group_id=>6},
82
+ {:switch=>"--pea", :parameter=>{:type=>Hash, :required=>false}, :description=>"Pea soup", :_id=>11, :_group_id=>6},
83
+ {:switch=>"-q", :description=>"exit the game", :_id=>12, :_group_id=>7},
84
+ {:switch=>"--quit", :description=>"exit the game", :_id=>13, :_group_id=>7},
85
+ {:switch=>"-r", :parameter=>{:type=>Fixnum}, :description=>"Arrrrrrrrrrgh!", :_id=>14, :_group_id=>8}
86
+ ]
87
+ end
88
+
89
+ it "should allow field names that are longer versions of other names" do
90
+ @hm = HashModel.new
91
+ @hm << {:long => "short", :longer => "long"}
92
+ @hm.should == [{:long => "short", :longer => "long"}]
93
+ end
94
+
95
+ it "should raise an error if something other than a hash, an array of hashes, or another HashModel (or an array of them) is added" do
96
+ proc { @hm << ["-x", "--xtended", "Xish stuff"] }.should raise_error
97
+ end
98
+
99
+ it "should allow an array of hashes to be specified when creating the HashModel" do
100
+ proc { HashModel.new(:raw_data=>@records) }.should_not raise_error
101
+ end
102
+
103
+ it "should retain the raw data used when creating the HashModel" do
104
+ @hm.raw_data.should == @records
105
+ end
106
+
107
+ it "should return a HashModel object when adding records using <<" do
108
+ (@hm << @records[0]).class.should == HashModel
109
+ end
110
+
111
+ it "should return the same HashModel instance when adding records using <<" do
112
+ (@hm << @records[0]).object_id.should == @hm.object_id
113
+ end
114
+
115
+ it "should allow chaining of record adds using <<" do
116
+ proc {@hm << @records[0] << @records[1] << @records[2]}.should_not raise_error
117
+ end
118
+
119
+ it "should contain all of the records when chaining record adds" do
120
+ @hm = HashModel.new
121
+ @hm << @records[0] << @records[1] <<@records[2]
122
+ @hm.raw_data.should == @records
123
+ end
124
+
125
+ context "flattened records" do
126
+
127
+ it "should allow a flattened record to be added" do
128
+ @hm = HashModel.new
129
+ @hm << {:switch=>"-x", :parameter__type=>String, :parameter__required=>true, :description=>"Xish stuff"}
130
+ @hm.raw_data.should == [{:switch => "-x", :parameter => {:type => String, :required => true}, :description => "Xish stuff"}]
131
+ end
132
+
133
+ it "should allow a flattened record to be added even with arrays in it" do
134
+ @hm = HashModel.new
135
+ @hm << {:switch=>["-x", "--xtend"],
136
+ :parameter__type=>String,
137
+ :parameter__required=>true,
138
+ :description=>"Xish stuff",
139
+ :field__field2 => {:field3 => "ff3", :field4 => "ff4"}
140
+ }
141
+ @hm.raw_data.should == [
142
+ {
143
+ :switch => ["-x", "--xtend"],
144
+ :parameter => {:type => String, :required => true},
145
+ :description => "Xish stuff",
146
+ :field => {:field2 => {:field3 => "ff3", :field4 => "ff4"}}
147
+ }
148
+ ]
149
+ end
150
+
151
+ it "should allow deeply flattened record to be added" do
152
+ deep_hash = {
153
+ :parameter__type=>String,
154
+ :switch__deep1__deep3 => "deepTwo",
155
+ :parameter__type__ruby=>true,
156
+ :parameter => "glorp",
157
+ :parameter__required=>true,
158
+ :switch__deep2 => "deepTwo",
159
+ :description=>"Xish stuff",
160
+ :switch => "--xtend",
161
+ }
162
+ @hm = HashModel.new
163
+ @hm << deep_hash
164
+ @hm.raw_data.should == [
165
+ {
166
+ :parameter => [
167
+ {:type=>String},
168
+ "glorp",
169
+ {:required=>true}
170
+ ],
171
+ :switch => [
172
+ {:deep1 => {:deep3=>"deepTwo"}},
173
+ {:deep2=>"deepTwo"},
174
+ "--xtend"
175
+ ],
176
+ :description=>"Xish stuff"
177
+ }
178
+ ]
179
+ end
180
+
181
+
182
+ end
183
+
184
+ context "using the + sign" do
185
+
186
+ it "should return a HashModel class when adding an Array" do
187
+ (@hm + @records2).class.should == HashModel
188
+ end
189
+
190
+ it "should return a HashModel class when adding a HashModel" do
191
+ (@hm + @hm2).class.should == HashModel
192
+ end
193
+
194
+ it "should return a different HashModel instance" do
195
+ (@hm + @records2).object_id.should_not == @hm.object_id
196
+ end
197
+
198
+ it "should contain the records of both recordsets when adding an Array" do
199
+ (@hm + @records2).raw_data.should == (@records + @records2)
200
+ end
201
+
202
+ it "should contain the records of both recordsets when adding a HashModel" do
203
+ (@hm + @hm2).raw_data.should == (@records + @records2)
204
+ end
205
+
206
+ it "should use the flatten index of the receiver HashModel" do
207
+ hm2 = HashModel.new
208
+ hm2 << {:potato=>7}
209
+ (@hm + hm2).flatten_index.should == :switch
210
+ (hm2 + @hm).flatten_index.should == :potato
211
+ end
212
+
213
+ end # "when using the plus sign"
214
+
215
+ context "using the += sign" do
216
+
217
+ it "should return a HashModel class" do
218
+ (@hm += @records2).class.should == HashModel
219
+ end
220
+
221
+ it "should return the same HashModel instance when using += to add an array" do
222
+ (@hm += @records2).object_id.should == @hm.object_id
223
+ end
224
+
225
+ it "should contain the records of both recordsets when adding an Array" do
226
+ @hm += @records2
227
+ @hm.raw_data.should == (@records + @records2)
228
+ end
229
+
230
+ it "should contain the records of both recordsets when adding a HashModel" do
231
+ @hm += @hm2
232
+ @hm.raw_data.should == (@records + @records2)
233
+ end
234
+
235
+ it "should not alter the added HashModel" do
236
+ proc{@hm += @hm2}.should_not change(@hm2, :raw_data)
237
+ end
238
+
239
+ end # "when using the += sign"
240
+
241
+ context "using the * sign" do
242
+
243
+ it "should return a HashRecord" do
244
+ (@hm * 2).class.should == HashModel
245
+ end
246
+
247
+ it "should return a different HashRecord" do
248
+ (@hm * 2).object_id.should_not == @hm.object_id
249
+ end
250
+
251
+ it "should return a HashModel with twice the amount of raw data if * 2'd" do
252
+ (@hm * 2).raw_data.should == [
253
+ {:switch=>["-x", "--xtended"], :parameter=>{:type=>String, :required=>true}, :description=>"Xish stuff"},
254
+ {:switch=>["-y", "--why"], :description=>"lucky what?"},
255
+ {:switch=>"-z", :parameter=>{:type=>String}, :description=>"zee svitch zu moost calz"},
256
+ {:switch=>["-x", "--xtended"], :parameter=>{:type=>String, :required=>true}, :description=>"Xish stuff"},
257
+ {:switch=>["-y", "--why"], :description=>"lucky what?"},
258
+ {:switch=>"-z", :parameter=>{:type=>String}, :description=>"zee svitch zu moost calz"}
259
+ ]
260
+ end
261
+
262
+ end
263
+
264
+ context "using the *= sign" do
265
+
266
+ it "should return the same HashModel" do
267
+ (@hm *= 2).object_id.should == @hm.object_id
268
+ end
269
+
270
+ it "should change current raw to twice its old raw data if *= 2'd" do
271
+ @hm *= 2
272
+ @hm.should == [
273
+ {:switch=>"-x", :parameter=>{:type=>String, :required=>true}, :description=>"Xish stuff", :_id=>0, :_group_id=>0},
274
+ {:switch=>"--xtended", :parameter=>{:type=>String, :required=>true}, :description=>"Xish stuff", :_id=>1, :_group_id=>0},
275
+ {:switch=>"-y", :description=>"lucky what?", :_id=>2, :_group_id=>1},
276
+ {:switch=>"--why", :description=>"lucky what?", :_id=>3, :_group_id=>1},
277
+ {:switch=>"-z", :parameter=>{:type=>String}, :description=>"zee svitch zu moost calz", :_id=>4, :_group_id=>2},
278
+ {:switch=>"-x", :parameter=>{:type=>String, :required=>true}, :description=>"Xish stuff", :_id=>5, :_group_id=>3},
279
+ {:switch=>"--xtended", :parameter=>{:type=>String, :required=>true}, :description=>"Xish stuff", :_id=>6, :_group_id=>3},
280
+ {:switch=>"-y", :description=>"lucky what?", :_id=>7, :_group_id=>4},
281
+ {:switch=>"--why", :description=>"lucky what?", :_id=>8, :_group_id=>4},
282
+ {:switch=>"-z", :parameter=>{:type=>String}, :description=>"zee svitch zu moost calz", :_id=>9, :_group_id=>5}
283
+ ]
284
+ end
285
+
286
+ end
287
+
288
+ context "using concat" do
289
+
290
+ it "should concatinate using a single Hash" do
291
+ @hm.concat(@records2[0]).should == [
292
+ {:switch=>"-x", :parameter=>{:type=>String, :required=>true}, :description=>"Xish stuff", :_id=>0, :_group_id=>0},
293
+ {:switch=>"--xtended", :parameter=>{:type=>String, :required=>true}, :description=>"Xish stuff", :_id=>1, :_group_id=>0},
294
+ {:switch=>"-y", :description=>"lucky what?", :_id=>2, :_group_id=>1},
295
+ {:switch=>"--why", :description=>"lucky what?", :_id=>3, :_group_id=>1},
296
+ {:switch=>"-z", :parameter=>{:type=>String}, :description=>"zee svitch zu moost calz", :_id=>4, :_group_id=>2},
297
+ {:switch=>"-p", :parameter=>{:type=>Hash, :required=>false}, :description=>"Pea soup", :_id=>5, :_group_id=>3},
298
+ {:switch=>"--pea", :parameter=>{:type=>Hash, :required=>false}, :description=>"Pea soup", :_id=>6, :_group_id=>3}
299
+ ]
300
+ end
301
+
302
+ it "should concatinate using an array" do
303
+ @hm.concat(@records2).should == @flat_records_all
304
+ end
305
+
306
+ it "should concatinate using a HashModel" do
307
+ @hm.concat(@hm2).should == @flat_records_all
308
+ end
309
+
310
+ end
311
+
312
+ context "using push" do
313
+
314
+ it "should add a single Hash" do
315
+ @hm.push(@records2[0]).should == [
316
+ {:switch=>"-x", :parameter=>{:type=>String, :required=>true}, :description=>"Xish stuff", :_id=>0, :_group_id=>0},
317
+ {:switch=>"--xtended", :parameter=>{:type=>String, :required=>true}, :description=>"Xish stuff", :_id=>1, :_group_id=>0},
318
+ {:switch=>"-y", :description=>"lucky what?", :_id=>2, :_group_id=>1},
319
+ {:switch=>"--why", :description=>"lucky what?", :_id=>3, :_group_id=>1},
320
+ {:switch=>"-z", :parameter=>{:type=>String}, :description=>"zee svitch zu moost calz", :_id=>4, :_group_id=>2},
321
+ {:switch=>"-p", :parameter=>{:type=>Hash, :required=>false}, :description=>"Pea soup", :_id=>5, :_group_id=>3},
322
+ {:switch=>"--pea", :parameter=>{:type=>Hash, :required=>false}, :description=>"Pea soup", :_id=>6, :_group_id=>3}
323
+ ]
324
+ end
325
+
326
+ it "should add an array" do
327
+ @hm.push(@records2).should == @flat_records_all
328
+ end
329
+
330
+ it "should add a HashModel" do
331
+ @hm.push(@hm2).should == @flat_records_all
332
+ end
333
+
334
+ end
335
+
336
+ end # adding records
337
+
338
+ end