action_state 1.0.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 66bc5c3dbb72d3061e32ebd722563bf5f1ea6d00714604c27c6720b7e49d8d72
4
+ data.tar.gz: bd61f346df26a1ca17c8c29b39ae9e57544a0082026a948788819fbff74499e6
5
+ SHA512:
6
+ metadata.gz: 45d45ce7c60a9353c05f7b0a727aadc817d60513511ba7018b7e76383737bc294ceda47c9b4dcbce8213fdc1a3d386320f93031103f42290ea0885368206ac6d
7
+ data.tar.gz: 7d1149caa26980a04fd2c7059f54675ab0afcc806aaed3aeb8273a11b9c8a81b8eb8104f4e8e30db8dda4a291f7699282b6e56b7e16ac42a39a7e939eb42af07
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2022 Joel Drapper
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,122 @@
1
+ # action_state
2
+
3
+ ActionState provides a simple DSL for defining model states and allows you to query the state as an ActiveRecord scope on the class and a predicate on the instance.
4
+
5
+ For example, the following state definition defines a class scope `Article.published` and an instance predicate `article.published?`.
6
+
7
+ ```ruby
8
+ class Article < ApplicationRecord
9
+ state(:published) { where(published_at: ..Time.current) }
10
+ ...
11
+ end
12
+ ```
13
+
14
+ ## Usage
15
+
16
+ ActionState supports a small subset of ActiveRecord queries for the predicate definition, and delegates the scope definition to ActiveRecord.
17
+
18
+ It's not meant to comprehensively support every possible ActiveRecord query; rather it supports a few features that tend to lend themselves well to predicate definitions.
19
+
20
+ ### `where`
21
+
22
+ The `where` method checks for inclusion in an Enumerable, coverage by a Range, and equality with other types of value.
23
+
24
+ #### Inclusion in an Enumerable
25
+
26
+ ```ruby
27
+ state(:crafter) { where(role: ["designer", "developer"]) }
28
+ ```
29
+
30
+ #### Covered by a Range
31
+
32
+ ```ruby
33
+ state(:negative) { where(stars: 1..4 }
34
+ state(:indifferent) { where(stars: 5..6) }
35
+ state(:positive) { where(stars: 7..9) }
36
+
37
+ state(:recently_published) { where(published_at: 1.week.ago..) }
38
+ ```
39
+
40
+ #### Equality
41
+
42
+ ```ruby
43
+ state(:featured) { where(featured: true) }
44
+ ```
45
+
46
+ ### `where.not`
47
+
48
+ The counterpart to `where` is `where.not` which checks for exclusion from an Enumerable or Range, and inequality with other types of value.
49
+
50
+ ```ruby
51
+ state(:deleted) { where.not(deleted_at: nil) }
52
+ ```
53
+
54
+ ### `excluding`
55
+
56
+ The `excluding` method excludes specific instances of a model.
57
+
58
+ ```ruby
59
+ state(:normal) { excluding(special_post) }
60
+ ```
61
+
62
+ ### Passing arguments
63
+
64
+ States can also be defined to accept arguments.
65
+
66
+ ```ruby
67
+ state(:before) { |whenever| where(created_at: ..whenever) }
68
+ state(:after) { |whenever| where(created_at: whenever..) }
69
+ ```
70
+
71
+ ### Composing states
72
+
73
+ You can chain query methods together to form more complex queries.
74
+
75
+ ```ruby
76
+ state(:can_edit) { where(role: "admin").where.not(disabled: true) }
77
+ ```
78
+
79
+ You can also compose multiple states together.
80
+
81
+ ```ruby
82
+ state(:published) { where(published: true) }
83
+ state(:featured) { published.where(featured: true) }
84
+ ```
85
+
86
+ ## Installation
87
+
88
+ Add this line to your application's Gemfile:
89
+
90
+ ```ruby
91
+ gem "action_state"
92
+ ```
93
+
94
+ And then execute:
95
+
96
+ ```other
97
+ $ bundle
98
+ ```
99
+
100
+ Or install it yourself as:
101
+
102
+ ```other
103
+ $ gem install action_state
104
+ ```
105
+
106
+ Finally, include `ActionState` in your model class or `ApplicationRecord`:
107
+
108
+ ```ruby
109
+ class ApplicationRecord < ActiveRecord::Base
110
+ include ActionState
111
+ ...
112
+ end
113
+ ```
114
+
115
+ ## Contributing
116
+
117
+ Contributions are welcome. Please feel free top open a PR / issue / discussion.
118
+
119
+ ## License
120
+
121
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
122
+
data/Rakefile ADDED
@@ -0,0 +1,3 @@
1
+ require "bundler/setup"
2
+
3
+ require "bundler/gem_tasks"
@@ -0,0 +1,63 @@
1
+ module ActionState
2
+ class Predicate
3
+ attr_reader :result
4
+
5
+ def initialize(record)
6
+ @record = record
7
+ @result = true
8
+ end
9
+
10
+ def excluding(*records)
11
+ return self unless result
12
+
13
+ @result = false if records.include?(@record)
14
+
15
+ self
16
+ end
17
+
18
+ def where(**kwargs)
19
+ return self unless @result
20
+
21
+ @result = false if kwargs.any? do |k, v|
22
+ case v
23
+ when ::Range
24
+ !v.cover? @record.send(k)
25
+ when ::Enumerable
26
+ !v.include? @record.send(k)
27
+ else
28
+ v != @record.send(k)
29
+ end
30
+ end
31
+
32
+ self
33
+ end
34
+
35
+ def not(**kwargs)
36
+ return self unless @result
37
+
38
+ @result = false if kwargs.any? do |k, v|
39
+ attribute = @record.send(k)
40
+ next true if attribute.nil? && !v.nil?
41
+
42
+ case v
43
+ when ::Range
44
+ v.cover? attribute
45
+ when ::Enumerable
46
+ v.include? attribute
47
+ else
48
+ v == attribute
49
+ end
50
+ end
51
+
52
+ self
53
+ end
54
+
55
+ def method_missing(method_name, *args, **kwargs, &block)
56
+ return self unless @result
57
+
58
+ @result = false unless @record.send("#{method_name}?", *args, **kwargs, &block)
59
+
60
+ self
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,4 @@
1
+ module ActionState
2
+ class Railtie < ::Rails::Railtie
3
+ end
4
+ end
@@ -0,0 +1,3 @@
1
+ module ActionState
2
+ VERSION = "1.0.0"
3
+ end
@@ -0,0 +1,17 @@
1
+ require "action_state/version"
2
+ require "action_state/railtie"
3
+ require "action_state/predicate"
4
+
5
+ module ActionState
6
+ extend ActiveSupport::Concern
7
+
8
+ class_methods do
9
+ def state(name, &block)
10
+ scope(name, Proc.new(&block))
11
+
12
+ define_method("#{name}?") do |*args, **kwargs|
13
+ Predicate.new(self).instance_exec(*args, **kwargs, &block).result
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :action_state do
3
+ # # Task goes here
4
+ # end
metadata ADDED
@@ -0,0 +1,68 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: action_state
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Joel Drapper
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2022-03-08 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '7.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '7.0'
27
+ description: Quickly define model state predicates and scopes at the same time.
28
+ email:
29
+ - joel@drapper.me
30
+ executables: []
31
+ extensions: []
32
+ extra_rdoc_files: []
33
+ files:
34
+ - MIT-LICENSE
35
+ - README.md
36
+ - Rakefile
37
+ - lib/action_state.rb
38
+ - lib/action_state/predicate.rb
39
+ - lib/action_state/railtie.rb
40
+ - lib/action_state/version.rb
41
+ - lib/tasks/action_state_tasks.rake
42
+ homepage: https://github.com/joeldrapper/action_state
43
+ licenses:
44
+ - MIT
45
+ metadata:
46
+ homepage_uri: https://github.com/joeldrapper/action_state
47
+ source_code_uri: https://github.com/joeldrapper/action_state
48
+ changelog_uri: https://github.com/joeldrapper/action_state
49
+ post_install_message:
50
+ rdoc_options: []
51
+ require_paths:
52
+ - lib
53
+ required_ruby_version: !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ version: '0'
58
+ required_rubygems_version: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: '0'
63
+ requirements: []
64
+ rubygems_version: 3.3.7
65
+ signing_key:
66
+ specification_version: 4
67
+ summary: Small DSL for defining model states.
68
+ test_files: []