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.
- data/Gemfile.lock +1 -1
- data/LICENSE.txt +1 -1
- data/README.markdown +65 -27
- data/hashmodel.gemspec +1 -1
- data/lib/hash_model/hash_model.rb +13 -105
- data/lib/hash_model/hash_model_create_object.rb +62 -0
- data/lib/hash_model/hash_model_delete.rb +13 -0
- data/lib/hash_model/hash_model_filter.rb +37 -0
- data/lib/hash_model/hash_model_flatten.rb +47 -0
- data/lib/hash_model/hash_model_group.rb +18 -0
- data/lib/hash_model/hash_model_update.rb +118 -0
- data/lib/hash_model/hash_model_where.rb +27 -0
- data/lib/hash_model/version.rb +1 -1
- data/lib/hashmodel.rb +1 -0
- data/lib/monkey_patch/deep_clone.rb +27 -0
- data/spec/hash_model/_current_spec.rb +6 -0
- data/spec/hash_model/hash_model_adding_records_spec.rb +338 -0
- data/spec/hash_model/hash_model_array_methods_spec.rb +132 -0
- data/spec/hash_model/hash_model_comparisons_spec.rb +55 -0
- data/spec/hash_model/hash_model_delete_spec.rb +51 -0
- data/spec/hash_model/hash_model_flattening_spec.rb +92 -0
- data/spec/hash_model/hash_model_group_spec.rb +67 -0
- data/spec/hash_model/hash_model_searching_spec.rb +245 -0
- data/spec/hash_model/hash_model_spec.rb +1 -1
- data/spec/hash_model/hash_model_unflattening_spec.rb +34 -0
- data/spec/hash_model/hash_model_update_spec.rb +147 -0
- data/spec/hash_model/hash_model_where_spec.rb +287 -0
- data/spec/support/configuration.rb +50 -0
- data/spec/support/debug_print.rb +13 -0
- data/spec/support/proc_tester.rb +17 -0
- metadata +49 -27
- data/_brainstorm/StrangeMarshal.txt +0 -0
- data/_brainstorm/_readme +0 -1
- data/_brainstorm/block_wheres.rb +0 -80
- data/_brainstorm/clone.rb +0 -19
- data/_brainstorm/hash_model_examples.rb +0 -57
- data/_brainstorm/hash_test.rb +0 -169
- data/_brainstorm/inspect.rb +0 -26
- data/_brainstorm/instance_vars.rb +0 -24
- data/_brainstorm/proc_tests.rb +0 -14
- data/_brainstorm/ref_val.rb +0 -16
- data/_brainstorm/regex_captures.rb +0 -18
- data/_brainstorm/spliting.rb +0 -46
- data/_brainstorm/test.rb +0 -27
- 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
|
data/lib/hash_model/version.rb
CHANGED
data/lib/hashmodel.rb
CHANGED
@@ -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,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
|