rails-patterns 0.4.0 → 0.8.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.
data/Rakefile CHANGED
@@ -21,6 +21,7 @@ Juwelier::Tasks.new do |gem|
21
21
  gem.description = "A collection of lightweight, standardized, rails-oriented patterns."
22
22
  gem.email = "b.kosmowski@selleo.com"
23
23
  gem.authors = ["Stevo"]
24
+ gem.required_ruby_version = ">= 2.5.0"
24
25
 
25
26
  # dependencies defined in Gemfile
26
27
  end
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.4.0
1
+ 0.8.0
@@ -0,0 +1,59 @@
1
+ require 'digest'
2
+
3
+ module Patterns
4
+ class Calculation
5
+ class_attribute :cache_expiry_every
6
+
7
+ def initialize(*args)
8
+ @options = args.extract_options!
9
+ @subject = args.first
10
+ end
11
+
12
+ def self.result(*args)
13
+ new(*args).cached_result
14
+ end
15
+
16
+ class << self
17
+ alias_method :result_for, :result
18
+ alias_method :calculate, :result
19
+ end
20
+
21
+ def self.set_cache_expiry_every(period)
22
+ self.cache_expiry_every = period
23
+ end
24
+
25
+ def cached_result
26
+ if cache_expiry_period.blank?
27
+ result
28
+ else
29
+ Rails.cache.fetch(cache_key, expires_in: cache_expiry_period) do
30
+ result
31
+ end
32
+ end
33
+ end
34
+
35
+ private
36
+
37
+ attr_reader :subject, :options
38
+
39
+ def result
40
+ raise NotImplementedError
41
+ end
42
+
43
+ def cache_key
44
+ "#{self.class.name}_#{hash_of(subject, options)}"
45
+ end
46
+
47
+ def self.hash_of(*args)
48
+ Digest::SHA1.hexdigest(args.map(&:to_s).join(':'))
49
+ end
50
+
51
+ def hash_of(*args)
52
+ self.class.hash_of(*args)
53
+ end
54
+
55
+ def cache_expiry_period
56
+ self.class.cache_expiry_every
57
+ end
58
+ end
59
+ end
@@ -49,8 +49,16 @@ module Patterns
49
49
  self
50
50
  end
51
51
 
52
+ def to_param
53
+ if resource.present? && resource.respond_to?(:to_param)
54
+ resource.to_param
55
+ else
56
+ nil
57
+ end
58
+ end
59
+
52
60
  def persisted?
53
- if resource&.respond_to?(:persisted?)
61
+ if resource.present? && resource.respond_to?(:persisted?)
54
62
  resource.persisted?
55
63
  else
56
64
  false
@@ -58,9 +66,7 @@ module Patterns
58
66
  end
59
67
 
60
68
  def model_name
61
- @model_name ||= Struct.
62
- new(:param_key).
63
- new(param_key)
69
+ @model_name ||= OpenStruct.new(model_name_attributes)
64
70
  end
65
71
 
66
72
  def self.param_key(key = nil)
@@ -75,13 +81,24 @@ module Patterns
75
81
 
76
82
  attr_reader :resource, :form_owner
77
83
 
78
- def param_key
79
- param_key = self.class.param_key
80
- param_key ||= resource&.respond_to?(:model_name) && resource.model_name.param_key
81
- raise NoParamKey if param_key.blank?
82
- param_key
84
+ def model_name_attributes
85
+ if self.class.param_key.present?
86
+ {
87
+ param_key: self.class.param_key,
88
+ route_key: self.class.param_key.pluralize,
89
+ singular_route_key: self.class.param_key
90
+ }
91
+ elsif resource.present? && resource.respond_to?(:model_name)
92
+ {
93
+ param_key: resource.model_name.param_key,
94
+ route_key: resource.model_name.route_key,
95
+ singular_route_key: resource.model_name.singular_route_key
96
+ }
97
+ else
98
+ raise NoParamKey
99
+ end
83
100
  end
84
-
101
+
85
102
  def build_original_attributes
86
103
  return {} if resource.nil?
87
104
  base_attributes = resource.respond_to?(:attributes) && resource.attributes.symbolize_keys
