law 0.1.0 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +12 -3
- data/lib/law/judgement.rb +58 -0
- data/lib/law/law_base.rb +19 -0
- data/lib/law/laws/actions.rb +43 -0
- data/lib/law/laws/judgements.rb +36 -0
- data/lib/law/laws/petitions.rb +27 -0
- data/lib/law/laws/statutes.rb +36 -0
- data/lib/law/legalize.rb +51 -0
- data/lib/law/permission_list.rb +7 -0
- data/lib/law/petition.rb +22 -0
- data/lib/law/regulation_base.rb +16 -0
- data/lib/law/regulations/core.rb +73 -0
- data/lib/law/regulations/statutes.rb +25 -0
- data/lib/law/rspec/custom_matchers/be_default_for.rb +31 -0
- data/lib/law/rspec/custom_matchers/be_enforced_by.rb +30 -0
- data/lib/law/rspec/custom_matchers/be_imposed_by.rb +29 -0
- data/lib/law/rspec/custom_matchers/define_action.rb +36 -0
- data/lib/law/rspec/custom_matchers/have_default_statute.rb +27 -0
- data/lib/law/rspec/custom_matchers/impose_regulations.rb +29 -0
- data/lib/law/rspec/custom_matchers.rb +8 -0
- data/lib/law/rspec/shoulda_matcher_helper.rb +7 -0
- data/lib/law/spec_helper.rb +4 -0
- data/lib/law/statute_base.rb +12 -0
- data/lib/law/statutes/laws.rb +25 -0
- data/lib/law/statutes/regulations.rb +50 -0
- data/lib/law/version.rb +3 -1
- data/lib/law.rb +25 -1
- metadata +157 -22
- data/.gitignore +0 -11
- data/.rspec +0 -3
- data/.travis.yml +0 -7
- data/Gemfile +0 -4
- data/Rakefile +0 -6
- data/bin/console +0 -14
- data/bin/setup +0 -8
- data/law.gemspec +0 -29
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3492ad53cbc6aec4593a8992e9e3725350306057ddfcde3e557e0f06e6a0b1d0
|
4
|
+
data.tar.gz: 7dfdf907d2bb6a0d87d31ce4b29b4f303435d969f56aa0729622e67193682d05
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b646db6d863c688f091b2b78999a34be85c78895b4d79259e9f01d17fba9aaf4424e24b999834a6d61d99b38d3c7a4d9867d8de64b2eb4b2ad7cb82c6c86cfb9
|
7
|
+
data.tar.gz: 0472a5af9b1ed53608bc96badeaafbe090897504c5d309bdaf83ee2341bab2f566998c054bd4e0651c002fd5db7c77309b0f9edbebfa8cb634949e9f297da9e2
|
data/README.md
CHANGED
@@ -1,8 +1,17 @@
|
|
1
1
|
# Law
|
2
2
|
|
3
|
-
|
3
|
+
Enforce the laws of your Rails application with highly extensible access policies.
|
4
4
|
|
5
|
-
|
5
|
+
[![Gem Version](https://badge.fury.io/rb/law.svg)](https://badge.fury.io/rb/law)
|
6
|
+
[![Build Status](https://semaphoreci.com/api/v1/freshly/law/branches/develop/badge.svg)](https://semaphoreci.com/freshly/law)
|
7
|
+
[![Maintainability](https://api.codeclimate.com/v1/badges/c5667b201773ea79ff5e/maintainability)](https://codeclimate.com/github/Freshly/law/maintainability)
|
8
|
+
[![Test Coverage](https://api.codeclimate.com/v1/badges/c5667b201773ea79ff5e/test_coverage)](https://codeclimate.com/github/Freshly/law/test_coverage)
|
9
|
+
|
10
|
+
* [Installation](#installation)
|
11
|
+
* [Usage](#usage)
|
12
|
+
* [Development](#development)
|
13
|
+
* [Contributing](#contributing)
|
14
|
+
* [License](#license)
|
6
15
|
|
7
16
|
## Installation
|
8
17
|
|
@@ -32,7 +41,7 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
|
|
32
41
|
|
33
42
|
## Contributing
|
34
43
|
|
35
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/
|
44
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/Freshly/law.
|
36
45
|
|
37
46
|
## License
|
38
47
|
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# A **Judgement** determines if an **Actor** (represented by their **Roles**) are in violation of the given **Statute**.
|
4
|
+
module Law
|
5
|
+
class Judgement < Spicerack::RootObject
|
6
|
+
class << self
|
7
|
+
def judge!(petition)
|
8
|
+
new(petition).judge!
|
9
|
+
end
|
10
|
+
|
11
|
+
def judge(petition)
|
12
|
+
new(petition).judge
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
attr_reader :petition, :violations, :applied_regulations
|
17
|
+
|
18
|
+
delegate :statute, :applicable_regulations, to: :petition
|
19
|
+
|
20
|
+
def initialize(petition)
|
21
|
+
@petition = petition
|
22
|
+
@violations = []
|
23
|
+
@applied_regulations = []
|
24
|
+
end
|
25
|
+
|
26
|
+
def adjudicated?
|
27
|
+
applied_regulations.present?
|
28
|
+
end
|
29
|
+
|
30
|
+
def authorized?
|
31
|
+
violations.blank?
|
32
|
+
end
|
33
|
+
|
34
|
+
def judge
|
35
|
+
judge!
|
36
|
+
rescue NotAuthorizedError => exception
|
37
|
+
error :not_authorized, exception: exception
|
38
|
+
false
|
39
|
+
end
|
40
|
+
|
41
|
+
def judge!
|
42
|
+
if statute.unregulated?
|
43
|
+
@applied_regulations = [ nil ]
|
44
|
+
return true
|
45
|
+
end
|
46
|
+
|
47
|
+
raise InjunctionError if applicable_regulations.blank?
|
48
|
+
raise AlreadyJudgedError if adjudicated?
|
49
|
+
|
50
|
+
@applied_regulations = applicable_regulations.map { |regulation| regulation.new(petition: petition) }
|
51
|
+
@violations = applied_regulations.reject(&:valid?)
|
52
|
+
|
53
|
+
raise NotAuthorizedError unless authorized?
|
54
|
+
|
55
|
+
true
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
data/lib/law/law_base.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "laws/actions"
|
4
|
+
require_relative "laws/statutes"
|
5
|
+
require_relative "laws/petitions"
|
6
|
+
require_relative "laws/judgements"
|
7
|
+
|
8
|
+
# A **Law** defines which **Statutes** are enforced against specifics **Actions**.
|
9
|
+
module Law
|
10
|
+
class LawBase < Spicerack::InputObject
|
11
|
+
include Conjunction::Junction
|
12
|
+
suffixed_with "Law"
|
13
|
+
|
14
|
+
include Law::Laws::Judgements
|
15
|
+
include Law::Laws::Actions
|
16
|
+
include Law::Laws::Statutes
|
17
|
+
include Law::Laws::Petitions
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# A **Law** enforces various **Statutes** to restrict **Actions**.
|
4
|
+
module Law
|
5
|
+
module Laws
|
6
|
+
module Actions
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
|
9
|
+
included do
|
10
|
+
class_attribute :actions, instance_writer: false, default: {}
|
11
|
+
|
12
|
+
delegate :statute_for_action?, to: :class
|
13
|
+
end
|
14
|
+
|
15
|
+
class_methods do
|
16
|
+
def inherited(base)
|
17
|
+
base.actions = actions.dup
|
18
|
+
super
|
19
|
+
end
|
20
|
+
|
21
|
+
def statute_for_action?(action)
|
22
|
+
actions[action].present?
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def define_action(*input_actions, enforces: nil)
|
28
|
+
raise ArgumentError, "invalid statute: #{enforces}" unless enforceable?(enforces)
|
29
|
+
|
30
|
+
input_actions.each do |action|
|
31
|
+
actions[action] = enforces
|
32
|
+
enforces.try(:enforced_by, self, action)
|
33
|
+
define_judgement_predicates_for_action(action)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def enforceable?(enforces)
|
38
|
+
enforces.nil? || enforces.respond_to?(:enforced_by)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# A **Law** is used to make a **Judgement** about whether an **Action** is authorized.
|
4
|
+
module Law
|
5
|
+
module Laws
|
6
|
+
module Judgements
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
|
9
|
+
class_methods do
|
10
|
+
private
|
11
|
+
|
12
|
+
def define_judgement_predicates_for_action(action)
|
13
|
+
method_name = "#{action}?".to_sym
|
14
|
+
define_method(method_name) { authorized?(action) }
|
15
|
+
define_singleton_method(method_name) { |**options| new(**options).public_send(method_name) }
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def judgement(action)
|
20
|
+
Law::Judgement.new(petition_for_action(action))
|
21
|
+
end
|
22
|
+
|
23
|
+
def authorize(action)
|
24
|
+
judgement(action).tap(&:judge)
|
25
|
+
end
|
26
|
+
|
27
|
+
def authorize!(action)
|
28
|
+
judgement(action).tap(&:judge!)
|
29
|
+
end
|
30
|
+
|
31
|
+
def authorized?(action)
|
32
|
+
judgement(action).judge
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# A **Law** creates a **Petition** for the **Statute** enforced against an **Action**; all other data must be specified.
|
4
|
+
module Law
|
5
|
+
module Laws
|
6
|
+
module Petitions
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
|
9
|
+
included do
|
10
|
+
option :source
|
11
|
+
option :permissions, default: []
|
12
|
+
option :target
|
13
|
+
option :params, default: {}
|
14
|
+
end
|
15
|
+
|
16
|
+
def petition_for_action(action)
|
17
|
+
Law::Petition.new(
|
18
|
+
statute: actions[action] || _default_statute,
|
19
|
+
source: source,
|
20
|
+
permissions: permissions,
|
21
|
+
target: target,
|
22
|
+
params: params,
|
23
|
+
)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# A **Law** can have a default **Statute** to apply against **Actions** which do not otherwise specify.
|
4
|
+
module Law
|
5
|
+
module Laws
|
6
|
+
module Statutes
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
|
9
|
+
included do
|
10
|
+
delegate :_default_statute, :default_statute?, to: :class
|
11
|
+
end
|
12
|
+
|
13
|
+
class_methods do
|
14
|
+
attr_reader :_default_statute
|
15
|
+
|
16
|
+
def inherited(base)
|
17
|
+
base.default_statute(_default_statute) if default_statute?
|
18
|
+
super
|
19
|
+
end
|
20
|
+
|
21
|
+
def default_statute?
|
22
|
+
_default_statute.present?
|
23
|
+
end
|
24
|
+
|
25
|
+
protected
|
26
|
+
|
27
|
+
def default_statute(statute)
|
28
|
+
raise ArgumentError, "invalid statute: #{enforces}" unless statute.respond_to?(:enforced_by)
|
29
|
+
|
30
|
+
@_default_statute = statute
|
31
|
+
statute.enforced_by(self, :__default__)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
data/lib/law/legalize.rb
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# To make something permissible by the enforcement of **Laws**.
|
4
|
+
module Law
|
5
|
+
module Legalize
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
included do
|
9
|
+
attr_reader :judgement
|
10
|
+
helper_method :law if respond_to?(:helper_method)
|
11
|
+
end
|
12
|
+
|
13
|
+
def authorized?
|
14
|
+
judgement.try(:authorized?) || false
|
15
|
+
end
|
16
|
+
|
17
|
+
def adjudicated?
|
18
|
+
judgement.try(:adjudicated?) || false
|
19
|
+
end
|
20
|
+
|
21
|
+
def violations
|
22
|
+
judgement.try(:violations) || []
|
23
|
+
end
|
24
|
+
|
25
|
+
def law(object = nil, petitioner = nil, permissions: nil, parameters: nil, law_class: nil)
|
26
|
+
object ||= try(:controller_name)&.singularize&.camelize&.safe_constantize
|
27
|
+
petitioner ||= try(:current_user)
|
28
|
+
permissions ||= petitioner.try(:permissions)
|
29
|
+
law_class ||= object.try(:conjugate, Law::LawBase)
|
30
|
+
|
31
|
+
raise ArgumentError, "a Law is required" unless law_class.is_a?(Class)
|
32
|
+
|
33
|
+
law_class.new(permissions: permissions, source: petitioner, target: object, params: parameters)
|
34
|
+
end
|
35
|
+
|
36
|
+
def authorize!(**options)
|
37
|
+
authorize(**options) or raise Law::NotAuthorizedError
|
38
|
+
end
|
39
|
+
|
40
|
+
def authorize(action = nil, object: nil, petitioner: nil, permissions: nil, parameters: nil, law_class: nil)
|
41
|
+
action ||= try(:action_name)
|
42
|
+
parameters ||= try(:params)
|
43
|
+
|
44
|
+
raise ArgumentError, "an action is required" if action.nil?
|
45
|
+
|
46
|
+
options = { permissions: permissions, parameters: parameters, law_class: law_class }
|
47
|
+
@judgement = law(object, petitioner, **options).authorize(action)
|
48
|
+
authorized?
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
data/lib/law/petition.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# A **Petition** is used to make a **Judgement** determining if an action would violate a given **Statute**.
|
4
|
+
module Law
|
5
|
+
class Petition < Spicerack::InputObject
|
6
|
+
argument :statute, allow_nil: false
|
7
|
+
option :source
|
8
|
+
option :permissions, default: []
|
9
|
+
option :target
|
10
|
+
option :params, default: {}
|
11
|
+
|
12
|
+
def permission_list
|
13
|
+
Law::PermissionList.new(Array.wrap(permissions).flatten.compact)
|
14
|
+
end
|
15
|
+
memoize :permission_list
|
16
|
+
|
17
|
+
def applicable_regulations
|
18
|
+
statute.regulations.select { |regulation| permission_list.include? regulation.key }
|
19
|
+
end
|
20
|
+
memoize :applicable_regulations
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "regulations/statutes"
|
4
|
+
require_relative "regulations/core"
|
5
|
+
|
6
|
+
# A **Regulation** is the "lock" which has a matching **Permission** "key".
|
7
|
+
module Law
|
8
|
+
class RegulationBase < Spicerack::InputModel
|
9
|
+
include Regulations::Statutes
|
10
|
+
include Regulations::Core
|
11
|
+
|
12
|
+
def self.key
|
13
|
+
name.chomp("Regulation").underscore.to_sym
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# A **Regulation** accepts a **Petition** and applies **Validations** to ensure the request is acceptable.
|
4
|
+
module Law
|
5
|
+
module Regulations
|
6
|
+
module Core
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
|
9
|
+
included do
|
10
|
+
argument :petition
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def respond_to_missing?(method_name, include_private = false)
|
16
|
+
petition_delegate, delegated_method_name, is_setter = petition_delegator_for_method_name(method_name)
|
17
|
+
delegation_type(petition_delegate, delegated_method_name, is_setter) != :none || super
|
18
|
+
end
|
19
|
+
|
20
|
+
def method_missing(method_name, *arguments)
|
21
|
+
petition_delegate, delegated_method_name, is_setter = petition_delegator_for_method_name(method_name)
|
22
|
+
|
23
|
+
case delegation_type(petition_delegate, delegated_method_name, is_setter)
|
24
|
+
when :delegate
|
25
|
+
petition_delegate
|
26
|
+
when :method
|
27
|
+
petition_delegate.public_send(delegated_method_name, *arguments)
|
28
|
+
when :attribute_set
|
29
|
+
petition_delegate[delegated_method_name] = arguments.first
|
30
|
+
when :attribute_sym
|
31
|
+
petition_delegate[delegated_method_name]
|
32
|
+
when :attribute_str
|
33
|
+
petition_delegate[delegated_method_name.to_s]
|
34
|
+
else
|
35
|
+
super
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# rubocop:disable Metrics/PerceivedComplexity
|
40
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
41
|
+
def delegation_type(petition_delegate, delegated_method_name, is_setter)
|
42
|
+
unless petition_delegate.nil?
|
43
|
+
return :delegate if delegated_method_name.blank? && !is_setter
|
44
|
+
|
45
|
+
method_name_to_test = is_setter ? "#{delegated_method_name}=".to_sym : delegated_method_name
|
46
|
+
return :method if petition_delegate.respond_to?(method_name_to_test)
|
47
|
+
|
48
|
+
if petition_delegate.respond_to?(:key?)
|
49
|
+
return :attribute_set if is_setter
|
50
|
+
return :attribute_sym if petition_delegate.key?(delegated_method_name)
|
51
|
+
return :attribute_str if petition_delegate.key?(delegated_method_name.to_s)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
:none
|
56
|
+
end
|
57
|
+
# rubocop:enable Metrics/CyclomaticComplexity
|
58
|
+
# rubocop:enable Metrics/PerceivedComplexity
|
59
|
+
|
60
|
+
def petition_delegator_for_method_name(method_name)
|
61
|
+
parts = method_name.to_s.split("_")
|
62
|
+
petition_delegate = parts.shift.to_sym
|
63
|
+
|
64
|
+
return if petition_delegate.nil? || !petition.respond_to?(petition_delegate)
|
65
|
+
|
66
|
+
method_name = parts.join("_")
|
67
|
+
is_setter = method_name.delete_suffix!("=").present?
|
68
|
+
|
69
|
+
[ petition.public_send(petition_delegate), method_name.to_sym, is_setter ]
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# A **Regulation** is imposed by various **Statutes** to restrict **Actors** from perform actions.
|
4
|
+
module Law
|
5
|
+
module Regulations
|
6
|
+
module Statutes
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
|
9
|
+
included do
|
10
|
+
class_attribute :statutes, instance_writer: false, default: []
|
11
|
+
end
|
12
|
+
|
13
|
+
class_methods do
|
14
|
+
def inherited(base)
|
15
|
+
base.statutes = []
|
16
|
+
super
|
17
|
+
end
|
18
|
+
|
19
|
+
def imposed_by(statute)
|
20
|
+
statutes << statute
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# RSpec matcher that tests references of statutes as defaults in laws.
|
4
|
+
#
|
5
|
+
# class ExampleStatute < ApplicationStatute
|
6
|
+
# end
|
7
|
+
#
|
8
|
+
# class ExampleLaw < ApplicationStatute
|
9
|
+
# default_statute ExampleStatute
|
10
|
+
# end
|
11
|
+
#
|
12
|
+
# RSpec.describe ExampleStatute, type: :statute do
|
13
|
+
# it { is_expected.to be_default_for ExampleLaw }
|
14
|
+
# end
|
15
|
+
|
16
|
+
RSpec::Matchers.define :be_default_for do |*laws|
|
17
|
+
match do
|
18
|
+
laws.each { |law| expect(test_subject.laws).to include(law => a_collection_including(:__default__)) }
|
19
|
+
end
|
20
|
+
description { "be default for #{Array.wrap(laws).join(", ")}" }
|
21
|
+
failure_message do
|
22
|
+
"expected #{test_subject} to be default for #{Array.wrap(laws).join(", ")}; #{test_subject.laws}"
|
23
|
+
end
|
24
|
+
failure_message_when_negated do
|
25
|
+
"expected #{test_subject} not to be default for #{Array.wrap(laws).join(", ")}; #{test_subject.laws}"
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_subject
|
29
|
+
subject.is_a?(Class) ? subject : subject.class
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# RSpec matcher that tests references of statutes in laws.
|
4
|
+
#
|
5
|
+
# class ExampleStatute < ApplicationStatute
|
6
|
+
# end
|
7
|
+
#
|
8
|
+
# class ExampleLaw < ApplicationStatute
|
9
|
+
# define_action :new, enforces: ExampleRegulation
|
10
|
+
# define_action :create, enforces: ExampleRegulation
|
11
|
+
# end
|
12
|
+
#
|
13
|
+
# RSpec.describe ExampleStatute, type: :statute do
|
14
|
+
# it { is_expected.to be_enforced_by ExampleLaw, :new, :create }
|
15
|
+
# end
|
16
|
+
|
17
|
+
RSpec::Matchers.define :be_enforced_by do |statute, *methods|
|
18
|
+
match { expect(test_subject.laws).to include(statute => a_collection_including(*methods.flatten)) }
|
19
|
+
description { "be enforced by #{statute} on #{methods.flatten.join(", ")}" }
|
20
|
+
failure_message do
|
21
|
+
"expected #{test_subject} to be enforced by #{statute} on #{methods.flatten.join(", ")}; #{test_subject.laws}"
|
22
|
+
end
|
23
|
+
failure_message_when_negated do
|
24
|
+
"expected #{test_subject} not to be enforced by #{statute} on #{methods.flatten.join(", ")}; #{test_subject.laws}"
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_subject
|
28
|
+
subject.is_a?(Class) ? subject : subject.class
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# RSpec matcher that tests references of statutes to regulations.
|
4
|
+
#
|
5
|
+
# class ExampleRegulation < ApplicationRegulation
|
6
|
+
# end
|
7
|
+
#
|
8
|
+
# class ExampleStatute < ApplicationStatute
|
9
|
+
# impose ExampleRegulation
|
10
|
+
# end
|
11
|
+
#
|
12
|
+
# RSpec.describe ExampleRegulation, type: :regulation do
|
13
|
+
# it { is_expected.to be_imposed_by ExampleStatute }
|
14
|
+
# end
|
15
|
+
|
16
|
+
RSpec::Matchers.define :be_imposed_by do |*statutes|
|
17
|
+
match { expect(test_subject.statutes).to include *Array.wrap(statutes).flatten }
|
18
|
+
description { "be imposed by #{Array.wrap(statutes).flatten}" }
|
19
|
+
failure_message do
|
20
|
+
"expected #{test_subject} to be imposed by #{Array.wrap(statutes).flatten}; #{test_subject.statutes}"
|
21
|
+
end
|
22
|
+
failure_message_when_negated do
|
23
|
+
"expected #{test_subject} not to be imposed by #{Array.wrap(statutes).flatten}; #{test_subject.statutes}"
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_subject
|
27
|
+
subject.is_a?(Class) ? subject : subject.class
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# RSpec matcher that tests references of statutes in laws.
|
4
|
+
#
|
5
|
+
# class ExampleStatute < ApplicationStatute
|
6
|
+
# end
|
7
|
+
#
|
8
|
+
# class ExampleLaw < ApplicationStatute
|
9
|
+
# define_action :new, enforces: ExampleRegulation
|
10
|
+
# define_action :create, enforces: ExampleRegulation
|
11
|
+
# define_action :edit
|
12
|
+
# end
|
13
|
+
#
|
14
|
+
# RSpec.describe ExampleLaw, type: :law do
|
15
|
+
# it { is_expected.to define_action :new, with_statute: ExampleRegulation }
|
16
|
+
# it { is_expected.to define_action :edit, with_statute: :default }
|
17
|
+
# end
|
18
|
+
|
19
|
+
RSpec::Matchers.define :define_action do |action, with_statute:|
|
20
|
+
match do
|
21
|
+
for_default = with_statute == :default
|
22
|
+
expect(test_subject.actions[action]).to eq with_statute unless for_default
|
23
|
+
expect(test_subject.statute_for_action?(action)).to eq !for_default
|
24
|
+
end
|
25
|
+
description { "define action #{action} with statute #{with_statute}" }
|
26
|
+
failure_message do
|
27
|
+
"expected #{test_subject} to define action #{action}, statute: #{with_statute}; #{test_subject.actions[action]}"
|
28
|
+
end
|
29
|
+
failure_message_when_negated do
|
30
|
+
"expected #{test_subject} not to define action #{action}, statute: #{with_statute}; #{test_subject.actions[action]}"
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_subject
|
34
|
+
subject.is_a?(Class) ? subject : subject.class
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# RSpec matcher that tests references of statutes as defaults in laws.
|
4
|
+
#
|
5
|
+
# class ExampleStatute < ApplicationStatute
|
6
|
+
# end
|
7
|
+
#
|
8
|
+
# class ExampleLaw < ApplicationStatute
|
9
|
+
# default_statute ExampleStatute
|
10
|
+
# end
|
11
|
+
#
|
12
|
+
# RSpec.describe ExampleLaw, type: :law do
|
13
|
+
# it { is_expected.to have_default_statute ExampleStatute }
|
14
|
+
# end
|
15
|
+
|
16
|
+
RSpec::Matchers.define :have_default_statute do |statute|
|
17
|
+
match { expect(test_subject._default_statute).to eq statute }
|
18
|
+
description { "have default statute #{statute}" }
|
19
|
+
failure_message { "expected #{test_subject} to have default statute #{statute}; #{test_subject._default_statute}" }
|
20
|
+
failure_message_when_negated do
|
21
|
+
"expected #{test_subject} not to have default statute #{statute}; #{test_subject._default_statute}"
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_subject
|
25
|
+
subject.is_a?(Class) ? subject : subject.class
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# RSpec matcher that tests imposing of regulations by statutes.
|
4
|
+
#
|
5
|
+
# class ExampleRegulation < ApplicationRegulation
|
6
|
+
# end
|
7
|
+
#
|
8
|
+
# class ExampleStatute < ApplicationStatute
|
9
|
+
# impose ExampleRegulation
|
10
|
+
# end
|
11
|
+
#
|
12
|
+
# RSpec.describe ExampleStatute, type: :statute do
|
13
|
+
# it { is_expected.to impose_regulations ExampleRegulation }
|
14
|
+
# end
|
15
|
+
|
16
|
+
RSpec::Matchers.define :impose_regulations do |*regulations|
|
17
|
+
match { expect(test_subject.regulations).to include *Array.wrap(regulations).flatten }
|
18
|
+
description { "impose regulations #{Array.wrap(regulations).flatten}" }
|
19
|
+
failure_message do
|
20
|
+
"expected #{test_subject} to impose regulations #{Array.wrap(regulations).flatten}; #{test_subject.regulations}"
|
21
|
+
end
|
22
|
+
failure_message_when_negated do
|
23
|
+
"expected #{test_subject} not to impose regulations #{Array.wrap(regulations).flatten}; #{test_subject.regulations}"
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_subject
|
27
|
+
subject.is_a?(Class) ? subject : subject.class
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,8 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "custom_matchers/be_default_for"
|
4
|
+
require_relative "custom_matchers/be_enforced_by"
|
5
|
+
require_relative "custom_matchers/be_imposed_by"
|
6
|
+
require_relative "custom_matchers/define_action"
|
7
|
+
require_relative "custom_matchers/have_default_statute"
|
8
|
+
require_relative "custom_matchers/impose_regulations"
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "statutes/regulations"
|
4
|
+
require_relative "statutes/laws"
|
5
|
+
|
6
|
+
# A **Statute** restricts actions to **Actors** by enforcing a collection of **Permissions**.
|
7
|
+
module Law
|
8
|
+
class StatuteBase < Spicerack::RootObject
|
9
|
+
include Law::Statutes::Regulations
|
10
|
+
include Law::Statutes::Laws
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# A **Statute** is enforced by **Laws**.
|
4
|
+
module Law
|
5
|
+
module Statutes
|
6
|
+
module Laws
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
|
9
|
+
included do
|
10
|
+
class_attribute :laws, instance_writer: false, default: Hash.new { |hash, key| hash[key] = [] }
|
11
|
+
end
|
12
|
+
|
13
|
+
class_methods do
|
14
|
+
def inherited(base)
|
15
|
+
base.laws = Hash.new { |hash, key| hash[key] = [] }
|
16
|
+
super
|
17
|
+
end
|
18
|
+
|
19
|
+
def enforced_by(law, action)
|
20
|
+
laws[law] << action
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# A **Statute** is a collection of imposed **Regulations**.
|
4
|
+
module Law
|
5
|
+
module Statutes
|
6
|
+
module Regulations
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
|
9
|
+
included do
|
10
|
+
class_attribute :regulations, instance_writer: false, default: []
|
11
|
+
delegate :unregulated?, to: :class
|
12
|
+
end
|
13
|
+
|
14
|
+
class_methods do
|
15
|
+
def unregulated?
|
16
|
+
regulations.empty?
|
17
|
+
end
|
18
|
+
|
19
|
+
def inherited(base)
|
20
|
+
base.regulations = regulations.dup
|
21
|
+
super
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def impose(*imposed_regulations)
|
27
|
+
imposed_regulations = imposed_regulations.flatten.compact
|
28
|
+
|
29
|
+
ensure_valid_regulations(imposed_regulations)
|
30
|
+
|
31
|
+
imposed_regulations.each do |regulation|
|
32
|
+
regulations << regulation
|
33
|
+
track_regulation(regulation)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def track_regulation(regulation)
|
38
|
+
regulation.imposed_by(self)
|
39
|
+
end
|
40
|
+
|
41
|
+
def ensure_valid_regulations(imposed_regulations)
|
42
|
+
raise ArgumentError, "a regulation is required" if imposed_regulations.empty?
|
43
|
+
|
44
|
+
invalid_regulations = imposed_regulations.reject { |permission| permission.respond_to?(:imposed_by) }
|
45
|
+
raise ArgumentError, "invalid regulations: #{invalid_regulations.join(", ")}" if invalid_regulations.present?
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
data/lib/law/version.rb
CHANGED
data/lib/law.rb
CHANGED
@@ -1,6 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support"
|
4
|
+
require "active_support/core_ext/enumerable"
|
5
|
+
|
6
|
+
require "spicery"
|
7
|
+
|
1
8
|
require "law/version"
|
2
9
|
|
10
|
+
require "law/regulation_base"
|
11
|
+
|
12
|
+
require "law/permission_list"
|
13
|
+
|
14
|
+
require "law/statute_base"
|
15
|
+
|
16
|
+
require "law/petition"
|
17
|
+
require "law/judgement"
|
18
|
+
|
19
|
+
require "law/law_base"
|
20
|
+
|
21
|
+
require "law/legalize"
|
22
|
+
|
3
23
|
module Law
|
4
24
|
class Error < StandardError; end
|
5
|
-
|
25
|
+
|
26
|
+
class AlreadyJudgedError < Error; end
|
27
|
+
|
28
|
+
class NotAuthorizedError < Error; end
|
29
|
+
class InjunctionError < NotAuthorizedError; end
|
6
30
|
end
|
metadata
CHANGED
@@ -1,29 +1,77 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: law
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Eric Garside
|
8
8
|
autorequire:
|
9
|
-
bindir:
|
9
|
+
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-02-07 00:00:00.000000000 Z
|
12
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.2.1
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 5.2.1
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: spicery
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 0.22.3.1
|
34
|
+
- - "<"
|
35
|
+
- !ruby/object:Gem::Version
|
36
|
+
version: '1.0'
|
37
|
+
type: :runtime
|
38
|
+
prerelease: false
|
39
|
+
version_requirements: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: 0.22.3.1
|
44
|
+
- - "<"
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '1.0'
|
13
47
|
- !ruby/object:Gem::Dependency
|
14
48
|
name: bundler
|
15
49
|
requirement: !ruby/object:Gem::Requirement
|
16
50
|
requirements:
|
17
51
|
- - "~>"
|
18
52
|
- !ruby/object:Gem::Version
|
19
|
-
version:
|
53
|
+
version: 2.0.1
|
20
54
|
type: :development
|
21
55
|
prerelease: false
|
22
56
|
version_requirements: !ruby/object:Gem::Requirement
|
23
57
|
requirements:
|
24
58
|
- - "~>"
|
25
59
|
- !ruby/object:Gem::Version
|
26
|
-
version:
|
60
|
+
version: 2.0.1
|
61
|
+
- !ruby/object:Gem::Dependency
|
62
|
+
name: pry-byebug
|
63
|
+
requirement: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - ">="
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: 3.7.0
|
68
|
+
type: :development
|
69
|
+
prerelease: false
|
70
|
+
version_requirements: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - ">="
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: 3.7.0
|
27
75
|
- !ruby/object:Gem::Dependency
|
28
76
|
name: rake
|
29
77
|
requirement: !ruby/object:Gem::Requirement
|
@@ -39,42 +87,130 @@ dependencies:
|
|
39
87
|
- !ruby/object:Gem::Version
|
40
88
|
version: '10.0'
|
41
89
|
- !ruby/object:Gem::Dependency
|
42
|
-
name:
|
90
|
+
name: simplecov
|
43
91
|
requirement: !ruby/object:Gem::Requirement
|
44
92
|
requirements:
|
45
93
|
- - "~>"
|
46
94
|
- !ruby/object:Gem::Version
|
47
|
-
version: '
|
95
|
+
version: '0.16'
|
48
96
|
type: :development
|
49
97
|
prerelease: false
|
50
98
|
version_requirements: !ruby/object:Gem::Requirement
|
51
99
|
requirements:
|
52
100
|
- - "~>"
|
53
101
|
- !ruby/object:Gem::Version
|
54
|
-
version: '
|
55
|
-
|
102
|
+
version: '0.16'
|
103
|
+
- !ruby/object:Gem::Dependency
|
104
|
+
name: timecop
|
105
|
+
requirement: !ruby/object:Gem::Requirement
|
106
|
+
requirements:
|
107
|
+
- - ">="
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: 0.9.1
|
110
|
+
type: :development
|
111
|
+
prerelease: false
|
112
|
+
version_requirements: !ruby/object:Gem::Requirement
|
113
|
+
requirements:
|
114
|
+
- - ">="
|
115
|
+
- !ruby/object:Gem::Version
|
116
|
+
version: 0.9.1
|
117
|
+
- !ruby/object:Gem::Dependency
|
118
|
+
name: shoulda-matchers
|
119
|
+
requirement: !ruby/object:Gem::Requirement
|
120
|
+
requirements:
|
121
|
+
- - '='
|
122
|
+
- !ruby/object:Gem::Version
|
123
|
+
version: 4.0.1
|
124
|
+
type: :development
|
125
|
+
prerelease: false
|
126
|
+
version_requirements: !ruby/object:Gem::Requirement
|
127
|
+
requirements:
|
128
|
+
- - '='
|
129
|
+
- !ruby/object:Gem::Version
|
130
|
+
version: 4.0.1
|
131
|
+
- !ruby/object:Gem::Dependency
|
132
|
+
name: rspice
|
133
|
+
requirement: !ruby/object:Gem::Requirement
|
134
|
+
requirements:
|
135
|
+
- - ">="
|
136
|
+
- !ruby/object:Gem::Version
|
137
|
+
version: 0.22.3.1
|
138
|
+
- - "<"
|
139
|
+
- !ruby/object:Gem::Version
|
140
|
+
version: '1.0'
|
141
|
+
type: :development
|
142
|
+
prerelease: false
|
143
|
+
version_requirements: !ruby/object:Gem::Requirement
|
144
|
+
requirements:
|
145
|
+
- - ">="
|
146
|
+
- !ruby/object:Gem::Version
|
147
|
+
version: 0.22.3.1
|
148
|
+
- - "<"
|
149
|
+
- !ruby/object:Gem::Version
|
150
|
+
version: '1.0'
|
151
|
+
- !ruby/object:Gem::Dependency
|
152
|
+
name: spicerack-styleguide
|
153
|
+
requirement: !ruby/object:Gem::Requirement
|
154
|
+
requirements:
|
155
|
+
- - ">="
|
156
|
+
- !ruby/object:Gem::Version
|
157
|
+
version: 0.22.3.1
|
158
|
+
- - "<"
|
159
|
+
- !ruby/object:Gem::Version
|
160
|
+
version: '1.0'
|
161
|
+
type: :development
|
162
|
+
prerelease: false
|
163
|
+
version_requirements: !ruby/object:Gem::Requirement
|
164
|
+
requirements:
|
165
|
+
- - ">="
|
166
|
+
- !ruby/object:Gem::Version
|
167
|
+
version: 0.22.3.1
|
168
|
+
- - "<"
|
169
|
+
- !ruby/object:Gem::Version
|
170
|
+
version: '1.0'
|
171
|
+
description: Enforce the laws of your Rails application with highly extensible access
|
172
|
+
policies
|
56
173
|
email:
|
57
174
|
- garside@gmail.com
|
58
175
|
executables: []
|
59
176
|
extensions: []
|
60
177
|
extra_rdoc_files: []
|
61
178
|
files:
|
62
|
-
- ".gitignore"
|
63
|
-
- ".rspec"
|
64
|
-
- ".travis.yml"
|
65
|
-
- Gemfile
|
66
179
|
- LICENSE.txt
|
67
180
|
- README.md
|
68
|
-
- Rakefile
|
69
|
-
- bin/console
|
70
|
-
- bin/setup
|
71
|
-
- law.gemspec
|
72
181
|
- lib/law.rb
|
182
|
+
- lib/law/judgement.rb
|
183
|
+
- lib/law/law_base.rb
|
184
|
+
- lib/law/laws/actions.rb
|
185
|
+
- lib/law/laws/judgements.rb
|
186
|
+
- lib/law/laws/petitions.rb
|
187
|
+
- lib/law/laws/statutes.rb
|
188
|
+
- lib/law/legalize.rb
|
189
|
+
- lib/law/permission_list.rb
|
190
|
+
- lib/law/petition.rb
|
191
|
+
- lib/law/regulation_base.rb
|
192
|
+
- lib/law/regulations/core.rb
|
193
|
+
- lib/law/regulations/statutes.rb
|
194
|
+
- lib/law/rspec/custom_matchers.rb
|
195
|
+
- lib/law/rspec/custom_matchers/be_default_for.rb
|
196
|
+
- lib/law/rspec/custom_matchers/be_enforced_by.rb
|
197
|
+
- lib/law/rspec/custom_matchers/be_imposed_by.rb
|
198
|
+
- lib/law/rspec/custom_matchers/define_action.rb
|
199
|
+
- lib/law/rspec/custom_matchers/have_default_statute.rb
|
200
|
+
- lib/law/rspec/custom_matchers/impose_regulations.rb
|
201
|
+
- lib/law/rspec/shoulda_matcher_helper.rb
|
202
|
+
- lib/law/spec_helper.rb
|
203
|
+
- lib/law/statute_base.rb
|
204
|
+
- lib/law/statutes/laws.rb
|
205
|
+
- lib/law/statutes/regulations.rb
|
73
206
|
- lib/law/version.rb
|
74
|
-
homepage: https://
|
207
|
+
homepage: https://github.com/Freshly/law/tree/master
|
75
208
|
licenses:
|
76
209
|
- MIT
|
77
|
-
metadata:
|
210
|
+
metadata:
|
211
|
+
homepage_uri: https://github.com/Freshly/law/tree/master
|
212
|
+
source_code_uri: https://github.com/Freshly/law/tree/master
|
213
|
+
changelog_uri: https://github.com/Freshly/law/blob/master/CHANGELOG.md
|
78
214
|
post_install_message:
|
79
215
|
rdoc_options: []
|
80
216
|
require_paths:
|
@@ -90,9 +226,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
90
226
|
- !ruby/object:Gem::Version
|
91
227
|
version: '0'
|
92
228
|
requirements: []
|
93
|
-
|
94
|
-
rubygems_version: 2.7.6
|
229
|
+
rubygems_version: 3.0.3
|
95
230
|
signing_key:
|
96
231
|
specification_version: 4
|
97
|
-
summary:
|
232
|
+
summary: Give illegal operations a whole new meaning with this policy enforcement
|
98
233
|
test_files: []
|
data/.gitignore
DELETED
data/.rspec
DELETED
data/.travis.yml
DELETED
data/Gemfile
DELETED
data/Rakefile
DELETED
data/bin/console
DELETED
@@ -1,14 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
require "bundler/setup"
|
4
|
-
require "law"
|
5
|
-
|
6
|
-
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
-
# with your gem easier. You can also use a different console, if you like.
|
8
|
-
|
9
|
-
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
-
# require "pry"
|
11
|
-
# Pry.start
|
12
|
-
|
13
|
-
require "irb"
|
14
|
-
IRB.start(__FILE__)
|
data/bin/setup
DELETED
data/law.gemspec
DELETED
@@ -1,29 +0,0 @@
|
|
1
|
-
|
2
|
-
lib = File.expand_path("../lib", __FILE__)
|
3
|
-
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
-
require "law/version"
|
5
|
-
|
6
|
-
Gem::Specification.new do |spec|
|
7
|
-
spec.name = "law"
|
8
|
-
spec.version = Law::VERSION
|
9
|
-
spec.authors = ["Eric Garside"]
|
10
|
-
spec.email = ["garside@gmail.com"]
|
11
|
-
|
12
|
-
spec.summary = "Enforce the laws of your application"
|
13
|
-
spec.description = "Illegal operations take on a whole new meaning"
|
14
|
-
spec.homepage = "https://www.freshly.com/"
|
15
|
-
spec.license = "MIT"
|
16
|
-
|
17
|
-
# Specify which files should be added to the gem when it is released.
|
18
|
-
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
19
|
-
spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
|
20
|
-
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
21
|
-
end
|
22
|
-
spec.bindir = "exe"
|
23
|
-
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
24
|
-
spec.require_paths = ["lib"]
|
25
|
-
|
26
|
-
spec.add_development_dependency "bundler", "~> 2.0"
|
27
|
-
spec.add_development_dependency "rake", "~> 10.0"
|
28
|
-
spec.add_development_dependency "rspec", "~> 3.0"
|
29
|
-
end
|