cheesecloth 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/bin/console ADDED
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "bundler/setup"
5
+ require "cheesecloth"
6
+
7
+ # You can add fixtures and/or initialization code here to make experimenting
8
+ # with your gem easier. You can also use a different console, if you like.
9
+
10
+ # (If you use this, don't forget to add pry to your Gemfile!)
11
+ require "pry"
12
+ Pry.start
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,43 @@
1
+ # coding: utf-8
2
+ # frozen_string_literal: true
3
+ lib = File.expand_path("../lib", __FILE__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+ require "cheesecloth/version"
6
+
7
+ Gem::Specification.new do |spec|
8
+ spec.name = "cheesecloth"
9
+ spec.version = CheeseCloth::VERSION
10
+ spec.authors = ["Steven Petryk"]
11
+ spec.email = ["me@stevenpetryk.com"]
12
+
13
+ spec.summary = "Take the boilerplate out of filtering a collection based on params."
14
+ spec.description = <<-DESCRIPTION
15
+ Dealing with filtering based on params in Rails APIs is a pain. CheeseCloth is designed to take
16
+ the pain out of parsing, validating, and acting upon params designed to filter collections.
17
+ DESCRIPTION
18
+ spec.homepage = "https://github.com/stevenpetryk/"
19
+ spec.license = "MIT"
20
+
21
+ # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
22
+ # to allow pushing to a single host or delete this section to allow pushing to any host.
23
+ if spec.respond_to?(:metadata)
24
+ spec.metadata["allowed_push_host"] = "https://rubygems.org"
25
+ else
26
+ raise "RubyGems 2.0 or newer is required to protect against " \
27
+ "public gem pushes."
28
+ end
29
+
30
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
31
+ f.match(%r{^(test|spec|features)/})
32
+ end
33
+ spec.bindir = "exe"
34
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
35
+ spec.require_paths = ["lib"]
36
+
37
+ spec.add_dependency "activesupport", "~> 5.0"
38
+
39
+ spec.add_development_dependency "bundler", "~> 1.13"
40
+ spec.add_development_dependency "pry", "~> 0.10"
41
+ spec.add_development_dependency "rake", "~> 10.0"
42
+ spec.add_development_dependency "rspec", "~> 3.0"
43
+ end
data/circle.yml ADDED
@@ -0,0 +1,3 @@
1
+ machine:
2
+ ruby:
3
+ version: 2.3.1
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+ require "active_support"
3
+
4
+ require_relative "cheesecloth/version"
5
+ require_relative "cheesecloth/errors"
6
+ require_relative "cheesecloth/filter"
7
+ require_relative "cheesecloth/filter_list"
8
+ require_relative "cheesecloth/wrapper"
9
+
10
+ module CheeseCloth
11
+ extend ActiveSupport::Concern
12
+
13
+ included do
14
+ cattr_accessor :cheesecloth_wrapper
15
+ self.cheesecloth_wrapper = Wrapper.new(self)
16
+
17
+ attr_accessor :scope_override
18
+ attr_writer :scope
19
+ end
20
+
21
+ class_methods do
22
+ def scope(*args)
23
+ cheesecloth_wrapper.assign_default_scope_proc(*args)
24
+ end
25
+
26
+ def filter(conditions = nil, &block)
27
+ cheesecloth_wrapper.filter_list.add_filter(conditions, block)
28
+ end
29
+ end
30
+
31
+ def scope
32
+ @scope || scope_override || cheesecloth_wrapper.scope
33
+ end
34
+
35
+ def filtered_scope(scope: nil)
36
+ self.scope_override = scope
37
+ cheesecloth_wrapper.prepare_and_run(self)
38
+ self.scope
39
+ end
40
+
41
+ private
42
+
43
+ def cheesecloth_wrapper
44
+ self.class.cheesecloth_wrapper
45
+ end
46
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CheeseCloth
4
+ class MissingScopeError < StandardError
5
+ def initialize(klass)
6
+ @klass = klass
7
+ end
8
+
9
+ def message
10
+ <<-MSG.strip_heredoc
11
+ Was unable to determine the base scope. Perhaps you forgot to call `scope`?"
12
+
13
+ Try something like:
14
+
15
+ class #{@klass.name || 'FooFilterer'}
16
+ include CheeseCloth
17
+
18
+ scope -> { Model.all }
19
+ end
20
+ MSG
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+ module CheeseCloth
3
+ class Filter
4
+ attr_reader :klass, :conditions, :block
5
+
6
+ def initialize(conditions, block)
7
+ @conditions = wrap(conditions)
8
+ @block = block
9
+ end
10
+
11
+ def run(instance)
12
+ instance.instance_eval(&block)
13
+ end
14
+
15
+ def conditions_satisfied?(instance)
16
+ conditions.all? { |condition| instance.send(condition) }
17
+ end
18
+
19
+ private
20
+
21
+ def wrap(possible_array)
22
+ [possible_array].flatten.compact
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+ module CheeseCloth
3
+ class FilterList
4
+ attr_reader :instance, :filters
5
+
6
+ def initialize(instance)
7
+ @instance = instance
8
+ @filters = []
9
+ end
10
+
11
+ def add_filter(conditions, block)
12
+ @filters << Filter.new(conditions, block)
13
+ end
14
+
15
+ def run_filters(instance)
16
+ runnable_filters_on(instance).each do |filter|
17
+ instance.scope = filter.run(instance)
18
+ end
19
+ end
20
+
21
+ private
22
+
23
+ def runnable_filters_on(instance)
24
+ filters.select { |filter| filter.conditions_satisfied?(instance) }
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+ module CheeseCloth
3
+ VERSION = "0.1.0"
4
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+ module CheeseCloth
3
+ class Wrapper
4
+ attr_reader :klass, :filter_list, :scope, :default_scope_proc
5
+
6
+ def initialize(klass)
7
+ @klass = klass
8
+ @filter_list = FilterList.new(klass)
9
+ end
10
+
11
+ def assign_default_scope_proc(block)
12
+ @default_scope_proc = block
13
+ end
14
+
15
+ def prepare_and_run(instance)
16
+ assign_and_check_scope(instance)
17
+ run(instance)
18
+ end
19
+
20
+ def assign_and_check_scope(instance)
21
+ @scope ||= default_scope_proc && default_scope_proc.call
22
+ raise MissingScopeError, self.class unless instance.scope
23
+ end
24
+
25
+ def run(instance)
26
+ filter_list.run_filters(instance)
27
+ end
28
+ end
29
+ end
metadata ADDED
@@ -0,0 +1,134 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cheesecloth
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Steven Petryk
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2016-12-30 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activesupport
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '5.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '5.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.13'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.13'
41
+ - !ruby/object:Gem::Dependency
42
+ name: pry
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '0.10'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '0.10'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '10.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '10.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '3.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '3.0'
83
+ description: |2
84
+ Dealing with filtering based on params in Rails APIs is a pain. CheeseCloth is designed to take
85
+ the pain out of parsing, validating, and acting upon params designed to filter collections.
86
+ email:
87
+ - me@stevenpetryk.com
88
+ executables: []
89
+ extensions: []
90
+ extra_rdoc_files: []
91
+ files:
92
+ - ".gitignore"
93
+ - ".rspec"
94
+ - ".rubocop.yml"
95
+ - Gemfile
96
+ - LICENSE.txt
97
+ - README.md
98
+ - Rakefile
99
+ - bin/console
100
+ - bin/setup
101
+ - cheesecloth.gemspec
102
+ - circle.yml
103
+ - lib/cheesecloth.rb
104
+ - lib/cheesecloth/errors.rb
105
+ - lib/cheesecloth/filter.rb
106
+ - lib/cheesecloth/filter_list.rb
107
+ - lib/cheesecloth/version.rb
108
+ - lib/cheesecloth/wrapper.rb
109
+ homepage: https://github.com/stevenpetryk/
110
+ licenses:
111
+ - MIT
112
+ metadata:
113
+ allowed_push_host: https://rubygems.org
114
+ post_install_message:
115
+ rdoc_options: []
116
+ require_paths:
117
+ - lib
118
+ required_ruby_version: !ruby/object:Gem::Requirement
119
+ requirements:
120
+ - - ">="
121
+ - !ruby/object:Gem::Version
122
+ version: '0'
123
+ required_rubygems_version: !ruby/object:Gem::Requirement
124
+ requirements:
125
+ - - ">="
126
+ - !ruby/object:Gem::Version
127
+ version: '0'
128
+ requirements: []
129
+ rubyforge_project:
130
+ rubygems_version: 2.5.1
131
+ signing_key:
132
+ specification_version: 4
133
+ summary: Take the boilerplate out of filtering a collection based on params.
134
+ test_files: []