@@ -6,17 +6,17 @@ module Patterns
6
6
 
7
7
  def initialize(*args)
8
8
  @options = args.extract_options!
9
- @relation = args.first || self.class.base_relation
9
+ @relation = args.first || base_relation
10
10
 
11
11
  if relation.nil?
12
12
  raise(
13
- RelationRequired,
14
- "Queries require a base relation defined. Use .queries method to define relation."
13
+ RelationRequired,
14
+ "Queries require a base relation defined. Use .queries method to define relation."
15
15
  )
16
16
  elsif !relation.is_a?(ActiveRecord::Relation)
17
17
  raise(
18
- RelationRequired,
19
- "Queries accept only ActiveRecord::Relation as input"
18
+ RelationRequired,
19
+ "Queries accept only ActiveRecord::Relation as input"
20
20
  )
21
21
  end
22
22
  end
@@ -29,20 +29,25 @@ module Patterns
29
29
  query.tap do |relation|
30
30
  unless relation.is_a?(ActiveRecord::Relation)
31
31
  raise(
32
- RelationRequired,
33
- "#query method should return object of ActiveRecord::Relation class"
32
+ RelationRequired,
33
+ "#query method should return object of ActiveRecord::Relation class"
34
34
  )
35
35
  end
36
36
  end
37
37
  end
38
38
 
39
39
  def self.queries(subject)
40
- self.base_relation =
41
- if subject.is_a?(ActiveRecord::Relation)
42
- subject
43
- elsif subject < ActiveRecord::Base
44
- subject.all
45
- end
40
+ self.base_relation = subject
41
+ end
42
+
43
+ def base_relation
44
+ return nil if self.class.base_relation.nil?
45
+
46
+ if self.class.base_relation.is_a?(ActiveRecord::Relation)
47
+ self.class.base_relation
48
+ elsif self.class.base_relation < ActiveRecord::Base
49
+ self.class.base_relation.all
50
+ end
46
51
  end
47
52
 
48
53
  private
@@ -55,8 +60,8 @@ module Patterns
55
60
 
56
61
  def query
57
62
  raise(
58
- NotImplementedError,
59
- "You need to implement #query method which returns ActiveRecord::Relation object"
63
+ NotImplementedError,
64
+ "You need to implement #query method which returns ActiveRecord::Relation object"
60
65
  )
61
66
  end
62
67
  end
@@ -0,0 +1,25 @@
1
+ class Rule
2
+ def initialize(subject)
3
+ @subject = subject
4
+ end
5
+
6
+ def satisfied?
7
+ raise NotImplementedError
8
+ end
9
+
10
+ def not_applicable?
11
+ false
12
+ end
13
+
14
+ def applicable?
15
+ !not_applicable?
16
+ end
17
+
18
+ def forceable?
19
+ true
20
+ end
21
+
22
+ private
23
+
24
+ attr_reader :subject
25
+ end
@@ -0,0 +1,69 @@
1
+ class Ruleset
2
+ class EmptyRuleset < StandardError; end
3
+
4
+ class << self
5
+ attr_accessor :rule_names
6
+ end
7
+
8
+ def self.rules
9
+ (rule_names || []).map do |rule_name|
10
+ rule_name.to_s.classify.constantize
11
+ end
12
+ end
13
+
14
+ def self.add_rule(rule_name)
15
+ self.rule_names ||= []
16
+ self.rule_names << rule_name.to_sym
17
+ self
18
+ end
19
+
20
+ def initialize(subject = nil)
21
+ raise EmptyRuleset if self.class.rules.empty?
22
+
23
+ @rules = self.class.rules.map { |rule| rule.new(subject) }
24
+ end
25
+
26
+ def satisfied?(force: false)
27
+ rules.all? do |rule|
28
+ rule.satisfied? ||
29
+ rule.not_applicable? ||
30
+ (force && rule.forceable?)
31
+ end
32
+ end
33
+
34
+ def not_satisfied?
35
+ !satisfied?
36
+ end
37
+
38
+ def applicable?
39
+ !not_applicable?
40
+ end
41
+
42
+ def not_applicable?
43
+ rules.all?(&:not_applicable?)
44
+ end
45
+
46
+ def forceable?
47
+ rules.all? do |rule|
48
+ rule.forceable? ||
49
+ rule.not_applicable? ||
50
+ rule.satisfied?
51
+ end
52
+ end
53
+
54
+ def each(&block)
55
+ return enum_for(:each) unless block_given?
56
+
57
+ rules.each do |rule_or_ruleset|
58
+ if rule_or_ruleset.is_a?(Ruleset)
59
+ rule_or_ruleset.each(&block)
60
+ else
61
+ yield rule_or_ruleset
62
+ end
63
+ end
64
+ end
65
+
66
+ private
67
+
68
+ attr_reader :rules
69
+ end
@@ -1,13 +1,17 @@
1
+ require 'ruby2_keywords'
2
+
1
3
  module Patterns
