tidied 0.0.1 → 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 463235ca364942725d0325e85cce2fe4ae40e59a7c84f80f4020e551f1062624
4
- data.tar.gz: c50531367cbd402a7a778b7bf6db04494b6d6282bb83c54ed803e7a4e8d1ce3c
3
+ metadata.gz: 6f28badb45e6138ff37f9aa60a877fd335a217f8b2feda6e0a3c323a317f3812
4
+ data.tar.gz: 77d1682eb7ea5c1d52512d6dfdfa78e97c10b9aad076657ae3ccfc3e75c8b999
5
5
  SHA512:
6
- metadata.gz: 585c8693f33827610835cbe7b95abcdf37bf0339f484290d1e8f6fca662963715aba3a2a08098fbb6dd5e77e5d8f51b2363a9c97ace869a448dacaade24ce1ae
7
- data.tar.gz: 6b9bf48cc6684674b36bce655ed1b08c2ad9008dfe0a3b624ecb3102e660678e2ab5f1795076ab23985c652cb8d4e9bd11df96f588af4594a90003dd82a9d2d7
6
+ metadata.gz: 9d1525ca8780bd9692f7e7c6e22f5f6154cf949ece328db960d52b47ffc8fe85d5d4b5c51f25f14d4ed0b3266d186a330beaece8581d6e8fd19fea56c26a6582
7
+ data.tar.gz: 7ce14f95efbc480cc073ab4be43c9a4c25ad0aaae0075e876e7d7efeb64fd8f48d5a3294dabc31b6fea5618708841aaccb66a2958a7b02b271cc163d2584d6c4
@@ -2,10 +2,13 @@ PATH
2
2
  remote: .
3
3
  specs:
4
4
  tidied (0.1.0)
5
+ anyway_config (>= 2.0.0)
5
6
 
6
7
  GEM
7
8
  remote: https://rubygems.org/
8
9
  specs:
10
+ anyway_config (2.1.0)
11
+ ruby-next-core (>= 0.11.0)
9
12
  ast (2.4.1)
10
13
  diff-lcs (1.4.4)
11
14
  parallel (1.20.1)
@@ -39,6 +42,7 @@ GEM
39
42
  unicode-display_width (>= 1.4.0, < 3.0)
40
43
  rubocop-ast (1.4.0)
41
44
  parser (>= 2.7.1.5)
45
+ ruby-next-core (0.12.0)
42
46
  ruby-progressbar (1.11.0)
43
47
  unicode-display_width (2.0.0)
44
48
 
@@ -1,8 +1,38 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "tidied/version"
4
+ require_relative "tidied/config"
5
+ require "forwardable"
6
+ require_relative "tidied/sorting"
7
+ require_relative "tidied/filtering"
8
+
9
+ class Tidied
10
+ extend Forwardable
11
+ def_delegators :@view, :first_name, :last_name
4
12
 
5
- module Tidied
6
13
  class Error < StandardError; end
7
- # Your code goes here...
14
+
15
+ class << self
16
+ def config
17
+ @config ||= Config.new
18
+ yield @config if block_given?
19
+ @config
20
+ end
21
+
22
+ def configure
23
+ yield(config) if block_given?
24
+ end
25
+ end
26
+
27
+ def initialize(collection)
28
+ @collection = collection
29
+ end
30
+
31
+ def sort(*instructions)
32
+ Sorting.new(*instructions).execute(@view)
33
+ end
34
+
35
+ def filter(*instructions)
36
+ Filtering.new(*instructions).execute(@view)
37
+ end
8
38
  end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "date"
