hashmodel 0.4.0.rc1 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- hashmodel (0.4.0.rc1)
4
+ hashmodel (0.4.0)
5
5
  file-tail (~> 1.0)
6
6
  sourcify (~> 0.4)
7
7
 
@@ -1,12 +1,22 @@
1
1
  # HashModel
2
2
 
3
- A simple MVC type model class for storing deeply nested hashes as records.
4
- It's meant to be used for small, in-memory recordset that you want an easy, flexible way to query.
3
+ A hash based MVC model class that makes searching and updating deeply nested hashes a breeze.
4
+ It's meant to be used for small, in-memory hash based recordset that you want an easy, flexible way to query and update.
5
5
  It is not meant as a data storage device for managing huge datasets.
6
6
 
7
+ ## Testing for a Better Tomorrow ##
8
+
9
+ I have the gem set up to ask if you would like to test on install so please allow the tests to run and upload. This will allow me to find any problems on different platforms.
10
+
11
+ You can take a look at the test results yourself here:
12
+
13
+ <http://test.rubygems.org/gems/hashmodel/v/0.4.0>
14
+
15
+ Thanks for your help.
16
+
7
17
  ## Synopsis ##
8
18
 
9
- The major usefulness of this class is it allows you to filter and search flattened records based on any field. You can even updated and delete data now! Exclamation!
19
+ HashModel allows you to filter, search, and updated flattened records based on any field, even deeply nested ones. You can even updated and delete data!
10
20
 
11
21
  A field can contain anything, including another hash, a string, an array, or even an Object class like String or Array, not just an instance of an Object class.
12
22
 
@@ -21,28 +31,60 @@ Searches are very simple and logical. You can search using just using the value
21
31
  hash_model = HashModel.new(:raw_data=>records)
22
32
  found = hash_model.where("-x") => Returns an array of flattened records
23
33
 
34
+ If you want to filter the data temporarily, but not delete any data, use `filter`:
24
35
 
25
- Or more powerfully you can search using boolean like logic with dynamic variables:
36
+ x = "-x"
37
+ found = hash_model.filter{:switch == x}
38
+ found == hash_model # => true
39
+
40
+ # To clear the filter just call it without any parameters
41
+ hash_model.filter
42
+ found.filter
43
+ found == hash_model # => true
44
+
45
+ If you want a copy of your data with just the records that don't match your query use `where`:
26
46
 
47
+ param_type = String
48
+ found = hash_model.where{:parameter__type == param_type}
49
+ found.raw_data != hash_model.raw_data # => true
50
+
51
+ To permanently remove the raw data that doesn't match your query use `where!`:
52
+
53
+ param_type = String
54
+ found = hash_model.where!{:parameter__type == param_type}
55
+ found.raw_data == hash_model.raw_data # => true
56
+
57
+ If you want a copy of your data with the data updated use `update`:
58
+
59
+ where = {:switch == "-x"}
60
+ param_type = String
61
+ updated = hash_model.update(:parameter__type => param_type) &where
62
+ updated.raw_data != hash_model.raw_data # => true
63
+
64
+ As you would expect you can also update your data in place using `update!`:
65
+
27
66
  x = "-x"
28
67
  param_type = String
29
- found = hash_model.where {:switch == x && :parameter__type == param_type} => Returns an array of flattened records
68
+ updated = hash_model.update!(x, :parameter__type => param_type)
69
+ updated.raw_data == hash_model.raw_data # => true
30
70
 
31
- If you want to update a record in place you can do that as well:
71
+ If you want to update a recored an add a field if it doesn't exist you can use `update_and_add`:
32
72
 
33
73
  x = "-x"
34
74
  param_type = String
35
- updated = hash_model.update!(x, :parameter__type => param_type) => Returns an array of flattened records
75
+ updated = hash_model.update!(x, :parameter__type => param_type)
76
+ updated.raw_data == hash_model.raw_data # => true
77
+
36
78
 
37
79
  For more info checkout the rdocs and also checkout the change history below. I go in-depth on the new method calls.
38
80
 
