Security News
Weekly Downloads Now Available in npm Package Search Results
Socket's package search now displays weekly downloads for npm packages, helping developers quickly assess popularity and make more informed decisions.
A hash based MVC model class that makes searching and updating deeply nested hashes a breeze. It's meant to be used for small, in-memory hash based recordset that you want an easy, flexible way to query and update. It is not meant as a data storage device for managing huge datasets.
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.
You can take a look at the test results yourself here:
http://test.rubygems.org/gems/hashmodel/v/0.4.0
Thanks for your help.
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!
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.
Searches are very simple and logical. You can search using just using the value of the default index
require 'hashmodel'
records = [
{:switch => ["-x", "--xtended"], :parameter => {:type => String, :required => true}, :description => "Xish stuff"},
{:switch => ["-y", "--why"], :description => "lucky what?"},
{:switch => "-z", :parameter => {:type => String}, :description => "zee svitch zu moost calz"},
]
hash_model = HashModel.new(:raw_data=>records)
found = hash_model.where("-x") => Returns an array of flattened records
If you want to filter the data temporarily, but not delete any data, use filter
:
x = "-x"
found = hash_model.filter{:switch == x}
found == hash_model # => true
# To clear the filter just call it without any parameters
hash_model.filter
found.filter
found == hash_model # => true
If you want a copy of your data with just the records that don't match your query use where
:
param_type = String
found = hash_model.where{:parameter__type == param_type}
found.raw_data != hash_model.raw_data # => true
To permanently remove the raw data that doesn't match your query use where!
:
param_type = String
found = hash_model.where!{:parameter__type == param_type}
found.raw_data == hash_model.raw_data # => true
If you want a copy of your data with the data updated use update
:
where = {:switch == "-x"}
param_type = String
updated = hash_model.update(:parameter__type => param_type) &where
updated.raw_data != hash_model.raw_data # => true
As you would expect you can also update your data in place using update!
:
x = "-x"
param_type = String
updated = hash_model.update!(x, :parameter__type => param_type)
updated.raw_data == hash_model.raw_data # => true
If you want to update a recored an add a field if it doesn't exist you can use update_and_add
:
x = "-x"
param_type = String
updated = hash_model.update!(x, :parameter__type => param_type)
updated.raw_data == hash_model.raw_data # => true
For more info checkout the rdocs and also checkout the change history below. I go in-depth on the new method calls.
2011.03.23 - Release: 0.4.0
Lots of changes with this one. The major changes are the ability to write to the HashModel data. See Version History for details.
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.
Check out Widget for a demo of just how easy it is to use HashModel. It now has a lot of complexity but it's a breeze to use:
https://github.com/mikbe/widget
I've covered most of the major stuff here but to see all of the functionality take a look at the RSpec files.
require 'hashmodel'
records = [
{:switch => ["-x", "--xtended"], :parameter => {:type => String, :required => true}, :description => "Xish stuff"},
{:switch => ["-y", "--why"], :description => "lucky what?"},
{:switch => "-z", :parameter => {:type => String}, :description => "zee svitch zu moost calz"},
]
hash_model = HashModel.new(:raw_data=>records)
puts hash_model
>> {:switch=>"-x", :parameter=>{:type=>String, :required=>true}, :description=>"Xish stuff", :_id=>0, :_group_id=>0}
>> {:switch=>"--xtended", :parameter=>{:type=>String, :required=>true}, :description=>"Xish stuff", :_id=>1, :_group_id=>0}
>> {:switch=>"-y", :description=>"lucky what?", :_id=>2, :_group_id=>1}
>> {:switch=>"--why", :description=>"lucky what?", :_id=>3, :_group_id=>1}
>> {:switch=>"-z", :parameter=>{:type=>String}, :description=>"zee svitch zu moost calz", :_id=>4, :_group_id=>2}
# You may have noticed that there are two fields you didn't add in the
# flattened records. These are the :_id field and the :_group_id fields.
# :_id is a unique ID for the flattened record while :_group_id is
# unique to the raw record you used to create the HashModel record.
hash_model = HashModel.new
hash_model += records[0]
hash_model.concat records[1]
hash_model.push records[2]
# You can also add another HashModel object to the existing one
# and it will add the raw records and reflatten.
records = [
{:switch => ["-x", "--xtended"], :parameter => {:type => String, :required => true}, :description => "Xish stuff"},
{:switch => ["-y", "--why"], :description => "lucky what?"}
]
records2 = {:switch => "-z", :parameter => {:type => String}, :description => "zee svitch zu moost calz"}
hash_model = HashModel.new(:raw_data => records)
hash_model2 = HashModel.new(:raw_data => records2)
hash_model << hash_model2
# or
hash_model += hash_model2
# You can always edit and access the raw data via the raw_data property accessor
# When you make changes to the raw_data the HashModel will automatically be updated.
records = [
{:switch => ["-x", "--xtended"], :parameter => {:type => String, :required => true}, :description => "Xish stuff"},
{:switch => ["-y", "--why"], :description => "lucky what?"},
{:switch => "-z", :parameter => {:type => String}, :description => "zee svitch zu moost calz"},
]
hash_model = HashModel.new(:raw_data=>records)
puts hash_model.raw_data
>> {:switch => ["-x", "--xtended"], :parameter => {:type => String, :required => true}, :description => "Xish stuff"}
>> {:switch => ["-y", "--why"], :description => "lucky what?"}
>> {:switch => "-z", :parameter => {:type => String}, :description => "zee svitch zu moost calz"}
# the HashModel acts a lot like an array so you can iterate over it
hash_model = HashModel.new(:raw_data=>records)
hash_model.each do |record|
# record is a hash
end
# the HashModel acts a lot like an array so you can iterate over it
hash_model = HashModel.new(:raw_data=>records)
puts hash_model[0]
>> {:switch=>"-x", :parameter=>{:type=>String, :required=>true}, :description=>"Xish stuff", :something=>4, :_id=>0, :_group_id=>0}
# Flatten index is automatically set to the first field ever given
# but you can change it to any field you want.
hash_model = HashModel.new(:raw_data=>records)
puts hash_model.flatten_index
>> :switch
# you can use flattened field names
hash_model.flatten_index = :parameter__type
puts hash_model.flatten_index
>> :parameter__type
puts hash_model
>> {:parameter__type=>String, :switch=>["-x", "--xtended"], :parameter__require=>true, :description=>"Xish stuff", :_id=>0, :_group_id=>0}
>> {:parameter__type=>nil, :switch=>["-y", "--why"], :description=>"lucky what?", :_id=>1, :_group_id=>1}
>> {:parameter__type=>String, :switch=>"-z", :description=>"zee svitch zu moost calz", :_id=>2, :_group_id=>2}
# Notice that records that don't have the flatten index field have that field added and the value is set to nil
# You can search using just a value and it will search based on the flatten_index
records = [
{:switch => ["-x", "--xtended"], :parameter => {:type => String, :required => true}, :description => "Xish stuff", :something => 4},
{:switch => ["-y", "--why"], :description => "lucky what?", :something => 7},
{:switch => "-z", :parameter => {:type => String, :required => true}, :description => "zee svitch zu moost calz", :something => 4},
]
hash_model = HashModel.new(:raw_data=>records)
where = hash_model.where("-x")
puts where
>> {:switch=>"-x", :parameter=>{:type=>String, :required=>true}, :description=>"Xish stuff", :something=>4, :_id=>0, :_group_id=>0}
# But the real power is the ability to search with complex boolean logic using normal and flattend field names.
# Note that flattened field names are seperated with double under lines __
hash_model = HashModel.new(:raw_data=>records)
where = hash_model.where {:something == 7 || (:parameter__type == String && :parameter__required == true)}
puts where
>> {:switch=>"-x", :parameter=>{:type=>String, :required=>true}, :description=>"Xish stuff", :something=>4, :_id=>0, :_group_id=>0}
>> {:switch=>"--xtended", :parameter=>{:type=>String, :required=>true}, :description=>"Xish stuff", :something=>4, :_id=>1, :_group_id=>0}
>> {:switch=>"-z", :parameter=>{:type=>String, :required=>true}, :description=>"zee svitch zu moost calz", :something=>4, :_id=>4, :_group_id=>2}
# You can even search using hash values
hash_model = HashModel.new(:raw_data=>records)
where = hash_model.where {:parameter == {:type => String, :required => true}}
puts where
>> {:switch=>"-x", :parameter=>{:type=>String, :required=>true}, :description=>"Xish stuff", :something=>4, :_id=>0, :_group_id=>0},
>> {:switch=>"--xtended", :parameter=>{:type=>String, :required=>true}, :description=>"Xish stuff", :something=>4, :_id=>1, :_group_id=>0}
# Since the HashModel class flattens records it is sometimes useful to know what records were created from the same raw data record.
# This works exactly like a #where search so you can send just a value or send a block and get all of the sibling records for your search criteria.
records = [
{:switch => ["-x", "--xtended"], :parameter => {:type => String, :required => true}, :description => "Xish stuff", :something => 4},
{:switch => ["-y", "--why"], :description => "lucky what?", :something => 7},
{:switch => "-z", :parameter => {:type => Integer, :required => true}, :description => "zee svitch zu moost calz", :something => 4},
]
hash_model = HashModel.new(:raw_data=>records)
group = hash_model.group {(:parameter__type == String && :parameter__required == true && :something == 4) || :something == 7}
puts group
>> {:switch=>"-x", :parameter=>{:type=>String, :required=>true}, :description=>"Xish stuff", :something=>4, :_id=>0, :_group_id=>0}
>> {:switch=>"--xtended", :parameter=>{:type=>String, :required=>true}, :description=>"Xish stuff", :something=>4, :_id=>1, :_group_id=>0}
>> {:switch=>"-y", :description=>"lucky what?", :something=>7, :_id=>2, :_group_id=>1}
>> {:switch=>"--why", :description=>"lucky what?", :something=>7, :_id=>3, :_group_id=>1}
# You can also add flat records in the same way you add raw records.
hash_model = HashModel.new
hash_model << {:switch=>"-x", :parameter__type=>String, :parameter__require=>true, :description=>"Xish stuff"}
puts hash_model.raw_data
>> {:switch => "-x", :parameter => {:type => String, :required => true}, :description => "Xish stuff"}
# You can also call the unflatten method on an instance or the class itself and send it a record. (It won't mess with the existing data.)
deep_hash = {
:parameter__type=>String,
:switch__deep1__deep3 => "deepTwo",
:parameter__type__ruby=>true,
:parameter => "glorp",
:parameter__require=>true,
:switch__deep2 => "deepTwo",
:description=>"Xish stuff",
:switch => "--xtend",
}
unflat = HashModel.unflatten(deep_hash)
puts unflat
>> {:parameter=>[{:type=>String}, "glorp", {:required=>true}], :switch=>[{:deep1=>{:deep3=>"deepTwo"}}, {:deep2=>"deepTwo"}, "--xtend"], :description=>"Xish stuff"}
0.4.0 - 2011.03-23
Lots of updates and a major bug fix for this release.
After using it for a little while I've broken down and added the write functionality I was avoiding previously.
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:
x = "-x"
hm.where{:x == x}
It works properly now.
Additions/Changes
update
and update!
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.
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.
For instance:
# Default index search
my_hash_model.update("-x", :parameter__type=>Fixnum)
# Boolean search
my_hash_model.update(:parameter__type=>Fixnum) {:switch == "-x"}
You can update more than one field at a time as well. When you do this make sure you wrap them in hash markers {}:
my_hash_model.update("-x", {:field_1=>"new value", :field_2=>"another new value"})
It's important to note that searches in an update
will search the entire recordset. i.e. they will ignore the current filter. They will reset that filter when they are done so if you change the value of the primary index then you'll have an empty recordset. I realize it would be better to make the filters additive, and I will, but that will have to wait for the next update.
You don't have to put in any search criteria at all though, you can just put in the field you want updated and it will update all records that are in the current filter set.
my_hash_model.update(:field_1=>"new value")
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.
update_and_add
and update_and_add!
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.
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"}
.
delete
and delete!
These use the standard where
type search used everywhere else. You guessed it, the delete!
method deletes in place while the delete
method returns the records that would be deleted.
This function removes data from the raw data so if you have other flattened records that are based on that raw data they will no longer exist since the data that generated them is gone.
Just like an array these methods return the records they deleted.
parent
Again this uses a where
search and returns all raw parent records for flattened records matching the search criteria. (This method was there before but hadn't actually be coded, it was just a copy of some code I started but shouldn't have been in a release version).
filter
Filter has been changed from a property to a method. It is exactly like a where
search but it's in-place and non-destructive. Since where!
was changed to be truly destructive this change was needed. It also makes all the search functions identical in their usage.
where!
Changed to be truly destructive. If you run a where! on the class you're losing records that don't match it. The non-destructive version where
is changed in that it does not contain the raw data of any records that don't match the where
clause but the original HashModel remains untouched.
group!
Since bangs (!) are all now destructive, to bring the class inline with Ruby standards, it doesn't make logical sense to have a a group method that deletes all data except the search data then tries to find siblings; they would have been deleted with the destructive call. Instead just use group
and it will return the sibling records without touching the data in the HashModel.
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.
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.
Cleaned up RSpecs a little along the lines of reorganization.
0.3.1 - 2011.03.18
0.3.0 - 2011.01.13
0.2.0 - 2011.01.08
0.1.1 - 2010.12.15
0.1.0 - 2010.12.15
update
and where
methods; any time a filter is given.Copyrighted free software - Copyright (c) 2011 Mike Bethany. See LICENSE.txt for further details.
FAQs
Unknown package
We found that hashmodel demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Socket's package search now displays weekly downloads for npm packages, helping developers quickly assess popularity and make more informed decisions.
Security News
A Stanford study reveals 9.5% of engineers contribute almost nothing, costing tech $90B annually, with remote work fueling the rise of "ghost engineers."
Research
Security News
Socket’s threat research team has detected six malicious npm packages typosquatting popular libraries to insert SSH backdoors.