activerecord-pull-alpha 0.1.1 → 0.1.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f19b19bbcf24f41a86b7e783b4c68ccb5708578bdc1ca7f4fda67cc9d49efb21
4
- data.tar.gz: 23e4a83ceb4377d1d757045cadcfa21ffd1d461f45b1a9692611c44bc2a7f75e
3
+ metadata.gz: ccb0237045bd530e776a70c9e54572c9914cbb14a62c9a1e2c33ef26ca78a989
4
+ data.tar.gz: fe01b85e846a4ed2860a16c18c8ed235877098e68e460ed618a131bc360ec615
5
5
  SHA512:
6
- metadata.gz: 923ce39daf5cfa8ae28c4a3a87e063fb6e228f01129ca878bc88b6d2f2e26612ab5fd1fe9323caa6b8d46f952072596dba32089c759b62bb3df13813b905e413
7
- data.tar.gz: ddcda0c8e54c7e6a2adff171c7612f7ee220fdb17d20e5e3f0c37750b03f08c69f6000f7a8747b233bb1b001405c39a9c2a218da6e9e588b3f709e10ce5de0dd
6
+ metadata.gz: 53416aabcb2c4e20bbb51fbc771914bb641cba6d6330cc8d8ad0de105c31495d50c5d4e54bb6ce2f15ae9fc611096ea2d7c9c9b48f7d8147493d5cbe4dc3a3ee
7
+ data.tar.gz: 215519fe1cb05dbe49ebac256de0961dc355aec22cf4c382782509b58a8f48168362d8acb1273876b118f26d3a838cb4c7762092072a9bc90b173b9b25692071
@@ -0,0 +1,38 @@
1
+ # This workflow uses actions that are not certified by GitHub.
2
+ # They are provided by a third-party and are governed by
3
+ # separate terms of service, privacy policy, and support
4
+ # documentation.
5
+ # This workflow will download a prebuilt Ruby version, install dependencies and run tests with Rake
6
+ # For more information see: https://github.com/marketplace/actions/setup-ruby-jruby-and-truffleruby
7
+
8
+ name: Ruby
9
+
10
+ on:
11
+ push:
12
+ branches: [ "master" ]
13
+ pull_request:
14
+ branches: [ "master" ]
15
+
16
+ permissions:
17
+ contents: read
18
+
19
+ jobs:
20
+ test:
21
+
22
+ runs-on: ubuntu-latest
23
+ strategy:
24
+ matrix:
25
+ ruby-version: ['2.6', '2.7', '3.0']
26
+
27
+ steps:
28
+ - uses: actions/checkout@v3
29
+ - name: Set up Ruby
30
+ # To automatically get bug fixes and new Ruby versions for ruby/setup-ruby,
31
+ # change this to (see https://github.com/ruby/setup-ruby#versioning):
32
+ # uses: ruby/setup-ruby@v1
33
+ uses: ruby/setup-ruby@2b019609e2b0f1ea1a2bc8ca11cb82ab46ada124
34
+ with:
35
+ ruby-version: ${{ matrix.ruby-version }}
36
+ bundler-cache: true # runs 'bundle install' and caches installed gems automatically
37
+ - name: Run tests
38
+ run: bundle exec rake
data/.gitignore CHANGED
@@ -10,3 +10,7 @@
10
10
  # rspec failure tracking
11
11
  .rspec_status
12
12
  tags
13
+ /.idea/
14
+ /.DS_Store
15
+ /.clj-kondo/
16
+ /.lsp/
data/Gemfile CHANGED
@@ -4,3 +4,9 @@ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
4
 
5
5
  # Specify your gem's dependencies in activerecord-pull.gemspec
6
6
  gemspec
7
+
8
+ gem 'activerecord', require: 'active_record'
9
+ gem 'pry'
10
+ gem 'rubocop'
11
+ gem 'rubocop-rspec'
12
+ gem 'sqlite3'
data/Gemfile.lock CHANGED
@@ -1,35 +1,89 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- activerecord-pull-alpha (0.1.1)
4
+ activerecord-pull-alpha (0.1.2)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
8
8
  specs:
