activerecord-pull-alpha 0.1.1 → 0.1.2

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
  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: []