omg-attrs 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: f3eaed4c8a55dca3f9d6e4c6ecd00781116986cc1c81d1105960225bbfb74a85
4
+ data.tar.gz: 65c6269712e3e992694866d0c95a96b8d27b3542e78807c7a842cc1bcc4fbc25
5
+ SHA512:
6
+ metadata.gz: 4c5c5c1d6c2b3fa0604cb1c930f87b3953b62c3679066deca0818e132187cc37579037886f8ff0d1e0daaf4c426d7a0c68de0da2997b4380b41cc7d209754fa2
7
+ data.tar.gz: 04c77582e0256724d3ed1dfa090dd398a3b5da7e5c5e0b450b94bf388532ca076fa7f3a03dbf51ddf0e04201e81b9268284a88e6970105d6785ccff4a61b2ea5
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/.rubocop.yml ADDED
@@ -0,0 +1,13 @@
1
+ AllCops:
2
+ TargetRubyVersion: 2.7.8
3
+
4
+ Style/StringLiterals:
5
+ Enabled: true
6
+ EnforcedStyle: single_quotes
7
+
8
+ Style/StringLiteralsInInterpolation:
9
+ Enabled: true
10
+ EnforcedStyle: single_quotes
11
+
12
+ Layout/LineLength:
13
+ Max: 120
data/README.md ADDED
@@ -0,0 +1,45 @@
1
+ # Attrs
2
+
3
+ ## Installation
4
+
5
+ ### Testing locally
6
+ ```sh
7
+ # Build gem
8
+ gem build attrs.gemspec
9
+
10
+ # Install gem
11
+ gem i -l /path/to/this/folder/omg-attrs-0.1.0.gem
12
+ ```
13
+
14
+ ## Usage
15
+
16
+ ```ruby
17
+ dad_hash = {
18
+ age: 35,
19
+ hair_color: 'brown',
20
+ children: [
21
+ { age: 7, hair_color: 'blonde' },
22
+ { age: 3, hair_color: 'brown' }
23
+ ],
24
+ wife: { age: 35, hair_color: 'brown' }
25
+ }
26
+
27
+ dad_hash.attrs(:age) # => 35
28
+ dad_hash.attrs(wife: :age, children: :hair_color) # =>
29
+ # {
30
+ # wife: 35,
31
+ # children: [
32
+ # { hair_color: 'blonde' },
33
+ # { hair_color: 'brown' },
34
+ # ],
35
+ # }
36
+ ```
37
+
38
+ ## Running tests
39
+
40
+ ```ruby
41
+ rspec
42
+
43
+ # or
44
+ bundle exec rspec
45
+ ```
data/attrs.gemspec ADDED
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ Gem::Specification.new do |spec|
4
+ spec.name = 'omg-attrs'
5
+ spec.version = '0.1.0'
6
+ spec.authors = ['Matthew Greenfield']
7
+ spec.email = ['mattgreenfield1@gmail.com']
8
+
9
+ spec.summary = 'Allows all objects to call `.attrs` which acts as a recursive `.slice` method'
10
+ spec.description = 'Makes it easy to slice and dice objects and collections of objects'
11
+
12
+ spec.homepage = 'https://github.com/omgreenfield/omg-util/tree/main/attrs'
13
+ spec.license = 'MIT'
14
+ spec.required_ruby_version = '>= 2.7.0'
15
+
16
+ spec.metadata['homepage_uri'] = spec.homepage
17
+ spec.metadata['rubygems_mfa_required'] = 'true'
18
+
19
+ spec.files = Dir.chdir(__dir__) do
20
+ `git ls-files -z`.split("\x0").reject do |f|
21
+ (File.expand_path(f) == __FILE__) ||
22
+ f.start_with?(*%w[spec/.git .github Gemfile])
23
+ end
24
+ end
25
+ spec.require_paths = ['lib']
26
+ end
data/lib/attrs.rb ADDED
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Attrs
4
+ def self.included(base)
5
+ base.include(InstanceMethods)
6
+ end
7
+
8
+ module InstanceMethods
9
+ def attrs(*attrs)
10
+ return map { |i| i.attrs(*attrs) } if is_list?(self)
11
+
12
+ base_attrs, nested_attrs = attrs.partition { |attr| attr.is_a?(Symbol) }
13
+ nested_attrs.map! do |attr|
14
+ if attr.is_a?(Hash)
15
+ nested_attrs(attr)
16
+ elsif attr.is_a?(Array) && attr.size == 2 && attr.first.is_a?(Symbol)
17
+ nested_attrs([attr].to_h)
18
+ elsif attr.is_a?(Array)
19
+ attr.map { |a| a.attrs(a) }
20
+ else
21
+ raise ArgumentError, "Invalid attribute: #{attr}"
22
+ end
23
+ end
24
+
25
+ base_attrs = base_attrs.to_h { |attr| [attr, get(attr)] }
26
+ nested_attrs.reduce(base_attrs, :merge)
27
+ end
28
+
29
+ private
30
+
31
+ def nested_attrs(attr)
32
+ attr.to_h do |key, value|
33
+ records = get(key)
34
+
35
+ values = is_list?(records) ?
36
+ records.map { |record| record.attrs(*value) } :
37
+ records.attrs(*value)
38
+
39
+ [key, values]
40
+ end
41
+ end
42
+
43
+ def get(key)
44
+ if respond_to?(key)
45
+ send(key)
46
+ elsif is_a?(Hash)
47
+ self[key]
48
+ else
49
+ # Denoting that the object does not respond to that key
50
+ NilClass
51
+ end
52
+ end
53
+
54
+ def is_list?(object)
55
+ object.is_a?(Enumerable) && !object.is_a?(Hash)
56
+ end
57
+ end
58
+ end
59
+
60
+ Object.include Attrs
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'ostruct'
4
+
5
+ RSpec.describe Attrs do
6
+ it 'allows recursive slice-like attribute fetching' do
7
+ dad_hash = {
8
+ age: 35,
9
+ hair_color: 'brown',
10
+ children: [
11
+ { age: 7, hair_color: 'blonde' },
12
+ { age: 3, hair_color: 'brown' }
13
+ ],
14
+ wife: { age: 35, hair_color: 'brown' }
15
+ }
16
+
17
+ expect(dad_hash.attrs(:age, wife: %i[hair_color age], children: %i[hair_color age])).to eq(
18
+ {
19
+ age: 35,
20
+ wife: { hair_color: 'brown', age: 35 },
21
+ children: [
22
+ { hair_color: 'blonde', age: 7 },
23
+ { hair_color: 'brown', age: 3 },
24
+ ],
25
+ }
26
+ )
27
+
28
+ dad_object = OpenStruct.new(dad_hash)
29
+ expect(dad_object.attrs(:age, wife: %i[hair_color age], children: %i[hair_color age])).to eq(
30
+ {
31
+ age: 35,
32
+ wife: { hair_color: 'brown', age: 35 },
33
+ children: [
34
+ { hair_color: 'blonde', age: 7 },
35
+ { hair_color: 'brown', age: 3 },
36
+ ],
37
+ }
38
+ )
39
+
40
+ children_array = dad_object.children
41
+ expect(children_array.attrs(:hair_color, :age)).to eq([{ hair_color: 'blonde', age: 7 }, { hair_color: 'brown', age: 3 }])
42
+ end
43
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "attrs"
4
+
5
+ RSpec.configure do |config|
6
+ # Enable flags like --only-failures and --next-failure
7
+ config.example_status_persistence_file_path = ".rspec_status"
8
+
9
+ # Disable RSpec exposing methods globally on `Module` and `main`
10
+ config.disable_monkey_patching!
11
+
12
+ config.expect_with :rspec do |c|
13
+ c.syntax = :expect
14
+ end
15
+ end
metadata ADDED
@@ -0,0 +1,52 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: omg-attrs
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Matthew Greenfield
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2024-04-30 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Makes it easy to slice and dice objects and collections of objects
14
+ email:
15
+ - mattgreenfield1@gmail.com
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - ".rspec"
21
+ - ".rubocop.yml"
22
+ - README.md
23
+ - attrs.gemspec
24
+ - lib/attrs.rb
25
+ - spec/attrs_spec.rb
26
+ - spec/spec_helper.rb
27
+ homepage: https://github.com/omgreenfield/omg-util/tree/main/attrs
28
+ licenses:
29
+ - MIT
30
+ metadata:
31
+ homepage_uri: https://github.com/omgreenfield/omg-util/tree/main/attrs
32
+ rubygems_mfa_required: 'true'
33
+ post_install_message:
34
+ rdoc_options: []
35
+ require_paths:
36
+ - lib
37
+ required_ruby_version: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ version: 2.7.0
42
+ required_rubygems_version: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
47
+ requirements: []
48
+ rubygems_version: 3.5.9
49
+ signing_key:
50
+ specification_version: 4
51
+ summary: Allows all objects to call `.attrs` which acts as a recursive `.slice` method
52
+ test_files: []