39
81
  ## Status ##
40
82
 
41
- 2011.03.19 - Beta: 0.4.0.beta2
83
+ 2011.03.23 - Release: 0.4.0
42
84
 
43
85
  Lots of changes with this one. The major changes are the ability to write to the HashModel data. See Version History for details.
44
86
 
45
- I fixed a **huge** bug that caused variables not to be ignored in boolean searches. It's all fixed now and there are specs to prove make sure it doesn't happen again.
87
+ I fixed a **huge** bug that caused variables to be ignored in boolean searches. It's all fixed now and there are specs to prove make sure it doesn't happen again.
46
88
 
47
89
  ## Demo App ##
48
90
 
@@ -233,11 +275,13 @@ I've covered most of the major stuff here but to see all of the functionality ta
233
275
 
234
276
  ## Version History ##
235
277
 
236
- 0.4.0.beta2 - 2011.03-21
278
+ 0.4.0 - 2011.03-23
279
+
280
+ **Lots of updates and a major bug fix for this release.**
237
281
 
238
- Lots of updates and code fixes for this release. After using it for a little while I've broken down and added the write functionality I was avoiding previously.
282
+ After using it for a little while I've broken down and added the write functionality I was avoiding previously.
239
283
 
240
- I fixed a **major** bug/design flaw that didn't let you use variables in a boolean block. For instance this was supposed to work but didn't:
284
+ I also fixed a **major** bug/design flaw that didn't let you use variables in a boolean block. For instance this was supposed to work but didn't:
241
285
 
242
286
  x = "-x"
243
287
  hm.where{:x == x}
@@ -246,7 +290,7 @@ It works properly now.
246
290
 
247
291
  **Additions/Changes**
248
292
 
249
- #### Methods: `update` and `update!` methods ####
293
+ #### Methods: `update` and `update!` ####
250
294
  These methods use a `where` like search that is slightly different. As you would expect the `update` method returns a changed copy of the HashModel while `update!` changes the data in place.
251
295
 
252
296
  The methods look like this `update(default_index_search, field_new_value_hash, boolean_search_block)`. So if you search using a single value, a default index search, then you put the update hashes at the end. If you want to search using a boolean search then you put the update hashes at the beginning.
@@ -273,7 +317,7 @@ You don't have to put in any search criteria at all though, you can just put in
273
317
 
274
318
  All of the `update` methods will return the records that were updated with the updates in place. If no records were updated then it will return an empty array. It currently returns the raw records that are or would be deleted but I will be changing it to return the flattened records. That will be in the next update.
275
319
 
276
- #### Methods: `update_and_add` and `update_and_add!` methods ####
320
+ #### Methods: `update_and_add` and `update_and_add!` ####
277
321
  Just like `update` and `update!` but will also add a hash if it doesn't exist already. Again the ! method changes the records in place.
278
322
 
279
323
  For instance if your HashModel has a record like `{:a=>"a"}` and you do `my_hash_model.update(:b=>"b")` it won't change that record, but it you do `my_hash_model.update_and_add(:b=>"b")` then your record will be `{:a=>'a', :b=>"b"}`.
@@ -300,7 +344,7 @@ Since bangs (!) are all now destructive, to bring the class inline with Ruby sta
300
344
  #### Other changes ####
301
345
  Because of the new destructive methods all input values will be cloned. You don't have to worry about cloning input objects yourself. If it's clonable HashModel will clone it.
302
346
 
303
- I've reorganized the code into multiple files based on functionality to make it easier to debug when adding new features. This is in anticipation of a major cleanup and refactoring.
347
+ I've reorganized the code into multiple files based on functionality to make it easier to debug when adding new features. I've done some refactoring but I plan on a major cleanup and refactoring for the next version. That version won't have too many new features but will be a major clean up and optimization.
304
348
 
305
349
  Cleaned up RSpecs a little along the lines of reorganization.
306
350
 
@@ -308,6 +352,7 @@ Cleaned up RSpecs a little along the lines of reorganization.
308
352
  * Didn't use variable in where searches.
