miscellany 0.1.17 → 0.1.19

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: d8272b6520386bb2e45ef6a4ae8cc6c2c349e465ca8eea10867df3c42c80c7c3
4
- data.tar.gz: aeb81bc5bb198c2f64589c1d416369bd101de5d1db074682e40f66247c2035d5
3
+ metadata.gz: e919c60e03780d5fab1a21ae6d528482db21d5a4fcfbc176b4808cc1d9acdc77
4
+ data.tar.gz: c9ecbbbb69e28e8fbef7aa9efe93db1cab9576634ede5ad2dc698180c1743dda
5
5
  SHA512:
6
- metadata.gz: 3f6ac9e74cacbdb4dd5d990a47c72608181eb005b4a9c3de81b46666bf314a246fa799c29d97753519822318740adf32fa2db63450d2d70e2abc4fb4a38c09de
7
- data.tar.gz: 99f033c54af54257ef660e889f34f683e080fe4e16baeb9d1f201fcf0091561c6d2fd5d26b582a57b9ab91b2d7c0fd3c8e72f8fc8e02f7425b0a395b4bcc7e9f
6
+ metadata.gz: 7468e0d150c79e5ebfb5760648e97699c7b88340c68b7eaa530c58e512ee982eb1d2b44c2599b374a7348a5b9cefe7d7037603a98aa5a8a895f18bac6d4efe84
7
+ data.tar.gz: 0a7dfb9250e3f686d88f7c2932cbd1a8b07f0a38ae4d3e491e98ee4c14a22126bcf3defd58227130fe3a43f8cd7d0b5dbaf002f6ded24fd187706b445fcce28e
@@ -7,8 +7,8 @@ module Miscellany
7
7
  include HttpErrorHandling
8
8
 
9
9
  # Deprecated
10
- def slice_results(queryset, **kwargs)
11
- @sliced_data = sliced_json(queryset, **kwargs) { |x| x }
10
+ def slice_results(*args, **kwargs, &blk)
11
+ @sliced_data = sliced_json(*args, **kwargs, &blk)
12
12
  end
13
13
 
