SknUtils
Ruby Gem containing a Ruby PORO (Plain Old Ruby Object) that can be instantiated at runtime with an input hash. This library creates an Object with instance variables and associated getters and setters for Dot or Hash notational access to each instance variable. Additional instance variables can be added post-create by 'obj.my_new_var = "some value"', or simply assigning it.
The intent of this gem is to be a container of data results or value bean, with easy access to its contents with on-demand transformation back to a hash (#to_hash) for easy serialization using standard ruby Hash serialization methods.
- Transforms the initialization hash into object instance variables, with their Keys as the method names.
- If the key's value is also a hash, it too can optionally become an Object.
- if the key's value is a Array of Hashes, each element of the Array can optionally become an Object.
This nesting action is controlled by the value of the options key ':depth'. The key :depth defaults to :multi, and has options of :single, :multi, or :multi_with_arrays
The ability of the resulting Object to be Marshalled(dump/load) can be preserved by merging configuration options into the input params key ':enable_serialization' set to true. It defaults to false for speed purposes
New Features
10/2016 V2.0.6
Added an SknUtils::NullObject and SknUtils::nullable?(value) extracted from [Avdi Grimm's Confident Code](https://gist.github.com/jschoolcraft/979827)
The NullObject class has great all around utility, check out it's specs!
08/2016 V2.0.3
Added an exploritory ActionService class and RSpec test, triggered by reading [Kamil Lelonek](https://blog.lelonek.me/what-service-objects-are-not-7abef8aa2f99#.p64vudxq4)
I don't support his approach, but the CreateTask class caught my attention as a Rubyist.
12/2015 V2.0
All references to ActiveRecord or Rails has been removed to allow use in non-Rails environments
as a result serialization is done with standard Ruby Hash serialization methods; by first transforming
object back to a hash using its #to_hash method.
06/2015 V1.5.1 commit #67ef656
Last Version to depend on Rails (ActiveModel) for #to_json and #to_xml serialization
Configuration Options
Include in initialization hash
:enable_serialization = false -- [ true | false ], for speed, omits creation of attr_accessor
:depth = :multi -- [ :single | :multi | :multi_with_arrays ]
Public Methods
Each concrete Class supports the following utility methods:
#to_hash -- returns a hash of all user attributes
#to_hash(true) -- returns a hash of all user and internal attributes -- use for serialization
#[] -- returns value of attr, when #[<attr_name_symbol>]
#[]=(attr, value) -- assigns value to existing attr, or creates a new key/value pair
#<attr>? -- detects true/false presence? of attr, and non-blank existance of attr's value; when #address?
#<attr> -- returns value of named attribute
#<attr> = (value) -- assigns value to existing attr, or creates a new key/value pair
-- Where <attr> is a key value from the initial hash, or a key that was/will be dynamically added
#depth_level -- returns parsing depth level, see :depth
#serialization_required? -- returns true/false if serialization is enabled
#clear_<attr> -- assigns nil to existing attr, when #clear_attr
Public Components
Inherit from NestedResultBase or instantiate an pre-built Class:
SknUtils::ResultBean # => Not Serializable and follows hash values only.
SknUtils::PageControls # => Serializable and follows hash values and arrays of hashes.
SknUtils::GenericBean # => Serializable and follows hash values only.
SknUtils::ValueBean # => Serializable and DOES NOT follows hash values.
or Include AttributeHelpers # => Add getter/setters, and hash notation access to instance vars of any object.
Basic features include:
- provides the hash or dot notation methods of accessing values from object created; i.e
'obj = SknUtils::ResultBean.new({value1: "some value", value2: {one: 1, two: "two"}})
'x = obj.value1' or 'x = obj.value2.one'
'x = obj["value1"]'
'x = obj[:value1]'
- enables serialization by avoiding the use of ''singleton_class'' methods, #attr_accessor, which breaks Serializers:
Serializer supports #to_hash, and standard Marshall''ing. Notice use of #to_hash to convert object back to a Ruby Hash before
using #to_json and #to_xml; presumed to be methods enabled on the standard Ruby Hash class.
person = SknUtils::PageControls.new({name: "Bob"})
person.to_hash # => {"name"=>"Bob"}
person.to_hash.to_json # => "{\"name\":\"Bob\"}"
person.to_hash.to_xml # => "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<page-controls>\n <name>Bob</name>\n</page-controls>\n"
dmp = Marshal.dump(person) # => "\x04\bo:\x1ASknUtils::PageControls\x06:\n@nameI\"\bBob\x06:\x06ET"
person = Marshal.load(dmp) # => #<SknUtils::PageControls:0x007faede906d40 @name="Bob">
***GenericBean designed to automatically handles the setup for serialization and multi level without arrays
- post create additions:
'obj = SknUtils::ResultBean.new({value1: "some value", value2: {one: 1, two: "two"}})
'x = obj.one' --causes NoMethodError
'x = obj.one = 'some other value' --creates a new instance value with accessors
'x = obj.one = {key1: 1, two: "two"}' --creates a new ***bean as the value of obj.one
'y = obj.one.two' --returns "two"
'y = obj.one[:two] --returns "two"
'y = obj.one['two'] --returns "two"
- supports predicates <attr>? and clear_<attr>? method patterns:
'obj = SknUtils::PageControls.new({name: "Something", phone: "2609998888"})'
'obj.name?' # => true true or false, like obj.name.present?
'obj.clear_name' # => nil sets :name to nil
The combination of this NestedResultBase(dot notation class) and AttributeHelpers(hash notation module), produces these effects given the same params hash:
drb = SknUtils::ResultBean.new(params) Basic dot notation: effect of :depth
---------------------------------------------------- -----------------------------------------------------------------
(DOES NOT FOLLOW Values) :depth => :single
* params = {one: 1, drb.one = 1
two: { one: 1, drb.two = {one: 1, two: 'two}
two: "two" drb.two.two = NoMethodError
},
three: [ {one: 'one', two: 2}, drb.three = [{one: 'one', two: 2},{three: 'three', four: 4}]
{three: 'three', four: 4} drb.three[1] = {three: 'three', four: 4}
] drb.three[1].four = NoMethodError
}
(Follow VALUES that are Hashes only.) :depth => :multi
* params = {one: 1, drb.one = 1
two: { one: 1, drb.two = <SknUtils::ResultBean>
two: "two" drb.two.two = 'two'
},
three: [ {one: 'one', two: 2}, drb.three = [{one: 'one', two: 2},{three: 'three', four: 4}]
{three: 'three', four: 4} drb.three[1] = {three: 'three', four: 4}
] drb.three[1].four = NoMethodError
}
(Follow VALUES that are Hashes and/or Arrays of Hashes) :depth => :multi_with_arrays
* params = {one: 1, drb.one = 1
two: { one: 1, drb.two = <SknUtils::ResultBean>
two: "two" drb.two.two = 'two'
},
three: [ {one: 'one', two: 2}, drb.three = [<SknUtils::ResultBean>,<SknUtils::ResultBean>]
{three: 'three', four: 4} drb.three[1] = <SknUtils::ResultBean>
] drb.three[1].four = 4
}
Usage:
(DOES NOT FOLLOW Values)
class SmallPackage < SknUtils::NestedResultBase
def initialize(params={})
super( params.merge({depth: :single}) ) # override default of :multi level
end
end
(Follow VALUES that are Hashes only.)
class MyPackage < SknUtils::NestedResultBase
# defaults to :multi level
end
-- or --
class MyPackage < SknUtils::NestedResultBase
def initialize(params={})
# your other init stuff here
super(params) # default taken
end
end
-- or --
class MyPackage < SknUtils::NestedResultBase
def initialize(params={})
# your other init stuff here
super( params.merge({depth: :multi}) ) # Specified
end
end
** - or -- enable serialization and default to multi
class MyPackage < SknUtils::NestedResultBase
def initialize(params={})
super( params.merge({enable_serialization: true}) ) # Specified with Serialization Enabled
end
end
(Follow VALUES that are Hashes and/or Arrays of Hashes, and enable Serializers)
class MyPackage < SknUtils::NestedResultBase
def initialize(params={})
super( params.merge({depth: :multi_with_arrays, enable_serialization: true}) ) # override defaults
end
end
NOTE: Cannot be Marshalled/Serialized unless input params.merge(true) -- default is false Use GenericBean or PageControls if serialization is needed, they initialize with this value true.
Installation
runtime prereqs: V2+ None V1+ gem 'active_model', '~> 3.0'
Add this line to your application's Gemfile:
gem 'skn_utils'
And then execute:
$ bundle
Or install it yourself as:
$ gem install skn_utils
Build
- $ git clone [email protected]:skoona/skn_utils.git
- $ cd skn_utils
- $ gem install bundler
- $ bundle install
- $ bundle exec rspec
- $ gem build skn_utils.gemspec
- $ gem install skn_utils
- Done
Console Workout
Start with building gem first.
$ cd skn_utils
$ bundle exec pry
[1] pry(main)> require 'skn_utils'
[2] pry(main)> rb = SknUtils::ResultBean.new({sample: [{one: "one", two: "two"},{one: 1, two: 2}] })
[3] pry(main)> pg = SknUtils::PageControls.new({sample: [{one: "one", two: "two"},{one: 1, two: 2}] })
[4] pry(main)> pg.sample.first.one # Tip :multi_with_arrays
[5] pry(main)> rb.sample.first.one # Tip :multi without arrays you will get a NoMethodError
[6] pry(main)> rb.sample.first[:one]
[n] pry(main)> exit
* Done
Contributing
- Fork it
- Create your feature branch (
git checkout -b my-new-feature) - Commit your changes (
git commit -am 'Add some feature') - Push to the branch (
git push origin my-new-feature) - Create a new Pull Request
License
The gem is available as open source under the terms of the MIT License.