2
4
  class Service
3
5
  attr_reader :result
4
6
 
5
- def self.call(*args)
6
- new(*args).tap do |service|
7
- service.instance_variable_set(
8
- "@result",
9
- service.call
10
- )
7
+ class << self
8
+ ruby2_keywords def call(*args)
9
+ new(*args).tap do |service|
10
+ service.instance_variable_set(
11
+ "@result",
12
+ service.call
13
+ )
14
+ end
11
15
  end
12
16
  end
13
17
 
@@ -0,0 +1,19 @@
1
+ # StrongRuleset is not satisfied and not forceable if any of rules is not applicable
2
+
3
+ class StrongRuleset < Ruleset
4
+ def satisfied?(force: false)
5
+ rules.all? do |rule|
6
+ (rule.applicable? && rule.satisfied?) || (force && rule.forceable?)
7
+ end
8
+ end
9
+
10
+ def not_applicable?
11
+ rules.any?(&:not_applicable?)
12
+ end
13
+
14
+ def forceable?
15
+ rules.all? do |rule|
16
+ (rule.applicable? && rule.forceable?) || rule.satisfied?
17
+ end
18
+ end
19
+ end
@@ -2,4 +2,8 @@ require "patterns"
2
2
  require "patterns/query"
3
3
  require "patterns/service"
4
4
  require "patterns/collection"
5
+ require "patterns/calculation"
5
6
  require "patterns/form"
7
+ require "patterns/rule"
8
+ require "patterns/ruleset"
9
+ require "patterns/strong_ruleset"
@@ -2,16 +2,16 @@
2
2
  # DO NOT EDIT THIS FILE DIRECTLY
3
3
  # Instead, edit Juwelier::Tasks in Rakefile, and run 'rake gemspec'
4
4
  # -*- encoding: utf-8 -*-
5
- # stub: rails-patterns 0.4.0 ruby lib
5
+ # stub: rails-patterns 0.8.0 ruby lib
6
6
 
7
7
  Gem::Specification.new do |s|
8
8
  s.name = "rails-patterns".freeze
9
- s.version = "0.4.0"
9
+ s.version = "0.8.0"
10
10
 
11
11
  s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
12
12
  s.require_paths = ["lib".freeze]
13
13
  s.authors = ["Stevo".freeze]
14
- s.date = "2017-04-21"
14
+ s.date = "2020-09-01"
15
15
  s.description = "A collection of lightweight, standardized, rails-oriented patterns.".freeze
16
16
  s.email = "b.kosmowski@selleo.com".freeze
17
17
  s.extra_rdoc_files = [
@@ -20,6 +20,7 @@ Gem::Specification.new do |s|
20
20
  ]