14
14
  def sliced_json(
@@ -219,7 +219,7 @@ module Miscellany
219
219
 
220
220
  def sort_sql
221
221
  sorts = [ *Array(self.sort) ]
222
- sorts << options[:sort_parser]&.default
222
+ sorts.push(*options[:sort_parser]&.default_sorts)
223
223
  sorts.compact!
224
224
 
225
225
  return nil unless sorts.present?
@@ -23,10 +23,25 @@ module Miscellany
23
23
  end
24
24
 
25
25
  def self.sqlize(sorts)
26
+ seen_sorts = Set.new
27
+
28
+ # Only include each sort key/"column" once
29
+ sorts = sorts.select do |sort|
30
+ sid = sort[:key] || sort[:column]
31
+ next true unless sid.present?
32
+
33
+ if seen_sorts.include?(sid)
34
+ false
35
+ else
36
+ seen_sorts << sid
37
+ true
38
+ end
39
+ end
40
+
26
41
  sorts.map do |sort|
27
42
  order = sort[:order] || 'ASC'
28
43
  if sort[:column].is_a?(Proc)
29
- sort[:column].call(qset, order)
44
+ sort[:column].call(order)
30
45
  else
31
46
  desired_nulls = (sort[:nulls] || :low).to_s.downcase.to_sym
32
47
  nulls = case desired_nulls
@@ -45,12 +60,20 @@ module Miscellany
45
60
  class Parser
46
61
  class SortParsingError < StandardError; end
47
62
 
63
+ attr_reader :default_sorts
64
+
48
65
  def initialize(valid_sorts, default: nil)
49
- @sorts_map = normalize_sort_options(valid_sorts, default: default)
50
- end
66
+ @sorts_map = normalize_sort_options(valid_sorts)
67
+
68
+ @default_sorts = []
51
69
 
52
- def default
53
- @sorts_map[:default]
70
+ parsed_defaults = normalize_sort_options(Array(default))
71
+ parsed_defaults.each do |k,v|
72
+ @sorts_map[k] ||= v
73
+ end
74
+ @default_sorts = parsed_defaults.keys.map do |k|
75
+ @sorts_map[k]
76
+ end
54
77
  end
55
78
 
56
79
  def valid?(sortstr)
@@ -80,39 +103,31 @@ module Miscellany
80
103
  sort
81
104
  end
82
105
 
83
- sorts << self.default if default == :append || default && !sorts.compact.present?
106
+ if default == :append || default && !sorts.compact.present?
107
+ sorts.push(*self.default_sorts)
108
+ end
84
109
 
85
110
  sorts.compact.presence
86
111
  end
87
112
 
88
113
  protected
89
114
 
90
- def normalize_sort_options(sorts, default: nil)
115
+ def normalize_sort_options(sorts)
91
116
  norm_sorts = { }
92
117
 
93
118
  sorts.each do |s|
94
119
  if s.is_a?(Hash)
95
120
  s.each do |k,v|
121
+ k = k.to_s
96
122
  sort_hash = normalize_sort(v, key: k)
97
123
  norm_sorts[k] = sort_hash
98
124
  end
99
125
  else
100
126
  sort_hash = normalize_sort(s)
101
127
  norm_sorts[sort_hash[:column]] = sort_hash
102
- # default ||= sort_hash
103
128
  end
104
129
  end
105
130
 
106
- if default.present?
107
- norm_default = normalize_sort(default)
108
- reference = norm_sorts[norm_default[:key].to_s] || norm_default
109
- norm_sorts[:default] = {
110
- key: reference[:key],
111
- column: reference[:column],
112
- order: norm_default[:order] || reference[:order],
113
- }
114
- end
115
-
116
131
  norm_sorts
117
132
  end
118
133
 
@@ -1,3 +1,3 @@
1
1
  module Miscellany
2
- VERSION = "0.1.17".freeze
2
+ VERSION = "0.1.19".freeze
3
3
  end
@@ -129,6 +129,19 @@ describe Miscellany::SlicedResponse do
129
129
 
130
130
  expect(subject.sliced_json(ARModel.all, { page: "all" }, allow_all: true, **slice_config)).to be_a Hash
131
131
  end
132
+
133
+ context "with Hash-based valid_sorts" do
134
+ let(:source) { ARModel.all }
135
+
136
+ before(:each) do
137
+ slice_config[:valid_sorts] = [
138
+ title: ->(dir) { "title #{dir}" }
139
+ ]
140
+ end
141
+
142
+ include_examples "basic functionality"
143
+ include_examples "sortable"
144
+ end
132
145
  end
133
146
 
134
147
  describe "#bearcat_as_sliced_json" do
@@ -0,0 +1,124 @@
1
+
2
+ require 'spec_helper'
3
+
4
+ describe Miscellany::SortLang do
5
+
6
+ describe ".normalize_sort" do
7
+ it "accepts Hash KV pairs" do
8
+ expect(Miscellany::SortLang.normalize_sort(["key", { column: "created_at", order: "ASC" }])).to eql({
9
+ column: "created_at", force_order: false, key: "created_at", order: "ASC"
10
+ })
11
+ end
12
+
13
+ it "accepts strings" do
14
+ expect(Miscellany::SortLang.normalize_sort("created_at")).to eql({
15
+ column: "created_at", force_order: false, key: "created_at"
16
+ })
17
+ expect(Miscellany::SortLang.normalize_sort("created_at ASC")).to eql({
18
+ column: "created_at", force_order: false, key: "created_at", order: "ASC"
19
+ })
20
+ expect(Miscellany::SortLang.normalize_sort("created_at ASC!")).to eql({
21
+ column: "created_at", force_order: true, key: "created_at", order: "ASC"
22
+ })
23
+ end
24
+
25
+ it "accepts Procs" do
26
+ logic = ->(x) { "created_at #{x}" }
27
+ expect(Miscellany::SortLang.normalize_sort(logic, key: "k")).to eql({
28
+ column: logic, key: "k"
29
+ })
30
+ end
31
+ end
32
+
33
+ describe ".sqlize" do
34
+ it "only includes each sort key once" do
35
+ expect(Miscellany::SortLang.sqlize([
36
+ { column: "created_at", order: "ASC" },
37
+ { column: "created_at", order: "DESC" },
38
+ ])).to eql "created_at ASC NULLS FIRST"
39
+ end
40
+
41
+ describe "nulls: option" do
42
+ it "accepts a nulls: option" do
43
+ expect(Miscellany::SortLang.sqlize([
44
+ { column: "created_at", order: "ASC", nulls: :last },
45
+ ])).to eql "created_at ASC NULLS LAST"
46
+ end
47
+
48
+ it "nulls: :high works" do
49
+ expect(Miscellany::SortLang.sqlize([
50
+ { column: "created_at", order: "ASC", nulls: :high },
51
+ ])).to eql "created_at ASC NULLS LAST"
52
+ expect(Miscellany::SortLang.sqlize([
53
+ { column: "created_at", order: "DESC", nulls: :high },
54
+ ])).to eql "created_at DESC NULLS FIRST"
55
+ end
56
+
57
+ it "nulls: :low works" do
58
+ expect(Miscellany::SortLang.sqlize([
59
+ { column: "created_at", order: "ASC", nulls: :low },
60
+ ])).to eql "created_at ASC NULLS FIRST"
61
+ expect(Miscellany::SortLang.sqlize([
62
+ { column: "created_at", order: "DESC", nulls: :low },
63
+ ])).to eql "created_at DESC NULLS LAST"
64
+ end
65
+ end
66
+ end
67
+
68
+ describe Miscellany::SortLang::Parser do
69
+ let(:valid_sorts) {[
70
+ "title", "created_at"
71
+ ]}
72
+ let(:default_sort) { "title" }
73
+
74
+ let(:subject) { Miscellany::SortLang::Parser.new(valid_sorts, default: default_sort) }
75
+
76
+ context "with multiple default sorts" do
77
+ let(:default_sort) { ["title", "created_at"] }
78
+
79
+ it "includes each" do
80
+ expect(subject.parse(nil)).to eql [
81
+ {:column=>"title", :force_order=>false, :key=>"title"},
82
+ {:column=>"created_at", :force_order=>false, :key=>"created_at"},
83
+ ]
84
+ end
85
+ end
86
+
87
+ describe "#parse" do
88
+ it "excludes uknown sorts" do
89
+ expect(subject.parse("updated_at")).to eql [
90
+ {:column=>"title", :force_order=>false, :key=>"title"},
91
+ ]
92
+ end
93
+
94
+ it "considers force_order" do
95
+ subject = Miscellany::SortLang::Parser.new([ "title ASC!" ])
96
+ expect(subject.parse("title DESC")).to eql [
97
+ {:column=>"title", :force_order=>true, :key=>"title", :order=>"ASC"},
98
+ ]
99
+ end
100
+
101
+ it "returns the default sort if no sort is given" do
102
+ expect(subject.parse("")).to eql [
103
+ {:column=>"title", :force_order=>false, :key=>"title"},
104
+ ]
105
+ expect(subject.parse("", default: true)).to eql [
106
+ {:column=>"title", :force_order=>false, :key=>"title"},
107
+ ]
108
+ end
109
+
110
+ it "appends the default sort" do
111
+ expect(subject.parse("created_at", default: :append)).to eql [
112
+ {:column=>"created_at", :force_order=>false, :key=>"created_at"},
113
+ {:column=>"title", :force_order=>false, :key=>"title"},
114
+ ]
115
+ end
116
+
117
+ it "excludes the default sort" do
118
+ expect(subject.parse("created_at", default: false)).to eql [
119
+ {:column=>"created_at", :force_order=>false, :key=>"created_at"},
120
+ ]
121
+ end
122
+ end
123
+ end
124
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: miscellany
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.17
4
+ version: 0.1.19
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ethan Knapp
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-09-29 00:00:00.000000000 Z
11
+ date: 2023-10-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -149,6 +149,7 @@ files:
149
149
  - spec/miscellany/goldiload_value_spec.rb
150
150
  - spec/miscellany/param_validator_spec.rb
151
151
  - spec/miscellany/sliced_response_spec.rb
152
+ - spec/miscellany/sort_lang_spec.rb
152
153
  - spec/spec_helper.rb
153
154
  homepage: https://instructure.com
154
155
  licenses: []
@@ -174,6 +175,7 @@ specification_version: 4
174
175
  summary: Gem for a bunch of random, re-usable Rails Concerns & Helpers
175
176
  test_files:
176
177
  - spec/db/database.yml
178
+ - spec/miscellany/sort_lang_spec.rb
177
179
  - spec/miscellany/arbitrary_prefetch_spec.rb
178
180
  - spec/miscellany/sliced_response_spec.rb
179
181
  - spec/miscellany/param_validator_spec.rb