tidied 0.0.1 → 0.1.0

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