21
21
  s.files = [
22
22
  ".document",
23
+ ".github/workflows/ruby.yml",
23
24
  ".rspec",
24
25
  "Gemfile",
25
26
  "Gemfile.lock",
@@ -28,47 +29,54 @@ Gem::Specification.new do |s|
28
29
  "Rakefile",
29
30
  "VERSION",
30
31
  "lib/patterns.rb",
32
+ "lib/patterns/calculation.rb",
31
33
  "lib/patterns/collection.rb",
32
34
  "lib/patterns/form.rb",
33
35
  "lib/patterns/query.rb",
36
+ "lib/patterns/rule.rb",
37
+ "lib/patterns/ruleset.rb",
34
38
  "lib/patterns/service.rb",
39
+ "lib/patterns/strong_ruleset.rb",
35
40
  "lib/rails-patterns.rb",
36
41
  "rails-patterns.gemspec",
42
+ "spec/helpers/custom_calculation.rb",
43
+ "spec/helpers/custom_calculation_script.rb",
44
+ "spec/helpers/rails_redis_cache_mock.rb",
45
+ "spec/patterns/calculation_spec.rb",
37
46
  "spec/patterns/collection_spec.rb",
38
47
  "spec/patterns/form_spec.rb",
39
48
  "spec/patterns/query_spec.rb",
49
+ "spec/patterns/rule_spec.rb",
50
+ "spec/patterns/ruleset_spec.rb",
40
51
  "spec/patterns/service_spec.rb",
52
+ "spec/patterns/strong_ruleset_spec.rb",
41
53
  "spec/spec_helper.rb"
42
54
  ]
43
55
  s.homepage = "http://github.com/selleo/pattern".freeze
44
56
  s.licenses = ["MIT".freeze]
45
- s.rubygems_version = "2.6.10".freeze
57
+ s.required_ruby_version = Gem::Requirement.new(">= 2.5.0".freeze)
58
+ s.rubygems_version = "3.1.2".freeze
46
59
  s.summary = "A collection of lightweight, standardized, rails-oriented patterns.".freeze
47
60
 
48
61
  if s.respond_to? :specification_version then
49
62
  s.specification_version = 4
63
+ end
50
64
 
51
- if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
52
- s.add_runtime_dependency(%q<activerecord>.freeze, [">= 4.2.6"])
53
- s.add_runtime_dependency(%q<actionpack>.freeze, [">= 4.2.6"])
54
- s.add_runtime_dependency(%q<virtus>.freeze, [">= 0"])
55
- s.add_development_dependency(%q<rspec>.freeze, [">= 0"])
56
- s.add_development_dependency(%q<bundler>.freeze, ["~> 1.0"])
57
- s.add_development_dependency(%q<juwelier>.freeze, ["~> 2.1.0"])
58
- else
59
- s.add_dependency(%q<activerecord>.freeze, [">= 4.2.6"])
60
- s.add_dependency(%q<actionpack>.freeze, [">= 4.2.6"])
61
- s.add_dependency(%q<virtus>.freeze, [">= 0"])
62
- s.add_dependency(%q<rspec>.freeze, [">= 0"])
63
- s.add_dependency(%q<bundler>.freeze, ["~> 1.0"])
64
- s.add_dependency(%q<juwelier>.freeze, ["~> 2.1.0"])
65
- end
65
+ if s.respond_to? :add_runtime_dependency then
66
+ s.add_runtime_dependency(%q<activerecord>.freeze, [">= 4.2.6"])
67
+ s.add_runtime_dependency(%q<actionpack>.freeze, [">= 4.2.6"])
68
+ s.add_runtime_dependency(%q<virtus>.freeze, [">= 0"])
69
+ s.add_runtime_dependency(%q<ruby2_keywords>.freeze, [">= 0"])
70
+ s.add_development_dependency(%q<rspec>.freeze, [">= 0"])
71
+ s.add_development_dependency(%q<bundler>.freeze, ["~> 2.0"])
72
+ s.add_development_dependency(%q<juwelier>.freeze, ["~> 2.1.0"])
66
73
  else
67
74
  s.add_dependency(%q<activerecord>.freeze, [">= 4.2.6"])
68
75
  s.add_dependency(%q<actionpack>.freeze, [">= 4.2.6"])
69
76
  s.add_dependency(%q<virtus>.freeze, [">= 0"])
77
+ s.add_dependency(%q<ruby2_keywords>.freeze, [">= 0"])
70
78
  s.add_dependency(%q<rspec>.freeze, [">= 0"])
71
- s.add_dependency(%q<bundler>.freeze, ["~> 1.0"])
79
+ s.add_dependency(%q<bundler>.freeze, ["~> 2.0"])
72
80
  s.add_dependency(%q<juwelier>.freeze, ["~> 2.1.0"])
73
81
  end
74
82
  end