hashmodel 0.4.0.beta1 → 0.4.0.beta2

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.
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