sd_struct 0.0.3 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7b543501f8aa5ae85ede78790fe9322e02329912
4
- data.tar.gz: 307b9ffef98af9acd6bfb3220ecefac8d1b593b3
3
+ metadata.gz: db8247a8652fb98a73e935ce9c0737d3bcb60aeb
4
+ data.tar.gz: e681ebbbe672b041dbe2d84e7e819123c7bfd966
5
5
  SHA512:
6
- metadata.gz: 5677a893ed42b6c60dadd6d411931174a7b39ff6303452a3418961bc304cc9948ad01f60d886eaad2109162c1952e76f166e69a484fdd71d67e39339348aef61
7
- data.tar.gz: cc7349a61ce9c5af6c9038a930e0e1fc13ea30091c2a546a02848b16c2461fdd9641413dfcae883d51fc88d31e601ff548a23d9234e879bf2b020178db05b877
6
+ metadata.gz: a46da494946f552eca4dc1912c1d6f96667f933539b9fe4f66d37f530600f52e6e508c7a9c26a4d541f71c433af85dd890008bef1b231af98fe8d30878027b03
7
+ data.tar.gz: 3987632b1425ef16c1535a740ac68183170a644b9b1122e14ac3e2eab9c9d24c6745c357e957e5fe2266ef452f9a8a172e7bd097afeb9f8c5b2ab96b7c942837
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format documentation
data/CHANGELOG.md ADDED
@@ -0,0 +1,13 @@
1
+ 0.1.0 / 2017-04-19
2
+ ==================
3
+ * Added warning when changing value to different type and
4
+ * Added conversion of value assigned to struct if it's a Hash or an Array and deep option is set to true
5
+ * Improved `find` to be more like xpath with its root and non-root search
6
+ * Upgraded Bundler
7
+ * Prepared to use Rspec (no specs yet)
8
+ * Added changelog
9
+
10
+ 0.0.3 / 2017-04-09
11
+ ==================
12
+
13
+ * Initial working version
@@ -0,0 +1,74 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ In the interest of fostering an open and welcoming environment, we as
6
+ contributors and maintainers pledge to making participation in our project and
7
+ our community a harassment-free experience for everyone, regardless of age, body
8
+ size, disability, ethnicity, gender identity and expression, level of experience,
9
+ nationality, personal appearance, race, religion, or sexual identity and
10
+ orientation.
11
+
12
+ ## Our Standards
13
+
14
+ Examples of behavior that contributes to creating a positive environment
15
+ include:
16
+
17
+ * Using welcoming and inclusive language
18
+ * Being respectful of differing viewpoints and experiences
19
+ * Gracefully accepting constructive criticism
20
+ * Focusing on what is best for the community
21
+ * Showing empathy towards other community members
22
+
23
+ Examples of unacceptable behavior by participants include:
24
+
25
+ * The use of sexualized language or imagery and unwelcome sexual attention or
26
+ advances
27
+ * Trolling, insulting/derogatory comments, and personal or political attacks
28
+ * Public or private harassment
29
+ * Publishing others' private information, such as a physical or electronic
30
+ address, without explicit permission
31
+ * Other conduct which could reasonably be considered inappropriate in a
32
+ professional setting
33
+
34
+ ## Our Responsibilities
35
+
36
+ Project maintainers are responsible for clarifying the standards of acceptable
37
+ behavior and are expected to take appropriate and fair corrective action in
38
+ response to any instances of unacceptable behavior.
39
+
40
+ Project maintainers have the right and responsibility to remove, edit, or
41
+ reject comments, commits, code, wiki edits, issues, and other contributions
42
+ that are not aligned to this Code of Conduct, or to ban temporarily or
43
+ permanently any contributor for other behaviors that they deem inappropriate,
44
+ threatening, offensive, or harmful.
45
+
46
+ ## Scope
47
+
48
+ This Code of Conduct applies both within project spaces and in public spaces
49
+ when an individual is representing the project or its community. Examples of
50
+ representing a project or community include using an official project e-mail
51
+ address, posting via an official social media account, or acting as an appointed
52
+ representative at an online or offline event. Representation of a project may be
53
+ further defined and clarified by project maintainers.
54
+
55
+ ## Enforcement
56
+
57
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
58
+ reported by contacting the project team at adrian.setyadi@reebonz.com. All
59
+ complaints will be reviewed and investigated and will result in a response that
60
+ is deemed necessary and appropriate to the circumstances. The project team is
61
+ obligated to maintain confidentiality with regard to the reporter of an incident.
62
+ Further details of specific enforcement policies may be posted separately.
63
+
64
+ Project maintainers who do not follow or enforce the Code of Conduct in good
65
+ faith may face temporary or permanent repercussions as determined by other
66
+ members of the project's leadership.
67
+
68
+ ## Attribution
69
+
70
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71
+ available at [http://contributor-covenant.org/version/1/4][version]
72
+
73
+ [homepage]: http://contributor-covenant.org
74
+ [version]: http://contributor-covenant.org/version/1/4/
data/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # SDStruct
2
2
 
3
- An alternative to OpenStruct that is stricter in assigning values and deeper in
4
- consuming the passed Hash and transforming it back to Hash or JSON, equipped
3
+ An alternative to OpenStruct that is more strict in assigning values and deeper
4
+ in consuming the passed Hash and transforming it back to Hash or JSON, equipped
5
5
  with deep digging capabilities.
6
6
 
7
7
  ## Usage
@@ -48,6 +48,11 @@ sd_struct = JSON.parse(response.body, object_class: SDStruct)
48
48
 
49
49
  sd_struct["two words"]
50
50
  # => "Foo bar"
51
+
52
+ # By the way, you can also use `send` method to access spaced key/field
53
+ o_struct.send("two words")
54
+ sd_struct.send("two words")
55
+ # => "Foo bar"
51
56
  ```
52
57
 
53
58
 
@@ -103,26 +108,26 @@ sd_struct
103
108
  sd_struct.find('object/a')
104
109
  # => "bau bau"
105
110
 
106
- sd_struct.find('array/0/one')
111
+ sd_struct.find('/array/0/one')
107
112
  # => 1
108
113
 
109
114
  sd_struct.find('object->a', separator: '->')
110
115
  # => "bau bau"
111
116
 
112
- sd_struct.find('array.0.one', separator: '.')
117
+ sd_struct.find('.array..one', separator: '.')
113
118
  # => 1
114
119
 
115
120
  # You can push it to find deeper. It will return the first occurrence of the matched field
116
- sd_struct.find('a')
121
+ sd_struct.find('//a')
117
122
  # => "bau bau"
118
123
 
119
- sd_struct.find('0/one')
124
+ sd_struct.find('//0/one')
120
125
  # => 1
121
126
 
122
- sd_struct.find('one')
127
+ sd_struct.find('//one')
123
128
  # => 1
124
129
 
125
- sd_struct.find('four')
130
+ sd_struct.find('//four')
126
131
  # => nil
127
132
 
128
133
  sd_struct.dig_deep(0, :one)
@@ -147,8 +152,8 @@ sd_struct
147
152
  # => #<SDStruct .object=#<SDStruct .a="bau bau", .c="boo boo">,
148
153
  # .array=[#<SDStruct .one=1, .two=2, .three=3>], ['two words']="Foo bar">
149
154
 
150
- sd_struct.find('0').one = 0
151
- sd_struct.find('0').three = 0
155
+ sd_struct.find('//0').one = 0
156
+ sd_struct.find('//0').three = 0
152
157
  sd_struct['two words'] = ""
153
158
  sd_struct
154
159
  # => #<SDStruct .object=#<SDStruct .a="bau bau", .c="boo boo">,
@@ -162,7 +167,7 @@ Therefore, keys with those values are excluded from the generated JSON string.
162
167
  sd_struct.to_json
163
168
  # => "{\"object\":{\"a\":\"bau bau\",\"c\":\"boo boo\"},\"array\":[{\"two\":2}]}"
164
169
 
165
- sd_struct.find('0').two = 0
170
+ sd_struct.find('///0').two = 0
166
171
  sd_struct.to_json
167
172
  # => "{\"object\":{\"a\":\"bau bau\",\"c\":\"boo boo\"}}"
168
173
  ```
@@ -215,8 +220,9 @@ sd_struct = SDStruct.new(hash)
215
220
 
216
221
  ## Reserved Field Names
217
222
 
218
- `dig`, `to_h`, `find`, `keys`, `marshal_dump`, `marshal_load`, `table`, `delete_field`,
219
- `fields`, `dig_deep`, `new_member`, `spaced_keys`, `non_spaced_keys`, `delete_key`
223
+ `delete_field`, `delete_key`, `dig`, `dig_deep`, `each_pair`, `fields`, `find`,
224
+ `keys`, `marshal_dump`, `marshal_load`, `new_struct_member`, `non_spaced_keys`,
225
+ `spaced_keys`, `structurize`, `table`, `to_h`, `to_json`
220
226
 
221
227
  ## Dependencies
222
228
 
@@ -238,6 +244,12 @@ Or install it yourself as:
238
244
 
239
245
  $ gem install activesupport
240
246
 
247
+ In your code:
248
+
249
+ ```ruby
250
+ require 'active_support/all'
251
+ ```
252
+
241
253
 
242
254
  ## Installation
243
255
 
@@ -255,6 +267,13 @@ Or install it yourself as:
255
267
 
256
268
  $ gem install sd_struct
257
269
 
270
+ If you're not using rails, you can choose to only require the minimal version
271
+ that doesn't have deep search and deep convert capabilities.
272
+
273
+ ```ruby
274
+ require 'sd_struct/base'
275
+ ```
276
+
258
277
  ## Contributing
259
278
 
260
279
  1. Fork it ( https://github.com/styd/sd_struct/fork )
data/Rakefile CHANGED
@@ -1,2 +1,2 @@
1
1
  require "bundler/gem_tasks"
2
-
2
+ task :default => :spec
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "sd_struct"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,225 @@
1
+ # Alternative to OpenStruct that is more strict and go deeper.
2
+ #
3
+ # @author Adrian Setyadi
4
+ #
5
+ class SDStruct
6
+ using Module.new {
7
+ refine Hash do
8
+ #
9
+ # Change current Hash object to SDStruct object
10
+ #
11
+ # @return [SDStruct] SDStruct object
12
+ #
13
+ def to_struct
14
+ SDStruct.new(to_h.dup)
15
+ end
16
+ end
17
+
18
+ refine Array do
19
+
20
+ #
21
+ # Call `to_struct` to an Array to go deeper or to a Hash to change it to SDStruct
22
+ #
23
+ # @return [Array<SDStruct,Object>] array of SDStruct or any other objects
24
+ #
25
+ def to_struct
26
+ map{|x| ( x.is_a?(Hash) || x.is_a?(Array) ) ? x.to_struct : x }
27
+ end
28
+ end
29
+ }
30
+
31
+ #
32
+ # Creates a new SDStruct object. By default, the resulting SDStruct object
33
+ # will have no attributes.
34
+ #
35
+ # The optional +hash+, if given, will generate attributes and values (can be
36
+ # a Hash, an SDStruct or a Struct).
37
+ # For example:
38
+ #
39
+ # require 'sd_struct' # or require 'sd_struct/base'
40
+ # hash = { "name" => "Matz", "coding language" => :ruby, :age => "old" }
41
+ # data = SDStruct.new(hash)
42
+ #
43
+ # p data # -> #<SDStruct .name="Matz", ['coding language']=:ruby, .age="old">
44
+ #
45
+ def initialize(hash = nil, deep = true)
46
+ @deep = deep
47
+ @table = {}
48
+ if hash
49
+ hash.each_pair do |k, v|
50
+ @table[new_struct_member(k)] = structurize(v) # @deep is used in this method
51
+ end
52
+ end
53
+ end
54
+
55
+ #
56
+ # Duplicate an SDStruct object members.
57
+ #
58
+ def initialize_copy(orig)
59
+ super
60
+ @table = @table.dup
61
+ end
62
+
63
+ #
64
+ # Provides marshalling support for use by the Marshal library.
65
+ #
66
+ def marshal_dump
67
+ to_h
68
+ end
69
+
70
+ #
71
+ # Provides marshalling support for use by the Marshal library.
72
+ #
73
+ def marshal_load(x)
74
+ @table = x.map{|a| structurize(a) }.original_to_h
75
+ end
76
+
77
+ #
78
+ # Yields all attributes (as a symbol) along with the corresponding values
79
+ # or returns an enumerator if not block is given.
80
+ # Example:
81
+ #
82
+ # require 'sd_struct'
83
+ # data = SDStruct.new("name" => "Matz", "coding language" => :ruby)
84
+ # data.each_pair.to_a # => [[:name, "Matz"], ["coding language", :ruby]]
85
+ #
86
+ def each_pair
87
+ return to_enum(__method__) { @table.size } unless block_given?
88
+ @table.each_pair{|p| yield p}
89
+ end
90
+
91
+ #
92
+ # Used internally to define field properties
93
+ #
94
+ def new_struct_member(name)
95
+ name = name.to_s.underscore.to_sym unless name[/^[A-Z]|\s+/]
96
+ unless respond_to?(name)
97
+ define_singleton_method(name) { @table[name] }
98
+ define_singleton_method("#{name}=") { |x| @table[name] = x }
99
+ end
100
+ name
101
+ end
102
+
103
+ #
104
+ # Call to struct to a value if it is an Array or a Hash and @deep is true
105
+ #
106
+ def structurize(value)
107
+ ( @deep && (value.is_a?(Hash) || value.is_a?(Array)) ) ? value.to_struct : value
108
+ end
109
+
110
+ protected :new_struct_member, :structurize
111
+
112
+
113
+ #
114
+ # Returns the value of a member.
115
+ #
116
+ # person = SDStruct.new('name' => 'Matz', 'lang' => 'ruby')
117
+ # person[:lang] # => ruby, same as person.lang
118
+ #
119
+ def [](name)
120
+ @table.has_key?(name) ? @table[name] : @table[name.to_s.underscore.to_sym]
121
+ end
122
+
123
+ #
124
+ # Sets the value of a member.
125
+ #
126
+ # person = SDStruct.new('name' => 'Matz', 'lang' => 'python')
127
+ # person[:lang] = 'ruby' # => equivalent to person.lang = 'ruby'
128
+ # person.lang # => ruby
129
+ #
130
+ def []=(name, value)
131
+ unless self.[](name).nil? || value.is_a?(self.[](name).class)
132
+ warn("You're assigning a value with different type as the previous value.")
133
+ end
134
+ @table[new_struct_member(name)] = structurize(value)
135
+ end
136
+
137
+ InspectKey = :__inspect_key__ # :nodoc:
138
+
139
+ #
140
+ # Returns a string containing a detailed summary of the keys and values.
141
+ #
142
+ def inspect
143
+ str = "#<#{self.class}"
144
+
145
+ ids = (Thread.current[InspectKey] ||= [])
146
+ if ids.include?(object_id)
147
+ return str << ' ...>'
148
+ end
149
+
150
+ ids << object_id
151
+ begin
152
+ first = true
153
+ for k,v in @table
154
+ str << "," unless first
155
+ first = false
156
+ str << " #{k[/\s+/] ? "['#{k}']" : ".#{k}"}=#{v.inspect}"
157
+ end
158
+ return str << '>'
159
+ ensure
160
+ ids.pop
161
+ end
162
+ end
163
+ alias :to_s :inspect
164
+
165
+ attr_reader :table
166
+ protected :table
167
+
168
+ def ==(other)
169
+ return false unless other.kind_of?(self.class)
170
+ @table == other.table
171
+ end
172
+
173
+ #
174
+ # Compares this object and +other+ for equality. An SDStruct is eql? to
175
+ # +other+ when +other+ is an SDStruct and the two objects' Hash tables are
176
+ # eql?.
177
+ #
178
+ def eql?(other)
179
+ return false unless other.kind_of?(self.class)
180
+ @table.eql?(other.table)
181
+ end
182
+
183
+ #
184
+ # Compute a hash-code for this SDStruct.
185
+ # Two hashes with the same content will have the same hash code
186
+ # (and will be eql?).
187
+ #
188
+ def hash
189
+ @table.hash
190
+ end
191
+
192
+ #
193
+ # Expose keys with space(s)
194
+ #
195
+ def spaced_keys
196
+ @table.keys - non_spaced_keys
197
+ end
198
+
199
+ #
200
+ # Expose keys without space(s)
201
+ #
202
+ def non_spaced_keys
203
+ methods(false).select{|x| x[/^\S+[^=]$/]}
204
+ end
205
+ alias :fields :non_spaced_keys
206
+
207
+ #
208
+ # Expose all keys
209
+ #
210
+ def keys
211
+ @table.keys
212
+ end
213
+
214
+ #
215
+ # Delete specified field or key
216
+ #
217
+ def delete_field(name)
218
+ sym = name.to_sym
219
+ @table.delete(sym) do
220
+ raise NameError.new("no field `#{sym}' in #{self}", sym)
221
+ end
222
+ singleton_class.__send__(:remove_method, sym, "#{sym}=")
223
+ end
224
+ alias :delete_key :delete_field
225
+ end
@@ -0,0 +1,37 @@
1
+ class SDStruct
2
+ using Module.new {
3
+ refine Array do
4
+ alias :original_to_h :to_h
5
+
6
+ def to_h(camelize_keys = false)
7
+ map{|x| x.respond_to?(:to_h) ? x.to_h(camelize_keys) : x }
8
+ end
9
+ end
10
+ }
11
+
12
+ def to_h(opts = {})
13
+ opts = {
14
+ camelize_keys: false,
15
+ exclude_blank_values: false,
16
+ values_to_exclude: []
17
+ }.merge(opts)
18
+
19
+ @table.map do |k, v|
20
+ v = v.to_h(opts) if v.is_a?(self.class) || v.is_a?(Array)
21
+ k = k.to_s.camelize(:lower) if opts[:camelize_keys] && !k[/\s+/]
22
+ [k, v]
23
+ end.original_to_h
24
+ .select{|_,v| opts[:exclude_blank_values] ? v.present? : !v.nil? }
25
+ .select{|_,v| !v.in?(opts[:values_to_exclude]) }
26
+ end
27
+
28
+ def to_json(opts = {})
29
+ opts = {
30
+ camelize_keys: true,
31
+ exclude_blank_values: true,
32
+ values_to_exclude: [0, [""], [{}]]
33
+ }.merge(opts)
34
+
35
+ to_h(opts).to_json
36
+ end
37
+ end
@@ -0,0 +1,112 @@
1
+ class SDStruct
2
+ using Module.new {
3
+ refine Array do
4
+
5
+ #
6
+ # Dig deep into array until non-Array and non-Hash primitive data is found
7
+ #
8
+ # @param [Symbol] multiple symbols
9
+ # @return [String,Integer,Float,Boolean,nil] first matched result
10
+ #
11
+ def dig_deep(*args)
12
+ full_args = args.dup
13
+ parent_key = args.shift
14
+ result = nil
15
+ if parent_key.is_a?(Integer)
16
+ result = dig(parent_key)
17
+ unless result.nil? || args.length.zero?
18
+ result = result.dig_deep(*args)
19
+ end
20
+ end
21
+ if result.nil?
22
+ each do |x|
23
+ if x.respond_to?(:dig_deep) || x.is_a?(Array)
24
+ result = x.dig_deep(*full_args)
25
+ end
26
+ return result unless result.nil?
27
+ end
28
+ end
29
+ return result
30
+ end
31
+ end
32
+ }
33
+
34
+ #
35
+ # Dig deep into Hash until non-Array and non-Hash primitive data is found
36
+ #
37
+ # @param [Symbol] multiple symbols
38
+ # @return [SDStruct,Hash,Array,String,Integer,Float,Boolean,nil] first matched result
39
+ #
40
+ def dig_deep(*args)
41
+ full_args = args.dup
42
+ parent_key = args.shift
43
+ result = dig(parent_key)
44
+ unless result.nil? || args.length.zero?
45
+ if result.respond_to?(:dig)
46
+ result = result.dig(*args)
47
+ end
48
+ end
49
+ if result.nil?
50
+ @table.values
51
+ .select{|v| v.respond_to?(:dig) }
52
+ .each do |v|
53
+ if v.respond_to?(:dig_deep) || v.is_a?(Array)
54
+ result = v.dig_deep(*full_args)
55
+ end
56
+ return result unless result.nil?
57
+ end
58
+ end
59
+ return result
60
+ end
61
+
62
+ #
63
+ # Dig the content of @table which is a hash
64
+ #
65
+ # @param [Symbol] multiple symbols
66
+ # @return [SDStruct,Hash,Array,String,Integer,Float,Boolean,nil] first matched result
67
+ #
68
+ def dig(*args)
69
+ @table.dig(*args)
70
+ end
71
+
72
+ def find(key_str, opts = {})
73
+ opts = {
74
+ separator: "/"
75
+ }.merge(opts)
76
+
77
+ sep = Regexp.quote(opts[:separator])
78
+
79
+ args = begin
80
+ key_str.gsub(/^#{sep}(?!#{sep})|#{sep}+$/, '')
81
+ .split(/#{sep}{2,}/)
82
+ .map do |ks|
83
+ ks.split(/#{sep}/)
84
+ .map do |x|
85
+ x.strip!
86
+ if !!x[/\A[-+]?\d+\z/]
87
+ x.to_i
88
+ else
89
+ if x[/^$|\s+/]
90
+ x
91
+ else
92
+ x.underscore.to_sym
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
98
+
99
+ if !(parent_key = args.shift) # args == [], key_str == ""
100
+ return
101
+ else # e.g. args == [[], ..] or [[.., ..], [..]]
102
+ result = dig(*parent_key) unless parent_key.empty?
103
+
104
+ unless args.length.zero?
105
+ args.each do |a|
106
+ result = result.dig_deep(*a) rescue result = dig_deep(*a)
107
+ end
108
+ end
109
+ end
110
+ return result
111
+ end
112
+ end
@@ -1,3 +1,3 @@
1
1
  class SDStruct
2
- VERSION = "0.0.3"
2
+ VERSION = "0.1.0"
3
3
  end
data/lib/sd_struct.rb CHANGED
@@ -1,234 +1,4 @@
1
1
  require "sd_struct/version"
2
-
3
-
4
- # Alternative to OpenStruct that is more strict and go deeper.
5
- #
6
- # @author Adrian Setyadi
7
- #
8
- class SDStruct
9
- using Module.new {
10
- refine Hash do
11
- def to_struct
12
- SDStruct.new(to_h.dup)
13
- end
14
- end
15
-
16
- refine Array do
17
- alias :original_to_h :to_h
18
-
19
- def to_h(camelize_keys = false)
20
- map{|x| x.respond_to?(:to_h) ? x.to_h(camelize_keys) : x }
21
- end
22
-
23
- # Call `to_struct` to an Array to go deeper or to a Hash to change it to SDStruct
24
- #
25
- # @return [Array<SDStruct,Object>] array of SDStruct or any other objects
26
- #
27
- def to_struct
28
- map{|x| ( x.is_a?(Hash) || x.is_a?(Array) ) ? x.to_struct : x }
29
- end
30
-
31
- def dig_deep(*args)
32
- full_args = args.dup
33
- parent_key = args.shift
34
- result = nil
35
- if parent_key.is_a?(Integer)
36
- result = dig(parent_key)
37
- unless result.nil? || args.length.zero?
38
- result = result.dig_deep(*args)
39
- end
40
- end
41
- if result.nil?
42
- each do |x|
43
- if x.respond_to?(:dig_deep) || x.is_a?(Array)
44
- result = x.dig_deep(*full_args)
45
- end
46
- return result unless result.nil?
47
- end
48
- end
49
- return result
50
- end
51
- end
52
- }
53
-
54
- def initialize(hash = nil, deep = true)
55
- @table = {}
56
- if hash
57
- hash.each_pair do |k, v|
58
- v = v.to_struct if deep && ( v.is_a?(Hash) || v.is_a?(Array) )
59
- @table[new_member(k)] = v
60
- end
61
- end
62
- end
63
-
64
- def initialize_copy(orig)
65
- super
66
- @table = @table.dup
67
- end
68
-
69
- def marshal_dump
70
- to_h
71
- end
72
-
73
- def marshal_load(x)
74
- @table = x.map{|a| ( a.is_a?(Hash) || a.is_a?(Array) ) ? a.to_struct : a }
75
- .original_to_h
76
- end
77
-
78
- def to_h(opt = {})
79
- opt = {
80
- camelize_keys: false,
81
- exclude_blank_values: false,
82
- values_to_exclude: []
83
- }.merge(opt)
84
-
85
- @table.map do |k, v|
86
- v = v.to_h(opt) if v.is_a?(self.class) || v.is_a?(Array)
87
- k = k.to_s.camelize(:lower) if opt[:camelize_keys] && !k[/\s+/]
88
- [k, v]
89
- end.original_to_h
90
- .select{|_,v| opt[:exclude_blank_values] ? v.present? : !v.nil? }
91
- .select{|_,v| !v.in?(opt[:values_to_exclude]) }
92
- end
93
-
94
- def to_json(opt = {})
95
- opt = {
96
- camelize_keys: true,
97
- exclude_blank_values: true,
98
- values_to_exclude: [0, [""], [{}]]
99
- }.merge(opt)
100
-
101
- to_h(opt).to_json
102
- end
103
-
104
- def new_member(name)
105
- name = name.to_s.underscore.to_sym unless name[/\s+/] # contains whitespace
106
- unless respond_to?(name)
107
- define_singleton_method(name) { @table[name] }
108
- define_singleton_method("#{name}=") { |x| @table[name] = x }
109
- end
110
- name
111
- end
112
- protected :new_member
113
-
114
- def [](name)
115
- @table.has_key?(name) ? @table[name] : @table[name.to_s.underscore.to_sym]
116
- end
117
-
118
- def []=(name, value)
119
- @table[new_member(name)] = value
120
- end
121
-
122
- InspectKey = :__inspect_key__ # :nodoc:
123
-
124
- def inspect
125
- str = "#<#{self.class}"
126
-
127
- ids = (Thread.current[InspectKey] ||= [])
128
- if ids.include?(object_id)
129
- return str << ' ...>'
130
- end
131
-
132
- ids << object_id
133
- begin
134
- first = true
135
- for k,v in @table
136
- str << "," unless first
137
- first = false
138
- str << " #{k[/\s+/] ? "['#{k}']" : ".#{k}"}=#{v.inspect}"
139
- end
140
- return str << '>'
141
- ensure
142
- ids.pop
143
- end
144
- end
145
- alias :to_s :inspect
146
-
147
- def dig_deep(*args)
148
- full_args = args.dup
149
- parent_key = args.shift
150
- result = dig(parent_key)
151
- unless result.nil? || args.length.zero?
152
- if result.respond_to?(:dig)
153
- result = result.dig(*args)
154
- end
155
- end
156
- if result.nil?
157
- @table.values
158
- .select{|v| v.respond_to?(:dig) }
159
- .each do |v|
160
- if v.respond_to?(:dig_deep) || v.is_a?(Array)
161
- result = v.dig_deep(*full_args)
162
- end
163
- return result unless result.nil?
164
- end
165
- end
166
- return result
167
- end
168
-
169
- def dig(*args)
170
- @table.dig(*args)
171
- end
172
-
173
- def find(key_str, opt = {})
174
- opt = {
175
- separator: "/"
176
- }.merge(opt)
177
-
178
- args = key_str.split(opt[:separator])
179
- .map do |x|
180
- x.strip!
181
- if !!(x =~ /\A[-+]?\d+\z/)
182
- x.to_i
183
- else
184
- if x[/\s+/]
185
- x
186
- else
187
- x.underscore.to_sym
188
- end
189
- end
190
- end
191
-
192
- result = dig_deep(*args) rescue nil
193
- return result
194
- end
195
-
196
- attr_reader :table
197
- protected :table
198
-
199
- def ==(other)
200
- return false unless other.kind_of?(self.class)
201
- @table == other.table
202
- end
203
-
204
- def eql?(other)
205
- return false unless other.kind_of?(self.class)
206
- @table.eql?(other.table)
207
- end
208
-
209
- def hash
210
- @table.hash
211
- end
212
-
213
- def spaced_keys
214
- @table.keys - non_spaced_keys
215
- end
216
-
217
- def non_spaced_keys
218
- methods(false).select{|x| x[/^\S+[^=]$/]}
219
- end
220
- alias :fields :non_spaced_keys
221
-
222
- def keys
223
- @table.keys
224
- end
225
-
226
- def delete_field(name)
227
- sym = name.to_sym
228
- @table.delete(sym) do
229
- raise NameError.new("no field `#{sym}' in #{self}", sym)
230
- end
231
- singleton_class.__send__(:remove_method, sym, "#{sym}=")
232
- end
233
- alias :delete_key :delete_field
234
- end
2
+ require "sd_struct/base"
3
+ require "sd_struct/deep_convert"
4
+ require "sd_struct/deep_search"
data/sd_struct.gemspec CHANGED
@@ -8,20 +8,20 @@ Gem::Specification.new do |spec|
8
8
  spec.version = SDStruct::VERSION
9
9
  spec.authors = ["Adrian Setyadi"]
10
10
  spec.email = ["a.styd@yahoo.com"]
11
- spec.summary = %q{Stricter and Deeper Struct}
12
- spec.description = %q{An alternative to OpenStruct that stricter in assigning values and deeper in
11
+ spec.summary = %q{Strict and Deep Struct}
12
+ spec.description = %q{An alternative to OpenStruct that more strict in assigning values and deeper in
13
13
  consuming the passed Hash and transforming it back to Hash or JSON, equipped
14
14
  with deep digging capabilities.}
15
- spec.homepage = ""
16
15
  spec.license = "MIT"
17
16
 
18
- spec.files = `git ls-files -z`.split("\x0")
19
- spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
20
- spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
17
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
18
+ f.match(%r{^(test|spec|features)/})
19
+ end
21
20
  spec.require_paths = ["lib"]
22
21
 
23
- spec.add_runtime_dependency "activesupport", "~> 4", "> 4"
22
+ spec.add_runtime_dependency "activesupport", "> 4"
24
23
 
25
- spec.add_development_dependency "bundler", "~> 1.7"
24
+ spec.add_development_dependency "bundler", "~> 1.14"
26
25
  spec.add_development_dependency "rake", "~> 10.0"
26
+ spec.add_development_dependency "rspec", "~> 3.5"
27
27
  end
metadata CHANGED
@@ -1,22 +1,19 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sd_struct
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Adrian Setyadi
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-04-09 00:00:00.000000000 Z
11
+ date: 2017-04-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
18
- - !ruby/object:Gem::Version
19
- version: '4'
20
17
  - - ">"
21
18
  - !ruby/object:Gem::Version
22
19
  version: '4'
@@ -24,9 +21,6 @@ dependencies:
24
21
  prerelease: false
25
22
  version_requirements: !ruby/object:Gem::Requirement
26
23
  requirements:
27
- - - "~>"
28
- - !ruby/object:Gem::Version
29
- version: '4'
30
24
  - - ">"
31
25
  - !ruby/object:Gem::Version
32
26
  version: '4'
@@ -36,14 +30,14 @@ dependencies:
36
30
  requirements:
37
31
  - - "~>"
38
32
  - !ruby/object:Gem::Version
39
- version: '1.7'
33
+ version: '1.14'
40
34
  type: :development
41
35
  prerelease: false
42
36
  version_requirements: !ruby/object:Gem::Requirement
43
37
  requirements:
44
38
  - - "~>"
45
39
  - !ruby/object:Gem::Version
46
- version: '1.7'
40
+ version: '1.14'
47
41
  - !ruby/object:Gem::Dependency
48
42
  name: rake
49
43
  requirement: !ruby/object:Gem::Requirement
@@ -58,8 +52,22 @@ dependencies:
58
52
  - - "~>"
59
53
  - !ruby/object:Gem::Version
60
54
  version: '10.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '3.5'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '3.5'
61
69
  description: |-
62
- An alternative to OpenStruct that stricter in assigning values and deeper in
70
+ An alternative to OpenStruct that more strict in assigning values and deeper in
63
71
  consuming the passed Hash and transforming it back to Hash or JSON, equipped
64
72
  with deep digging capabilities.
65
73
  email:
@@ -69,14 +77,22 @@ extensions: []
69
77
  extra_rdoc_files: []
70
78
  files:
71
79
  - ".gitignore"
80
+ - ".rspec"
81
+ - CHANGELOG.md
82
+ - CODE_OF_CONDUCT.md
72
83
  - Gemfile
73
84
  - LICENSE.txt
74
85
  - README.md
75
86
  - Rakefile
87
+ - bin/console
88
+ - bin/setup
76
89
  - lib/sd_struct.rb
90
+ - lib/sd_struct/base.rb
91
+ - lib/sd_struct/deep_convert.rb
92
+ - lib/sd_struct/deep_search.rb
77
93
  - lib/sd_struct/version.rb
78
94
  - sd_struct.gemspec
79
- homepage: ''
95
+ homepage:
80
96
  licenses:
81
97
  - MIT
82
98
  metadata: {}
@@ -99,5 +115,5 @@ rubyforge_project:
99
115
  rubygems_version: 2.5.1
100
116
  signing_key:
101
117
  specification_version: 4
102
- summary: Stricter and Deeper Struct
118
+ summary: Strict and Deep Struct
103
119
  test_files: []