9
- diff-lcs (1.3)
10
- rake (13.0.1)
11
- rspec (3.9.0)
12
- rspec-core (~> 3.9.0)
13
- rspec-expectations (~> 3.9.0)
14
- rspec-mocks (~> 3.9.0)
15
- rspec-core (3.9.1)
16
- rspec-support (~> 3.9.1)
17
- rspec-expectations (3.9.0)
9
+ activemodel (6.1.6.1)
10
+ activesupport (= 6.1.6.1)
11
+ activerecord (6.1.6.1)
12
+ activemodel (= 6.1.6.1)
13
+ activesupport (= 6.1.6.1)
14
+ activesupport (6.1.6.1)
15
+ concurrent-ruby (~> 1.0, >= 1.0.2)
16
+ i18n (>= 1.6, < 2)
17
+ minitest (>= 5.1)
18
+ tzinfo (~> 2.0)
19
+ zeitwerk (~> 2.3)
20
+ ast (2.4.2)
21
+ coderay (1.1.3)
22
+ concurrent-ruby (1.1.10)
23
+ diff-lcs (1.5.0)
24
+ i18n (1.12.0)
25
+ concurrent-ruby (~> 1.0)
26
+ method_source (1.0.0)
27
+ minitest (5.15.0)
28
+ parallel (1.22.1)
29
+ parser (3.1.2.0)
30
+ ast (~> 2.4.1)
31
+ pry (0.14.1)
32
+ coderay (~> 1.1)
33
+ method_source (~> 1.0)
34
+ rainbow (3.1.1)
35
+ rake (13.0.6)
36
+ regexp_parser (2.5.0)
37
+ rexml (3.2.5)
38
+ rspec (3.11.0)
39
+ rspec-core (~> 3.11.0)
40
+ rspec-expectations (~> 3.11.0)
41
+ rspec-mocks (~> 3.11.0)
42
+ rspec-core (3.11.0)
43
+ rspec-support (~> 3.11.0)
44
+ rspec-expectations (3.11.0)
18
45
  diff-lcs (>= 1.2.0, < 2.0)
19
- rspec-support (~> 3.9.0)
20
- rspec-mocks (3.9.1)
46
+ rspec-support (~> 3.11.0)
47
+ rspec-mocks (3.11.1)
21
48
  diff-lcs (>= 1.2.0, < 2.0)
22
- rspec-support (~> 3.9.0)
23
- rspec-support (3.9.2)
49
+ rspec-support (~> 3.11.0)
50
+ rspec-support (3.11.0)
51
+ rubocop (1.28.2)
52
+ parallel (~> 1.10)
53
+ parser (>= 3.1.0.0)
54
+ rainbow (>= 2.2.2, < 4.0)
55
+ regexp_parser (>= 1.8, < 3.0)
56
+ rexml
57
+ rubocop-ast (>= 1.17.0, < 2.0)
58
+ ruby-progressbar (~> 1.7)
59
+ unicode-display_width (>= 1.4.0, < 3.0)
60
+ rubocop-ast (1.17.0)
61
+ parser (>= 3.1.1.0)
62
+ rubocop-rspec (2.10.0)
63
+ rubocop (~> 1.19)
64
+ ruby-progressbar (1.11.0)
65
+ sqlite3 (1.4.4)
66
+ tzinfo (2.0.5)
67
+ concurrent-ruby (~> 1.0)
68
+ unicode-display_width (2.1.0)
69
+ zeitwerk (2.6.0)
24
70
 
25
71
  PLATFORMS
26
- ruby
72
+ -darwin-21
73
+ arm64-darwin-21
74
+ arm64-darwin-22
75
+ x86_64-linux
27
76
 
28
77
  DEPENDENCIES
78
+ activerecord
29
79
  activerecord-pull-alpha!
30
- bundler (~> 1.17)
80
+ bundler (~> 2.2)
81
+ pry
31
82
  rake (~> 13.0)
32
83
  rspec (~> 3.0)
84
+ rubocop
85
+ rubocop-rspec
86
+ sqlite3
33
87
 
34
88
  BUNDLED WITH
