law 0.1.0 → 0.1.1
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 +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
|
+
[](https://badge.fury.io/rb/law)
|
6
|
+
[](https://semaphoreci.com/freshly/law)
|
7
|
+
[](https://codeclimate.com/github/Freshly/law/maintainability)
|
8
|
+
[](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
|