skn_utils 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: a4bd06660f0cfd6c83198afffde3139c97b65188
4
+ data.tar.gz: 483673c80d6f9e3c0217dd74b21b7b05b69bc1e8
5
+ SHA512:
6
+ metadata.gz: a11b5159b5108a8c2aa6174912023f17d9bca2f085f703ba26805589186af0ec8bdf705e45f1ad4416f8c11eddc9fc1590fa27dd027e642cc7ed9b45797f4711
7
+ data.tar.gz: c5de9340f8529b1a83c05dfcc9848dc8f2deb29a21c81edf9cabea72a0c71f810ceac7d44f39c9b7be2731eaadc136682cba8994e528267444b8b875b398f953
data/.gitignore ADDED
@@ -0,0 +1,15 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
15
+ /vendor/
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec_helper
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in skn_utils.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 James Scott, Jr
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2015 James Scott Jr
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,221 @@
1
+ # SknUtils
2
+ Rails 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.
3
+
4
+
5
+ The intent of this component is to be a container of data results, with easy access to its contents with on-demand transformation to hash, xml, or json.
6
+
7
+ * If the key's value is also a hash, it too can optionally become an Object.
8
+ * if the key's value is a Array of Hashes, each element of the Array can optionally become an Object.
9
+
10
+ This nesting action is controlled by the value of the options key ':depth'.
11
+ The key :depth defaults to :multi, an has options of :single, :multi, or :multi_with_arrays
12
+
13
+ The ability of the resulting Object to be Marshalled(dump/load) can be preserved by merging configuration options
14
+ into the input params key ':enable_serialization' set to true. It defaults to false for speed purposes
15
+
16
+
17
+ ### Configuration Options
18
+ --------------------------------
19
+
20
+ :enable_serialization = false -- [ true | false ], for speed, omits creation of attr_accessor
21
+ :depth = :multi -- [ :single | :multi | :multi_with_arrays ]
22
+
23
+ ### Public Components
24
+ --------------------------------
25
+
26
+ Inherit from NestedResultBase or instantiate an pre-built Class:
27
+ SknUtils::GenericBean # => Serializable, includes attr_accessors, and follows hash values only.
28
+ SknUtils::PageControls # => Serializable, includes attr_accessors, and follows hash values and arrays of hashes.
29
+ SknUtils::ResultBean # => Not Serializable, includes attr_accessors, and follows hash values only.
30
+ SknUtils::ResultsBeanWithErrors # => Same as ResultBean with addition of ActiveModel::Errors object.
31
+ or Include AttributeHelpers # => Add getter/setters, and hash notation access to instance vars of any object.
32
+
33
+
34
+ ## Basic features include:
35
+ ```ruby
36
+ - provides the hash or dot notation methods of accessing values from object created; i.e
37
+ 'obj = ResultBean.new({value1: "some value", value2: {one: 1, two: "two"}})
38
+ 'x = obj.value1' or 'x = obj.value2.one'
39
+ 'x = obj["value1"]'
40
+ 'x = obj[:value1]'
41
+
42
+ - enables serialization by avoiding the use of ''singleton_class'' methods which breaks Serializers:
43
+ Serializer supports xml, json, hash, and standard Marshall''ing
44
+
45
+ person = PageControls.new({name: "Bob"})
46
+ person.attributes # => {"name"=>"Bob"}
47
+ person.serializable_hash # => {"name"=>"Bob"}
48
+ person.to_hash # => {"name"=>"Bob"}
49
+ person.to_json # => "{\"name\":\"Bob\"}"
50
+ person.to_xml # => "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<page-controls>\n <name>Bob</name>\n</page-controls>\n"
51
+ dmp = Marshal.dump(person) # => "\x04\bo:\x1ASknUtils::PageControls\x06:\n@nameI\"\bBob\x06:\x06ET"
52
+ person = Marshal.load(dmp) # => #<SknUtils::PageControls:0x007faede906d40 @name="Bob">
53
+
54
+ ***GenericBean designed to automatically handle the setup for serialization and multi level without arrays
55
+
56
+ - post create additions:
57
+ 'obj = ResultBean.new({value1: "some value", value2: {one: 1, two: "two"}})
58
+ 'x = obj.one' --causes NoMethodError
59
+ 'x = obj.one = 'some other value' --creates a new instance value with accessors
60
+ 'x = obj.one = {key1: 1, two: "two"}' --creates a new ***bean as the value of obj.one
61
+ 'y = obj.one.two' --returns "two"
62
+ 'y = obj.one[:two] --returns "two"
63
+ 'y = obj.one['two'] --returns "two"
64
+
65
+ - supports predicates <attr>? and clear_<attr>? method patterns:
66
+ 'obj = PageControls.new({name: "Something", phone: "2609998888"})'
67
+ 'obj.name?' # => true true or false, like obj.name.present?
68
+ 'obj.clear_name' # => nil sets :name to nil
69
+ ```
70
+
71
+ The combination of this NestedResultBase(dot notation class) and AttributeHelpers(hash notation module), produces this effect given the same params hash:
72
+
73
+ {:depth => <select>, ...} Input Hash Basic dot notation: effect of :depth
74
+ ---------------------------------------------------- ---------------------------------
75
+
76
+ (DOES NOT FOLLOW Values) :depth => :single
77
+ ```ruby
78
+ * params = {one: 1, drb.one = 1
79
+ two: { one: 1, drb.two = {one: 1, two: 'two}
80
+ two: "two" drb.two.two = NoMethodError
81
+ },
82
+ three: [ {one: 'one', two: 2}, drb.three = [{one: 'one', two: 2},{three: 'three', four: 4}]
83
+ {three: 'three', four: 4} drb.three[1] = {three: 'three', four: 4}
84
+ ] drb.three[1].four = NoMethodError
85
+ }
86
+ ```
87
+
88
+ (Follow VALUES that are Hashes only.) :depth => :multi
89
+ ```ruby
90
+ * params = {one: 1, drb.one = 1
91
+ two: { one: 1, drb.two.one = 1
92
+ two: "two" drb.two.two = 'two'
93
+ },
94
+ three: [ {one: 'one', two: 2}, drb.three = [{one: 'one', two: 2},{three: 'three', four: 4}]
95
+ {three: 'three', four: 4} drb.three[1] = {three: 'three', four: 4}
96
+ ] drb.three[1].four = NoMethodError
97
+ }
98
+ ```
99
+
100
+ (Follow VALUES that are Hashes and/or Arrays of Hashes) :depth => :multi_with_arrays
101
+ ```ruby
102
+ * params = {one: 1, drb.one = 1
103
+ two: { one: 1, drb.two.one = 1
104
+ two: "two" drb.two.two = 'two'
105
+ },
106
+ three: [ {one: 'one', two: 2}, drb.three.first.one = 'one'
107
+ {three: 'three', four: 4} drb.three[1].four = 4
108
+ ]
109
+ }
110
+
111
+ ```
112
+ # Usage Examples: SubClassing
113
+
114
+ (DOES NOT FOLLOW Values)
115
+ ```ruby
116
+ class SmallPackage < NestedResultBase
117
+ def initialize(params={})
118
+ super( params.merge({depth: :single}) ) # override default of :multi level
119
+ end
120
+ end
121
+ ```
122
+
123
+ (Follow VALUES that are Hashes only.)
124
+ ```ruby
125
+ class ResultBean < NestedResultBase
126
+ # defaults to :multi level
127
+ end
128
+
129
+ -- or --
130
+
131
+ class ResultBean < NestedResultBase
132
+ def initialize(params={})
133
+ # your other init stuff here
134
+ super(params) # default taken
135
+ end
136
+ end
137
+
138
+ -- or --
139
+
140
+ class ResultBean < NestedResultBase
141
+ def initialize(params={})
142
+ # your other init stuff here
143
+ super( params.merge({depth: :multi}) ) # Specified
144
+ end
145
+ end
146
+
147
+ ** - or -- enable serialization and default to multi
148
+ class GenericBean < NestedResultBase
149
+ def initialize(params={})
150
+ super( params.merge({enable_serialization: true}) ) # Specified with Serialization Enabled
151
+ end
152
+ end
153
+ ```
154
+
155
+ (Follow VALUES that are Hashes and/or Arrays of Hashes, and enable Serializers)
156
+ ```ruby
157
+ class PageControl < NestedResultBase
158
+ def initialize(params={})
159
+ super( params.merge({depth: :multi_with_arrays, enable_serialization: true}) ) # override defaults
160
+ end
161
+ end
162
+ ```
163
+
164
+
165
+ NOTE: Cannot be Marshalled/Serialized unless input params.merge({enable_serialization: true}) -- default is false
166
+ Use GenericBean or PageControls if serialization is needed, it initializes with this value true.
167
+
168
+ ## Installation
169
+ ----------------
170
+ runtime prereqs: gem 'active_model', '~> 3.0'
171
+
172
+ Add this line to your application's Gemfile:
173
+
174
+ ```ruby
175
+ gem 'skn_utils'
176
+ ```
177
+
178
+ And then execute:
179
+
180
+ $ bundle
181
+
182
+ Or install it yourself as:
183
+
184
+ $ gem install skn_utils
185
+
186
+ ## Build (If not found in RubyGems Yet...)
187
+
188
+ 1. $ git clone git@github.com:skoona/skn_utils.git
189
+ 2. $ cd skn_utils
190
+ 3. $ gem install bundler
191
+ 4. $ bundle install
192
+ 5. $ bundle exec rspec
193
+ 6. $ gem build skn_utils.gemspec
194
+ 7. $ gem install skn_utils
195
+ * Done
196
+
197
+ ## Console Workout
198
+
199
+ Start with building gem first.
200
+ ```bash
201
+ $ cd skn_utils
202
+ $ bundle exec pry
203
+ [1] pry(main)> require 'active_model'
204
+ [2] pry(main)> require 'skn_utils'
205
+ [3] pry(main)> rb = SknUtils::ResultBean.new({sample: [{one: "one", two: "two"},{one: 1, two: 2}] })
206
+ [4] pry(main)> pg = SknUtils::PageControls.new({sample: [{one: "one", two: "two"},{one: 1, two: 2}] })
207
+ [5] pry(main)> pg.sample.first.one # Tip :multi_with_arrays
208
+ [6] pry(main)> rb.sample.first.one # Tip :multi without arrays you will get a NoMethodError
209
+ [7] pry(main)> rb.sample.first[:one]
210
+
211
+ [n] pry(main)> exit
212
+ * Done
213
+ ```
214
+
215
+ ## Contributing
216
+
217
+ 1. Fork it
218
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
219
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
220
+ 4. Push to the branch (`git push origin my-new-feature`)
221
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
data/lib/skn_utils.rb ADDED
@@ -0,0 +1,11 @@
1
+ require "skn_utils/version"
2
+ require 'skn_utils/attribute_helpers'
3
+ require 'skn_utils/nested_result_base'
4
+ require 'skn_utils/generic_bean'
5
+ require 'skn_utils/page_controls'
6
+ require 'skn_utils/result_bean'
7
+ require 'skn_utils/result_bean_with_errors'
8
+
9
+ module SknUtils
10
+
11
+ end
@@ -0,0 +1,159 @@
1
+ ##
2
+ # <Rails.root>/lib/skn_utils/attribute_helpers.rb
3
+ #
4
+ # *** See SknUtils::NestedResultBase for details ***
5
+ #
6
+ ##
7
+ # This module provides
8
+ #
9
+ # to_hash Serializer:
10
+ # person.to_hash
11
+ # => {"name"=>"Bob"}
12
+ #
13
+ # Support <attr>? and clear_<attr>? method patterns
14
+ # example:
15
+ # person.name?
16
+ # => true true or false, like obj.name.present?
17
+ # person.clear_name
18
+ # => nil sets :name to nil
19
+ #
20
+ # attr_accessor like feature for all instance variables
21
+ # person.name
22
+ # => "Bob"
23
+ # person.name = "James"
24
+ # => "James"
25
+ ##
26
+
27
+
28
+
29
+ module SknUtils
30
+ module AttributeHelpers
31
+
32
+ # return a hash of all attributes and their current values
33
+ # including nested arrays of hashes/objects
34
+ def attributes
35
+ instance_variable_names.each_with_object({}) do |attr,collector|
36
+ next if ['skn_enable_serialization', 'skn_enabled_depth'].include?(attr.to_s[1..-1]) # skip control keys
37
+ value = instance_variable_get(attr)
38
+ next if value.is_a?(ActiveModel::Errors)
39
+
40
+ if value.kind_of?(Array) and value.first.respond_to?(:attribute_helper_object)
41
+ value = value.map {|ov| ov.respond_to?(:attribute_helper_object) ? ov.attributes : ov }
42
+ elsif value.respond_to?(:attribute_helper_object)
43
+ value = value.attributes
44
+ end
45
+ collector[attr.to_s[1..-1].to_sym] = value
46
+ end
47
+ end
48
+
49
+ def to_hash
50
+ attributes
51
+ end
52
+
53
+ # An alternative mechanism for property access.
54
+ # This let's you do foo['bar'] along with foo.bar.
55
+ def [](attr)
56
+ send("#{attr}")
57
+ end
58
+
59
+ def []=(attr, value)
60
+ send("#{attr}=", value)
61
+ end
62
+
63
+ # determines if this is one of our objects
64
+ def attribute_helper_object
65
+ true
66
+ end
67
+
68
+ ##
69
+ # DO NOT ADD METHODS BELOW THIS LINE, unless you want them to be private
70
+ ##
71
+ private
72
+
73
+ def attribute?(attr)
74
+ if attr.is_a? Symbol
75
+ send(attr).present?
76
+ else
77
+ send(attr.to_sym).present?
78
+ end
79
+ end
80
+
81
+ def clear_attribute(attr)
82
+ if attr.is_a? Symbol
83
+ instance_variable_set("@#{attr.to_s}", nil)
84
+ else
85
+ instance_variable_set("@#{attr}", nil)
86
+ end
87
+ end
88
+
89
+ # Determines operable Options in effect for this instance
90
+ # see NestedResultBase
91
+ def serial_required?
92
+ respond_to? :serialization_required? and serialization_required?
93
+ end
94
+ # see NestedResultBase
95
+ def multi_required?
96
+ respond_to? :depth_level and depth_level != :single
97
+ end
98
+ # see NestedResultBase
99
+ def multi_with_arrays_required?
100
+ respond_to? :depth_level and depth_level == :multi_with_arrays
101
+ end
102
+
103
+ ##
104
+ # Adds the attr?() method pattern. all attributes will respond to attr?: example - obj.name? with true or false
105
+ # Adds the clear_attr() method pattern. all attributes will respond to clear_attr(): example - obj.clear_name sets :name to nil
106
+ # Handles getter for any instance_variable currently defined
107
+ # Handles setter for any instance_variable currently defined
108
+ # Sets new instance_variable for any undefined variable with non-hash value
109
+ # Sets instance_variable value to Bean object for any undefined variable with hash value param
110
+ #
111
+ # Using any form of singleton_class() will break the generic bean, which requires Serialization.
112
+ # However not adding attr_accessors may impact performance, as method_missing must fill-in for read/writes
113
+ ##
114
+ def method_missing(method, *args, &block)
115
+ # puts("method_missing/method/class/*args=#{method}/#{method.class.name}/#{args}")
116
+ if method.to_s.start_with?('clear_') and instance_variable_defined?("@#{method.to_s[6..-1]}")
117
+ clear_attribute(method.to_s[6..-1].to_sym)
118
+ elsif method.to_s.end_with?('?')
119
+ if instance_variable_defined?("@#{method.to_s[0..-2]}")
120
+ attribute?(method.to_s[0..-2].to_sym)
121
+ else
122
+ false
123
+ end
124
+ elsif method.to_s.end_with?("=") # add new attribute or whole object
125
+ if args.first.is_a?(Hash)
126
+ singleton_class.send(:attr_accessor, method.to_s[0..-2]) unless serial_required?
127
+ if multi_required?
128
+ instance_variable_set "@#{method.to_s[0..-2]}", self.class.new(*args)
129
+ else
130
+ instance_variable_set "@#{method.to_s[0..-2]}", *args
131
+ end
132
+ elsif args.first.is_a?(Array) and args.flatten.first.kind_of?(Hash)
133
+ singleton_class.send(:attr_accessor, method.to_s[0..-2]) unless serial_required?
134
+ if multi_with_arrays_required?
135
+ instance_variable_set("@#{method.to_s[0..-2]}",
136
+ (args.flatten.map {|nobj| nobj.kind_of?(Hash) ? self.class.new(nobj) : nobj })
137
+ )
138
+ else
139
+ instance_variable_set "@#{method.to_s[0..-2]}", *args
140
+ end
141
+ elsif !args.empty?
142
+ singleton_class.send(:attr_accessor, method.to_s[0..-2]) unless serial_required?
143
+ instance_variable_set "@#{method.to_s[0..-2]}", *args
144
+ else
145
+ super(method, *args, &block) # throw excpt for not found or could return false
146
+ end
147
+ elsif instance_variable_defined? "@#{method.to_s}"
148
+ instance_variable_get "@#{method.to_s}"
149
+ else
150
+ super(method, *args, &block)
151
+ end
152
+ rescue
153
+ # puts $!.message + $!.backtrace.join("\n")
154
+ super(method, *args, &block)
155
+ end
156
+ # end of private section
157
+
158
+ end # end module
159
+ end # end module