4
+
5
+ class Tidied
6
+ class Filtering
7
+ def initialize(*instructions)
8
+ @defaults = {
9
+ operator: :==,
10
+ accessor: :itself,
11
+ value: nil,
12
+ case_sensitive: true
13
+ }
14
+ @instructions = instructions.map do |instruction|
15
+ @defaults.merge(instruction)
16
+ end
17
+ end
18
+
19
+ def execute(collection)
20
+ return false unless collection.respond_to? :select
21
+
22
+ @instructions.reduce(collection) do |output, instruction|
23
+ output.select do |item|
24
+ raw_value = access_value(from: item, at: instruction[:accessor])
25
+ attribute_value = process_value(from: raw_value, given: instruction)
26
+
27
+ next false if not attribute_value.respond_to?(instruction[:operator])
28
+
29
+ operator_method = attribute_value.method(instruction[:operator])
30
+ query_value = process_value(from: instruction[:value], given: instruction)
31
+
32
+ if operator_method.arity.zero?
33
+ operator_method.call
34
+ elsif operator_method.arity == 1
35
+ operator_method.call(query_value)
36
+ elsif operator_method.arity.abs.positive?
37
+ operator_method.call(*query_value)
38
+ else
39
+ raise Error.new("invalid operator `#{instruction[:operator]}` for `#{attribute_value}` at `#{instruction[:accessor]}`")
40
+ end
41
+ end
42
+ end
43
+ end
44
+
45
+ private
46
+
47
+ def access_value(from:, at:)
48
+ path = at.to_s.split('.')
49
+ path.reduce(from) do |object, signal|
50
+ break nil unless object.respond_to? signal
51
+
52
+ object.public_send(signal)
53
+ end
54
+ end
55
+
56
+ def process_value(from:, given:)
57
+ return from.map { |v| process_value(from: v, given: given) } if from.respond_to?(:each)
58
+ return from if not from.is_a?(String) || from.is_a?(Symbol)
59
+ return from.downcase if not given[:case_sensitive]
60
+
61
+ from
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,134 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "date"
4
+
5
+ class Tidied
6
+ class Sorting
7
+ def initialize(*instructions)
8
+ @defaults = {
9
+ direction: :ascending,
10
+ nils: :small,
11
+ accessor: :itself,
12
+ case_sensitive: true,
13
+ normalized: false,
14
+ natural: false
15
+ }
16
+ @instructions = instructions.map do |instruction|
17
+ @defaults.merge(instruction)
18
+ end
19
+ end
20
+
21
+ def execute(collection)
22
+ collection.sort_by do |item|
23
+ @instructions.map do |instruction|
24
+ raw_value = access_value(from: item, at: instruction[:accessor])
25
+ processed_value = process_value(from: raw_value, given: instruction)
26
+ numeric_value = numeric_value(from: processed_value)
27
+ direction_multiplier = direction_multiplier(given: instruction[:direction])
28
+ nils_multiplier = nils_multiplier(given: instruction[:nils])
29
+
30
+ # p({ raw_value: raw_value, processed_value: processed_value, numeric_value: numeric_value, direction_multiplier: direction_multiplier, nils_multiplier: nils_multiplier })
31
+
32
+ if numeric_value.nil? # [1, 0] [-1, 0]
33
+ [direction_multiplier * nils_multiplier, 0]
34
+ else # [0, n] [0, -n]
35
+ [0, numeric_value * direction_multiplier]
36
+ end # [-1, 0] [0, -n] [0, n] [1, 0]
37
+ end
38
+ end
39
+ end
40
+
41
+ private
42
+
43
+ def access_value(from:, at:)
44
+ path = at.to_s.split('.')
45
+ path.reduce(from) do |object, signal|
46
+ break nil unless object.respond_to? signal
47
+
48
+ object.public_send(signal)
49
+ end
50
+ end
51
+
52
+ def process_value(from:, given:)
53
+ return from if not from.is_a?(String) || from.is_a?(Symbol)
54
+ return from.downcase if not given[:case_sensitive]
55
+ return normalize(from) + from if given[:normalized]
56
+ return segment(from) if given[:natural]
57
+
58
+ from
59
+ end
60
+
61
+ def numeric_value(from:)
62
+ return from if from.is_a?(Numeric)
63
+ return 1 if from == true
64
+ return 0 if from == false
65
+
66
+ if from.is_a?(String) || from.is_a?(Symbol)
67
+ string_to_numeric_value(from)
68
+ elsif from.is_a?(Date)
69
+ time_to_numeric_value(Time.new(from.year, from.month, from.day, 00, 00, 00, 00))
70
+ elsif from.respond_to?(:to_time)
71
+ time_to_numeric_value(from.to_time)
72
+ elsif from.respond_to?(:map)
73
+ segment_array_to_numeric_value(from)
74
+ else
75
+ from
76
+ end
77
+ end
78
+
79
+ def direction_multiplier(given:)
80
+ return -1 if given == :descending
81
+
82
+ 1
83
+ end
84
+
85
+ def nils_multiplier(given:)
86
+ return -1 if given == :small
87
+
88
+ 1
89
+ end
90
+
91
+ def string_to_numeric_value(string)
92
+ string # "aB09ü""
93
+ .chars # ["a", "B", "0", "9", "ü"]
94
+ .map { |char| char.ord.to_s.rjust(3, '0') } # ["097", "066", "048", "057", "252"]
95
+ .insert(1, '.') # ["097", ".", "066", "048", "057", "252"]
96
+ .reduce(&:concat) # "097.066048057252"
97
+ .to_r # (24266512014313/2500000000000)
98
+ end
99
+
100
+ def time_to_numeric_value(time) # https://stackoverflow.com/a/30604935/2884386
101
+ time # 2000-01-01 00:00:00 +0000
102
+ .utc # 2000-01-01 00:00:00 UTC
103
+ .to_f # 946684800.0
104
+ .*(1000) # 946684800000.0
105
+ .round # 946684800000
106
+ end
107
+
108
+ def segment_array_to_numeric_value(segments)
109
+ segments # ["a", 12, "b", 34, "c"]
110
+ .map { |x| x.is_a?(Numeric) ? x : x.ord } # [97, 12, 98, 34, 99]
111
+ .map { |n| (n + 1).to_s.rjust(3, '0') } # ["098", "013", "099", "035", "100"]
112
+ .insert(1, '.') # ["098", ".", "013", "099", "035", "100"]
113
+ .join # "098.013099035100"
114
+ .to_r # (980130990351/100000000000)
115
+ end
116
+
117
+ def normalize(string) # https://github.com/grosser/sort_alphabetical
118
+ string # "Äaáäßs"
119
+ .unicode_normalize(:nfd) # "Äaáäßs"
120
+ .downcase(:fold) # "äaáässs"
121
+ .chars # ["a", "̈", "a", "a", "́", "a", "̈", "s", "s", "s"]
122
+ .select { |char| char =~ /[[:ascii:]]/ } # ["a", "a", "a", "a", "s", "s", "s"]
123
+ .join # "aaaasss"
124
+ end
125
+
126
+ def segment(string) # https://stackoverflow.com/a/15170063/2884386
127
+ digits_or_not_digit_regex = /[[:digit:]]+|[^[:digit:]]/
128
+
129
+ string # "ab12cd34,-56"
130
+ .scan(digits_or_not_digit_regex) # ["a", "b", "12", "c", "d", "34", ",", "-", "56"]
131
+ .map { |a| a =~ /[[:digit:]]+/ ? a.to_i : a } # ["a", "b", 12, "c", "d", 34, ",", "-", 56]
132
+ end
133
+ end
134
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Tidied
4
- VERSION = "0.0.1"
3
+ class Tidied
4
+ VERSION = "0.1.0"
5
5
  end
@@ -28,8 +28,7 @@ Gem::Specification.new do |spec|
28
28
  spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
29
29
  spec.require_paths = ["lib"]
30
30
 
31
- # Uncomment to register a new dependency of your gem
32
- # spec.add_dependency "example-gem", "~> 1.0"
31
+ spec.add_dependency "anyway_config", ">= 2.0.0"
33
32
 
34
33
  # For more information and examples about making a new gem, checkout our
35
34
  # guide at: https://bundler.io/guides/creating_gem.html
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tidied
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - fractaledmind
@@ -9,7 +9,21 @@ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
11
  date: 2021-01-20 00:00:00.000000000 Z
12
- dependencies: []
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: anyway_config
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 2.0.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 2.0.0
13
27
  description: Filter, sort, and/or paginate your data, whether it comes from a database
14
28
  or is a simple Ruby collection.
15
29
  email:
@@ -30,6 +44,8 @@ files:
30
44
  - bin/console
31
45
  - bin/setup
32
46
  - lib/tidied.rb
47
+ - lib/tidied/filtering.rb
48
+ - lib/tidied/sorting.rb
33
49
  - lib/tidied/version.rb
34
50
  - tidied.gemspec
35
51
  homepage: https://github.com/fractaledmind/tidied