35
- 1.17.3
89
+ 2.3.8
data/README.md CHANGED
@@ -1,3 +1,5 @@
1
+ [![Ruby](https://github.com/delonnewman/activerecord-pull/actions/workflows/ruby.yml/badge.svg)](https://github.com/delonnewman/activerecord-pull/actions/workflows/ruby.yml)
2
+
1
3
  # ActiveRecord::Pull::Alpha
2
4
 
3
5
  A simple query interface for pulling deeply nested data from records.
@@ -67,7 +69,7 @@ Or install it yourself as:
67
69
  - [GraphQL](https://graphql.org/)
68
70
  - [Entity Attribute Value Model][eav-model]
69
71
  - [RDF][rdf]
70
- - [A City is not a Tree](https://www.patternlanguage.com/archive/cityisnotatree.html)
72
+ - [A City is Not a Tree](https://www.patternlanguage.com/archive/cityisnotatree.html)
71
73
 
72
74
  ## License
73
75
 
@@ -34,7 +34,7 @@ Gem::Specification.new do |spec|
34
34
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
35
35
  spec.require_paths = ["lib"]
36
36
 
37
- spec.add_development_dependency "bundler", "~> 1.17"
37
+ spec.add_development_dependency "bundler", "~> 2.2"
38
38
  spec.add_development_dependency "rake", "~> 13.0"
39
39
  spec.add_development_dependency "rspec", "~> 3.0"
40
40
  end
@@ -1,54 +1,89 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  module Pull
3
5
  module Alpha
6
+ class SyntaxError < RuntimeError; end
7
+ class ReflectionError < RuntimeError; end
8
+
4
9
  module Core
5
10
  class << self
6
- def pull_many(records, q)
7
- records.map { |r| pull(r, q) }
11
+ # Apply the pull query to a collection of records.
12
+ #
13
+ # @param records [Enumerable<ActiveRecord::Base>]
14
+ # @param query [Symbol, String, Hash, Array]
15
+ #
16
+ # @return [Array<Hash>]
17
+ def pull_many(records, query)
18
+ records.map { |r| pull(r, query) }
8
19
  end
9
-
10
- def pull(record, q)
11
- case q
20
+
21
+ # Apply the pull query to the given record.
22
+ #
23
+ # @param record [ActiveRecord::Base]
24
+ # @param query [Symbol, String, Hash, Array]
25
+ #
26
+ # @return [Hash]
27
+ def pull(record, query)
28
+ case query
12
29
  when :*, '*'
13
30
  pull_all(record)
14
31
  when Symbol, String
15
- pull_symbol(record, q)
32
+ pull_symbol(record, query)
16
33
  when Hash
17
- pull_hash(record, q)
34
+ pull_hash(record, query)
18
35
  when Array
19
- q.map { |pat| pull(record, pat) }.reduce(&:merge)
36
+ # TODO: add filter_map as a core extension for older Ruby implementations
37
+ query.map { |pat| pull(record, pat) }.reduce(&:merge)
20
38
  else
21
- raise "Invalid pull syntax: #{q.inspect}"
39
+ raise SyntaxError, "invalid pull syntax: #{query.inspect}"
22
40
  end
23
41
  end
24
-
42
+
25
43
  private
26
-
44
+
45
+ EMPTY_HASH = {}.freeze
46
+ private_constant :EMPTY_HASH
47
+
48
+ R = ActiveRecord::Reflection
49
+ private_constant :R
50
+
51
+ # @param record [#[]]
52
+ # @param attr [Symbol, String]
53
+ #
54
+ # @return [ActiveRecord::Base, ActiveRecord::Relation, Object, nil]
27
55
  def resolved_value(record, attr)
28
- if !(val = record[attr]).nil?
29
- val
30
- elsif val.nil? and !(reflection = record.class.reflect_on_association(attr)).nil?
31
- model = reflection.class_name.constantize
32
- p model
33
- case reflection
34
- when ActiveRecord::Reflection::HasManyReflection, ActiveRecord::Reflection::HasAndBelongsToManyReflection
35
- model.where(reflection.foreign_key => record[record.class.primary_key])
36
- when ActiveRecord::Reflection::BelongsToReflection
37
- model.find_by(model.primary_key => record[reflection.foreign_key])
38
- else
39
- raise "Don't know how to resolve value for reflection: #{reflection.inspect}"
40
- end
56
+ val = record[attr]
57
+ return val unless val.nil?
58
+
59
+ reflection = record.class.reflect_on_association(attr)
60
+ return if reflection.nil?
61
+
62
+ model = reflection.class_name.constantize
63
+
64
+ case reflection
65
+ when R::HasManyReflection, R::HasAndBelongsToManyReflection
66
+ model.where(reflection.foreign_key => record[record.class.primary_key])
67
+ when R::BelongsToReflection
68
+ model.find_by(model.primary_key => record[reflection.foreign_key])
41
69
  else
42
- nil
70
+ raise ReflectionError, "don't know how to resolve value for reflection: #{reflection.inspect}"
43
71
  end
44
72
  end
45
-
73
+
46
74
  def ref_type?(value)
47
75
  value.is_a?(ActiveRecord::Relation) || value.is_a?(ActiveRecord::Base)
48
76
  end
49
-
77
+
78
+ # @param record [#[]]
79
+ # @param attr [Symbol, String]
80
+ #
81
+ # @return [Hash]
50
82
  def pull_symbol(record, attr)
51
- val = resolved_value(record, attr)
83
+ attr = attr.to_sym
84
+ val = resolved_value(record, attr)
85
+ return EMPTY_HASH if val.nil?
86
+
52
87
  if ref_type?(val) && val.is_a?(Enumerable)
53
88
  { attr => val.map { |v| pull_all(v) } }
54
89
  elsif ref_type?(val)
@@ -57,7 +92,11 @@ module ActiveRecord
57
92
  { attr => val }
58
93
  end
59
94
  end
60
-
95
+
96
+ # @param record [#[]]
97
+ # @param hash [Hash{Symbol, String => Symbol, String, Hash, Array}]
98
+ #
99
+ # @return [Hash]
61
100
  def pull_hash(record, hash)
62
101
  hash.reduce({}) do |h, (attr, pat)|
63
102
  val = resolved_value(record, attr)
@@ -72,25 +111,25 @@ module ActiveRecord
72
111
  end
73
112
  end
74
113
  end
75
-
76
- # TODO: this should pull all associations
114
+
115
+ # @param [ActiveRecord::Base]
116
+ #
117
+ # @return [Hash]
77
118
  def pull_all(record)
78
- names = record.class.attribute_names
119
+ names = record.class.attribute_names.map(&:to_sym)
79
120
  nested = record.class.nested_attributes_options
80
121
  attrs = pull(record, names)
81
-
82
- if nested.empty?
83
- attrs
84
- else
85
- nested.keys.reduce(attrs) do |h, assoc|
86
- assoc_val = record.send(assoc)
87
- if assoc_val.nil? || assoc_val.is_a?(Enumerable) && assoc_val.empty?
88
- h
89
- elsif assoc_val.is_a?(Enumerable)
90
- h.merge!(assoc => assoc_val.map { |rec| pull_all(rec) }.reject(&:nil?))
91
- else
92
- h.merge!(assoc => pull_all(assoc_val))
93
- end
122
+
123
+ return attrs if nested.empty?
124
+
125
+ nested.keys.reduce(attrs) do |h, assoc|
126
+ assoc_val = record.send(assoc)
127
+ if assoc_val.nil? || assoc_val.is_a?(Enumerable) && assoc_val.empty?
128
+ h
129
+ elsif assoc_val.is_a?(Enumerable)
130
+ h.merge!(assoc => assoc_val.filter_map { |rec| pull_all(rec) })
131
+ else
132
+ h.merge!(assoc => pull_all(assoc_val))
94
133
  end
95
134
  end
96
135
  end
@@ -0,0 +1,13 @@
1
+ require 'activerecord/pull/alpha/core'
2
+
3
+ module Enumerable
4
+ def pull(*pattern)
5
+ ActiveRecord::Pull::Alpha::Core.pull_many(self, pattern)
6
+ end
7
+ end
8
+
9
+ class Object
10
+ def pull(*pattern)
11
+ ActiveRecord::Pull::Alpha::Core.pull(self, pattern)
12
+ end
13
+ end
@@ -1,7 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Activerecord
2
4
  module Pull
3
5
  module Alpha
4
- VERSION = "0.1.1"
6
+ VERSION = "0.1.2"
5
7
  end
6
8
  end
7
9
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activerecord-pull-alpha
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Delon Newman
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-03-10 00:00:00.000000000 Z
11
+ date: 2023-08-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '1.17'
19
+ version: '2.2'
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '1.17'
26
+ version: '2.2'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rake
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -52,13 +52,14 @@ dependencies:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
54
  version: '3.0'
55
- description:
55
+ description:
56
56
  email:
57
57
  - contact@delonnewman.name
58
58
  executables: []
59
59
  extensions: []
60
60
  extra_rdoc_files: []
61
61
  files:
62
+ - ".github/workflows/ruby.yml"
62
63
  - ".gitignore"
63
64
  - ".rspec"
64
65
  - Gemfile
@@ -69,6 +70,7 @@ files:
69
70
  - activerecord-pull-alpha.gemspec
70
71
  - lib/activerecord/pull/alpha.rb
71
72
  - lib/activerecord/pull/alpha/core.rb
73
+ - lib/activerecord/pull/alpha/core_ext.rb
72
74
  - lib/activerecord/pull/alpha/version.rb
73
75
  homepage: https://github.com/delonnewman/activerecord-pull
74
76
  licenses:
@@ -78,7 +80,7 @@ metadata:
78
80
  homepage_uri: https://github.com/delonnewman/activerecord-pull
79
81
  source_code_uri: https://github.com/delonnewman/activerecord-pull
80
82
  changelog_uri: https://github.com/delonnewman/activerecord-pull#changelog
81
- post_install_message:
83
+ post_install_message:
82
84
  rdoc_options: []
83
85
  require_paths:
84
86
  - lib
@@ -93,8 +95,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
93
95
  - !ruby/object:Gem::Version
94
96
  version: '0'
95
97
  requirements: []
96
- rubygems_version: 3.0.6
97
- signing_key:
98
+ rubygems_version: 3.4.17
99
+ signing_key:
98
100
  specification_version: 4
99
101
  summary: A simple query interface for pulling deeply nested data from records.
100
102
  test_files: []