309
353
  * Threw error if you searched an empty HashModel (can't build a flatten index on nothing)
310
354
  * Couldn't change the flatten index in some rare cases.
355
+ * Threw error when filtering on non-existent fields.
311
356
 
312
357
  0.3.1 - 2011.03.18
313
358
 
@@ -343,6 +388,13 @@ e.g. hash_model.where{:x == "x" && :y == "y"} instead of the less natural hash_m
343
388
  * Initial publish
344
389
  * Released on wrong RubyGems account (yanked)
345
390
 
391
+ ## Planned Updates ##
392
+
393
+ * Add a simple load/save to file methodology that's **FAST!**
394
+ * Make :\_group\_id and :\_id fields not show up by default. They'll still be accessible but they are really more internal values than external so it would be cleaner not to show them.
395
+ * Major refactor for memory efficiency, speed, and general code de-stink.
396
+ * Allow additive filters, i.e. filter on one thing then filter on another and they second filter is based on the first filter. This would also be applicable for `update` and `where` methods; any time a filter is given.
397
+
346
398
 
347
399
  ## Contributing to HashModel ##
348
400
 
@@ -11,7 +11,14 @@ Gem::Specification.new do |s|
11
11
  s.email = ["mikbe.tk@gmail.com"]
12
12
  s.homepage = "http://mikbe.tk"
13
13
  s.summary = %q{A hash based MVC model class that makes searching and updating deeply nested hashes a breeze.}
14
- s.description = %q{A hash based MVC model class that makes searching and updating deeply nested hashes a breeze. You can store deeply nested hashes and still easily flatten, query, and update the records using flattened field names.}
14
+ s.description =
15
+ """
16
+ A hash based MVC model class that makes searching and updating deeply nested hashes a breeze.
17
+ You can store deeply nested hashes and still easily flatten, query, and update the records using flattened field names.
18
+ It's meant to be used for small, in-memory recordset that you want an easy, flexible way to query.
19
+ It is not meant as a data storage device for managing huge datasets.
20
+ """
21
+
15
22
  s.license = 'MIT'
16
23
 
17
24
  s.add_dependency "sourcify", "~>0.4"
@@ -11,7 +11,7 @@ class HashModel
11
11
  # is based on the raw_data that's changed that record will be changed too.
12
12
  # Returns the records that were updated.
13
13
  def update!(index_search=:DontSearchForThis_195151c48a254db2949ed102c81ec579, update_hash, &block_search)
14
-
14
+
15
15
  # only filter if they sent something to be filter otherwise leave the filter alone
16
16
  unless index_search == :DontSearchForThis_195151c48a254db2949ed102c81ec579 && block_search.nil?
17
17
  old_filter = @filter_proc
@@ -99,7 +99,7 @@ class HashModel
99
99
  source_hash.each do |source_hash_key, source_hash_value|
100
100
  current_key = "#{parent_key}#{"__" if parent_key}#{source_hash_key}".to_sym
101
101
  if current_key == terminal_key
102
- return unless target_hash[source_hash_key] or add
102
+ return if target_hash.nil? or !(target_hash[source_hash_key] or add)
103
103
  target_hash[source_hash_key] = source_hash_value
104
104
  save_change << record_id
105
105
  else
@@ -3,8 +3,8 @@ class HashModel
3
3
  MAJOR = 0
4
4
  MINOR = 4
5
5
  TINY = 0
6
- PRE = "rc1"
7
-
6
+ PRE = nil
7
+
8
8
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')
9
9
 
10
10
  SUMMARY = "HashModel #{STRING}"
@@ -0,0 +1,35 @@
1
+ require "spec_helper"
2
+
3
+ describe HashModel do
4
+
5
+ let(:hm){
6
+ HashModel.new(:raw_data=> [
7
+ {:a=>"a", :b=>{:b1=>"b1", :b2=>{:b2a =>"b2a", :b2b=>"b2b"}}},
8
+ {:a=>"17", :b=>"7291", :c=>"0"},
9
+ {:a=>"17", :b=>"080", :c=>"1"},
10
+ {:a=>"17", :b=>"24134", :c=>2}
11
+ ]
12
+ )
13
+ }
14
+
15
+ context "when using a block" do
16
+
17
+ it "should accept a block variable" do
18
+ where = lambda {:a == "a"}
19
+ lambda{hm.filter &where}.should change{hm.clone}
20
+ .from( [
21
+ {:a=>"a", :b=>{:b1=>"b1", :b2=>{:b2a =>"b2a", :b2b=>"b2b"}}},
22
+ {:a=>"17", :b=>"7291", :c=>"0"},
23
+ {:a=>"17", :b=>"080", :c=>"1"},
24
+ {:a=>"17", :b=>"24134", :c=>2}
25
+ ]
26
+ )
27
+ .to( [
28
+ {:a=>"a", :b=>{:b1=>"b1", :b2=>{:b2a =>"b2a", :b2b=>"b2b"}}, :_id=>0, :_group_id=>0}
29
+ ]
30
+ )
31
+ end
32
+
33
+ end
34
+
35
+ end
@@ -5,7 +5,6 @@ describe HashModel do
5
5
  context "when updating records" do
6
6
 
7
7
  let(:hm){HashModel.new(:raw_data=> {:a=>"a", :b=>{:b1=>"b1", :b2=>{:b2a =>"b2a", :b2b=>"b2b"}}} )}
8
-
9
8
 
10
9
  context "and the search key exists in the target" do
11
10
 
@@ -103,7 +102,46 @@ describe HashModel do
103
102
  context "after changing the filtered field to something that doesn't match the filter" do
104
103
  it {hm.update!(:a=>"BBBB"); hm.length.should == 0}
105
104
  end
106
-
105
+
106
+ context "and using variables for the update values" do
107
+
108
+ it "should return the updated records" do
109
+ update_field = :b
110
+ update_to = "23"
111
+ update_hash = {update_field => update_to}
112
+ hm.update!(update_hash).should == [
113
+ {:a=>"17", :b=>"23", :c=>"0", :_id=>1, :_group_id=>1},
114
+ {:a=>"17", :b=>"23", :c=>"1", :_id=>2, :_group_id=>2},
115
+ {:a=>"17", :b=>"23", :c=>2, :_id=>3, :_group_id=>3}
116
+ ]
117
+ end
118
+
119
+ it "should not update records not in the current filter" do
120
+ update_field = :b
121
+ update_to = "23"
122
+ update_hash = {update_field => update_to}
123
+ lambda{hm.update!(update_hash)}.should_not change{hm.raw_data.clone[0]}
124
+ end
125
+
126
+ it "should return the updated records even if the primary key is the one changed" do
127
+ update_field = :a
128
+ update_to = "BBBB"
129
+ update_hash = {update_field => update_to}
130
+ hm.update!(update_hash).should == [
131
+ {:a=>"BBBB", :b=>"7291", :c=>"0", :_id=>1, :_group_id=>1},
132
+ {:a=>"BBBB", :b=>"080", :c=>"1", :_id=>2, :_group_id=>2},
133
+ {:a=>"BBBB", :b=>"24134", :c=>2, :_id=>3, :_group_id=>3}
134
+ ]
135
+ end
136
+
137
+ context "after changing the filtered field to something that doesn't match the filter in a destructive update" do
138
+ update_field = :a
139
+ update_to = "BBBB"
140
+ update_hash = {update_field => update_to}
141
+ it {hm.update!(update_hash); hm.length.should == 0}
142
+ end
143
+ end
144
+
107
145
  end
108
146
 
109
147
  context "when using non-destructive methods" do
@@ -141,6 +179,101 @@ describe HashModel do
141
179
 
142
180
 
143
181
  end
182
+
183
+ context "when the update field doesn't exist" do
184
+
185
+ before(:each) do
186
+ hm << {:a=>"17", :b=>"7291", :c=>"0"}
187
+ hm << {:a=>"17", :b=>"080", :c=>"1"}
188
+ hm << {:a=>"17", :b=>"24134", :c=>2}
189
+ end
190
+
191
+ it {lambda{hm.update({:parameter__type => String})}.should_not raise_error}
192
+ it {hm.update(:parameter__type => String).should be_empty}
193
+
194
+ context "and using a block variable search" do
195
+
196
+ it "should not raise an error" do
197
+ where = lambda {:a == "a"}
198
+ param_type = String
199
+ lambda{hm.update({:parameter__type => param_type}, &where)}.should_not raise_error
200
+ end
201
+
202
+ it "should return an empty recordset" do
203
+ where = lambda {:a == "a"}
204
+ param_type = String
205
+ hm.update(:parameter__type => param_type, &where).should be_empty
206
+ end
207
+
208
+ end
209
+
210
+ end
211
+
212
+ context "when the search field doesn't exist" do
213
+
214
+ before(:each) do
215
+ hm << {:a=>"17", :b=>"7291", :c=>"0"}
216
+ hm << {:a=>"17", :b=>"080", :c=>"1"}
217
+ hm << {:a=>"17", :b=>"24134", :c=>2}
218
+ end
219
+
220
+ it {lambda{hm.update({:b=>"blorg"},lambda {:parameter__type == String})}.should_not raise_error}
221
+ it {hm.update({:b=>"blorg"},lambda {:parameter__type == String}).should be_empty}
222
+
223
+ context "and using a block variable search" do
224
+
225
+ it "should not raise an error" do
226
+ param_type = String
227
+ where = lambda {:parameter__type == param_type}
228
+ update_to_value = "g"
229
+ update_to = {:a => update_to_value}
230
+ lambda{hm.update(update_to, &where)}.should_not raise_error
231
+ end
232
+
233
+ it "should return an empty recordset" do
234
+ param_type = String
235
+ where = lambda {:parameter__type == param_type}
236
+ update_to_value = "g"
237
+ update_to = {:a => update_to_value}
238
+ hm.update(update_to, &where).should be_empty
239
+ end
240
+
241
+ end
242
+
243
+ end
244
+
245
+ context "when the neither the search field nor the update field exist" do
246
+
247
+ before(:each) do
248
+ hm << {:a=>"17", :b=>"7291", :c=>"0"}
249
+ hm << {:a=>"17", :b=>"080", :c=>"1"}
250
+ hm << {:a=>"17", :b=>"24134", :c=>2}
251
+ end
252
+
253
+ it {lambda{hm.update({:g=>"blorg"},lambda {:parameter__type == String})}.should_not raise_error}
254
+ it {hm.update({:g=>"blorg"},lambda {:parameter__type == String}).should be_empty}
255
+
256
+ context "and using variables in the search and update" do
257
+
258
+ it "should not raise an error" do
259
+ param_type = String
260
+ where = lambda {:flappidy == param_type}
261
+ update_to_value = "malamute"
262
+ update_to = {:puppies => update_to_value}
263
+ lambda{hm.update(update_to, &where)}.should_not raise_error
264
+ end
265
+
266
+ it "should return an empty recordset" do
267
+ param_type = String
268
+ where = lambda {:zippidy__do__da == param_type}
269
+ update_to_value = "delicious"
270
+ update_to = {:goldfish => update_to_value}
271
+ hm.update(update_to, &where).should be_empty
272
+ end
273
+
274
+ end
275
+
276
+ end
144
277
 
145
278
  end
146
279
 
metadata CHANGED
@@ -1,20 +1,20 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hashmodel
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0.rc1
5
- prerelease: 6
4
+ version: 0.4.0
5
+ prerelease:
6
6
  platform: ruby
7
7
  authors:
8
8
  - Mike Bethany
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-03-21 00:00:00.000000000 -04:00
12
+ date: 2011-03-23 00:00:00.000000000 -04:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: sourcify
17
- requirement: &2158444080 !ruby/object:Gem::Requirement
17
+ requirement: &2154533780 !ruby/object:Gem::Requirement
18
18
  none: false
19
19
  requirements:
20
20
  - - ~>
@@ -22,10 +22,10 @@ dependencies:
22
22
  version: '0.4'
23
23
  type: :runtime
24
24
  prerelease: false
25
- version_requirements: *2158444080
25
+ version_requirements: *2154533780
26
26
  - !ruby/object:Gem::Dependency
27
27
  name: file-tail
28
- requirement: &2158443580 !ruby/object:Gem::Requirement
28
+ requirement: &2154533200 !ruby/object:Gem::Requirement
29
29
  none: false
30
30
  requirements:
31
31
  - - ~>
@@ -33,10 +33,10 @@ dependencies:
33
33
  version: '1.0'
34
34
  type: :runtime
35
35
  prerelease: false
36
- version_requirements: *2158443580
36
+ version_requirements: *2154533200
37
37
  - !ruby/object:Gem::Dependency
38
38
  name: rspec
39
- requirement: &2158443080 !ruby/object:Gem::Requirement
39
+ requirement: &2154532600 !ruby/object:Gem::Requirement
40
40
  none: false
41
41
  requirements:
42
42
  - - ~>
@@ -44,10 +44,10 @@ dependencies:
44
44
  version: '2.5'
45
45
  type: :development
46
46
  prerelease: false
47
- version_requirements: *2158443080
47
+ version_requirements: *2154532600
48
48
  - !ruby/object:Gem::Dependency
49
49
  name: cucumber
50
- requirement: &2158442600 !ruby/object:Gem::Requirement
50
+ requirement: &2154532020 !ruby/object:Gem::Requirement
51
51
  none: false
52
52
  requirements:
53
53
  - - ~>
@@ -55,10 +55,21 @@ dependencies:
55
55
  version: '0.3'
56
56
  type: :development
57
57
  prerelease: false
58
- version_requirements: *2158442600
59
- description: A hash based MVC model class that makes searching and updating deeply
60
- nested hashes a breeze. You can store deeply nested hashes and still easily flatten,
61
- query, and update the records using flattened field names.
58
+ version_requirements: *2154532020
59
+ description: ! '
60
+
61
+ A hash based MVC model class that makes searching and updating deeply nested hashes
62
+ a breeze.
63
+
64
+ You can store deeply nested hashes and still easily flatten, query, and update the
65
+ records using flattened field names.
66
+
67
+ It''s meant to be used for small, in-memory recordset that you want an easy, flexible
68
+ way to query.
69
+
70
+ It is not meant as a data storage device for managing huge datasets.
71
+
72
+ '
62
73
  email:
63
74
  - mikbe.tk@gmail.com
64
75
  executables: []
@@ -101,6 +112,7 @@ files:
101
112
  - spec/hash_model/hash_model_array_methods_spec.rb
102
113
  - spec/hash_model/hash_model_comparisons_spec.rb
103
114
  - spec/hash_model/hash_model_delete_spec.rb
115
+ - spec/hash_model/hash_model_filter_spec.rb
104
116
  - spec/hash_model/hash_model_flattening_spec.rb
105
117
  - spec/hash_model/hash_model_group_spec.rb
106
118
  - spec/hash_model/hash_model_searching_spec.rb
@@ -130,9 +142,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
130
142
  required_rubygems_version: !ruby/object:Gem::Requirement
131
143
  none: false
132
144
  requirements:
133
- - - ! '>'
145
+ - - ! '>='
134
146
  - !ruby/object:Gem::Version
135
- version: 1.3.1
147
+ version: '0'
136
148
  requirements: []
137
149
  rubyforge_project:
138
150
  rubygems_version: 1.6.2
@@ -150,6 +162,7 @@ test_files:
150
162
  - spec/hash_model/hash_model_array_methods_spec.rb
151
163
  - spec/hash_model/hash_model_comparisons_spec.rb
152
164
  - spec/hash_model/hash_model_delete_spec.rb
165
+ - spec/hash_model/hash_model_filter_spec.rb
153
166
  - spec/hash_model/hash_model_flattening_spec.rb
154
167
  - spec/hash_model/hash_model_group_spec.rb
155
168
  - spec/hash_model/hash_model_searching_spec.rb