core-extension 0.2.0 → 0.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e7e64f65a82f9721e0a0a39f85d8593e6b57e295d6cb8eb6c8febcc798c8497b
4
- data.tar.gz: e4d1dd2fea0b66d39a4c258c296a763c8d512b6404300a58c5f9d1dab66b67f0
3
+ metadata.gz: 02a2bc3087e71c3fc19d9f0c20210833a2a8bd88cf76f6187fade6e389eadf14
4
+ data.tar.gz: 8e38b249e3e3e5badcd6f9e6658d967f96f63ab837d563ca5e97f0cc408ee7a8
5
5
  SHA512:
6
- metadata.gz: d9c8dc64956983a80358ec5ed0dc8d7122029a2fc59750e1bacb3c84da0e81d44bec2958b82c2ae3f506a3c068e9e63b49cec8000e1ff0a9f7d021692b409bbc
7
- data.tar.gz: 1601c7edd515bd085ee5890ca0b4e3ce46b7920d594dbb9a806808da8c73549582ce7287926fa73de7abd4e662b87281a7c31a6b0ab22357fb52e09caf7f2502
6
+ metadata.gz: 38f391788aef874938c0e69dd95eaa24fee62737dbd5744a3bfafe0813e52189266ef079808a61d29c93688963b8eef1ef4bad9c38dbe2fcd38fb4ea468a3174
7
+ data.tar.gz: a41f51ba2b22432e4cd8505065f3e44fa57d1ba5c38cbc5c7b7fb362698cf47580dca2515b0b35fc0a2d2bd2fa5c498ab7faf1e7a67ada66f5591fb551d32954
data/CHANGELOG.md CHANGED
@@ -1,3 +1,13 @@
1
+ ## [v0.3.0](https://github.com/metabahn/corerb/releases/tag/2021-07-15)
2
+
3
+ *released on 2021-07-15*
4
+
5
+ * `chg` [#72](https://github.com/metabahn/corerb/pull/72) Rename extension dependency flags ([bryanp](https://github.com/bryanp))
6
+ * `chg` [#71](https://github.com/metabahn/corerb/pull/71) Rename Is::Extension::applied to applies ([bryanp](https://github.com/bryanp))
7
+ * `chg` [#68](https://github.com/metabahn/corerb/pull/68) Pass the extension scope to apply blocks ([bryanp](https://github.com/bryanp))
8
+ * `chg` [#67](https://github.com/metabahn/corerb/pull/67) Refactor extend support for extensions ([bryanp](https://github.com/bryanp))
9
+ * `chg` [#66](https://github.com/metabahn/corerb/pull/66) Support extending classes with extensions ([bryanp](https://github.com/bryanp))
10
+
1
11
  ## [v0.2.0](https://github.com/metabahn/corerb/releases/tag/2021-07-07)
2
12
 
3
13
  *released on 2021-07-07*
@@ -12,9 +12,17 @@ module Core
12
12
  @module = build_module(&block)
13
13
  end
14
14
 
15
- # [public] Applies this behavior to an object.
15
+ # [public] Applies this behavior to an object via extend.
16
16
  #
17
- def apply(object)
17
+ def apply_extend(object)
18
+ if force? || unapplied?(object)
19
+ object.extend(@module)
20
+ end
21
+ end
22
+
23
+ # [public] Applies this behavior to an object via include.
24
+ #
25
+ def apply_include(object)
18
26
  if force? || unapplied?(object)
19
27
  object.include(@module)
20
28
  end
@@ -34,11 +42,35 @@ module Core
34
42
 
35
43
  private def build_module(&block)
36
44
  Module.new {
45
+ define_singleton_method(:extended) do |base|
46
+ arguments = {
47
+ extended: true,
48
+ included: false
49
+ }
50
+
51
+ base.class_exec(**Behavior.conditional_keyword_arguments(arguments, block), &block)
52
+ end
53
+
37
54
  define_singleton_method(:included) do |base|
38
- base.class_eval(&block)
55
+ arguments = {
56
+ extended: false,
57
+ included: true
58
+ }
59
+
60
+ base.class_exec(**Behavior.conditional_keyword_arguments(arguments, block), &block)
39
61
  end
40
62
  }
41
63
  end
64
+
65
+ class << self
66
+ def conditional_keyword_arguments(arguments, callable)
67
+ arguments.keep_if { |key, _value|
68
+ callable.parameters.any? { |type, name|
69
+ (type == :key || type == :keyreq || type == :keyrest) && name == key
70
+ }
71
+ }
72
+ end
73
+ end
42
74
  end
43
75
  end
44
76
  end
@@ -6,70 +6,71 @@ module Core
6
6
  #
7
7
  # flags - Changes how the dependencies are applied. Possible values include:
8
8
  #
9
- # * `:class` - Extends the including object, making methods available at the class level.
9
+ # * `:definition` - Extends the definition of the including object.
10
10
  #
11
- # * `:instance` - Includes the methods into the object, making them available at the instance level.
11
+ # * `:implementation` - Extends the implementation of the including object.
12
12
  #
13
- # Dependencies are applied with the `:instance` flag by default.
13
+ # Dependencies are applied with the `:implementation` flag by default.
14
14
  #
15
- # modules - Modules to be applied to objects, following the rules defined by flags.
15
+ # dependency - The dependency to be applied to objects, following the rules defined by flags.
16
16
  #
17
- # prepend - If `true`, methods will be prepended rather than appended.
18
- #
19
- # block - If passed, defines an anonymous module as a dependency.
17
+ # prepend - If `true`, methods will be prepended.
20
18
  #
21
19
  class Dependency
22
- ALLOWED_FLAGS = %i[class instance].freeze
20
+ ALLOWED_FLAGS = %i[definition implementation].freeze
21
+ ALLOWED_FLAGS_STRING = ALLOWED_FLAGS.map { |allowed_flag|
22
+ "`#{allowed_flag.inspect}'"
23
+ }.join(", ").freeze
23
24
 
24
- def initialize(*flags, modules: [], prepend: false, &block)
25
+ def initialize(*flags, dependency:, prepend: false)
25
26
  flags = flags.map(&:to_sym)
26
27
  enforce_allowed_flags(flags)
27
28
 
28
29
  @flags = flags
29
- @modules = modules.to_a
30
+ @dependency = dependency
30
31
  @prepend = prepend
31
-
32
- @modules << build_module(&block) if block
32
+ @definition = @flags.include?(:definition)
33
+ @implementation = @flags.include?(:implementation) || (!definition? && !prepend?)
33
34
  end
34
35
 
35
- # [public] Apply the defined dependencies to an object.
36
+ # [public] Apply the defined dependencies to an object via extend.
36
37
  #
37
- def apply(object)
38
- including = include?
39
- extending = extend?
40
- prepending = prepend?
38
+ def apply_extend(object)
39
+ if prepend?
40
+ object.singleton_class.prepend(@dependency) if implementation? || definition?
41
+ elsif definition? || implementation?
42
+ object.extend(@dependency)
43
+ end
44
+ end
41
45
 
42
- @modules.each do |each_module|
43
- if prepending
44
- object.prepend(each_module) if including
45
- object.singleton_class.prepend(each_module) if extending
46
- else
47
- object.include(each_module) if including
48
- object.extend(each_module) if extending
49
- end
46
+ # [public] Apply the defined dependencies to an object via include.
47
+ #
48
+ def apply_include(object)
49
+ if prepend?
50
+ object.prepend(@dependency) if implementation?
51
+ object.singleton_class.prepend(@dependency) if definition?
52
+ else
53
+ object.include(@dependency) if implementation?
54
+ object.extend(@dependency) if definition?
50
55
  end
51
56
  end
52
57
 
53
- private def include?
54
- @flags.include?(:instance) || (!extend? && !prepend?)
58
+ private def implementation?
59
+ @implementation == true
55
60
  end
56
61
 
57
- private def extend?
58
- @flags.include?(:class)
62
+ private def definition?
63
+ @definition == true
59
64
  end
60
65
 
61
66
  private def prepend?
62
67
  @prepend == true
63
68
  end
64
69
 
65
- private def build_module(&block)
66
- Module.new(&block)
67
- end
68
-
69
70
  private def enforce_allowed_flags(flags)
70
71
  flags.each do |flag|
71
72
  unless allowed_flag?(flag)
72
- raise ArgumentError, "Expected flag `#{flag.inspect}' to be one of: #{allowed_flags_string}"
73
+ raise ArgumentError, "Expected flag `#{flag.inspect}' to be one of: #{ALLOWED_FLAGS_STRING}"
73
74
  end
74
75
  end
75
76
  end
@@ -77,12 +78,6 @@ module Core
77
78
  private def allowed_flag?(flag)
78
79
  ALLOWED_FLAGS.include?(flag)
79
80
  end
80
-
81
- private def allowed_flags_string
82
- ALLOWED_FLAGS.map { |allowed_flag|
83
- "`#{allowed_flag.inspect}'"
84
- }.join(", ")
85
- end
86
81
  end
87
82
  end
88
83
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Core
4
4
  module Extension
5
- VERSION = "0.2.0"
5
+ VERSION = "0.3.0"
6
6
 
7
7
  def self.version
8
8
  VERSION
data/lib/is/extension.rb CHANGED
@@ -15,20 +15,39 @@ module Is
15
15
 
16
16
  # [public] Define a module to be extended or included into objects that include this extension.
17
17
  #
18
+ # TODO: These flags almost need different names, since instance extensions could technically end up on a class.
19
+ # Name them by their intended use? Like a definition and usage lifecycle or something.
20
+ #
21
+ # Maybe `definition` and `implementation`?
22
+ #
18
23
  def extends(*flags, dependencies: [], prepend: false, &block)
19
- defined_methods << Core::Extension::Dependency.new(*flags, modules: dependencies, prepend: prepend, &block)
24
+ dependencies.each do |dependency|
25
+ defined_dependencies << Core::Extension::Dependency.new(*flags, dependency: dependency, prepend: prepend)
26
+ end
27
+
28
+ if block
29
+ defined_dependencies << Core::Extension::Dependency.new(*flags, dependency: Module.new(&block), prepend: prepend)
30
+ end
20
31
  end
21
32
 
22
33
  # [public] Define behavior to be evaled on objects that include this extension.
23
34
  #
24
- def applied(force: false, &block)
35
+ def applies(force: false, &block)
25
36
  defined_behaviors << Core::Extension::Behavior.new(force: force, &block)
26
37
  end
27
38
 
39
+ def extended(base)
40
+ enforce_allowed_types(base)
41
+ extend_defined_dependencies(base)
42
+ extend_defined_behaviors(base)
43
+
44
+ super
45
+ end
46
+
28
47
  def included(base)
29
48
  enforce_allowed_types(base)
30
- apply_defined_methods(base)
31
- apply_defined_behaviors(base)
49
+ include_defined_dependencies(base)
50
+ include_defined_behaviors(base)
32
51
 
33
52
  super
34
53
  end
@@ -51,15 +70,27 @@ module Is
51
70
  }
52
71
  end
53
72
 
54
- private def apply_defined_behaviors(object)
73
+ private def extend_defined_behaviors(object)
74
+ defined_behaviors.each do |behavior|
75
+ behavior.apply_extend(object)
76
+ end
77
+ end
78
+
79
+ private def extend_defined_dependencies(object)
80
+ defined_dependencies.each do |dependency|
81
+ dependency.apply_extend(object)
82
+ end
83
+ end
84
+
85
+ private def include_defined_behaviors(object)
55
86
  defined_behaviors.each do |behavior|
56
- behavior.apply(object)
87
+ behavior.apply_include(object)
57
88
  end
58
89
  end
59
90
 
60
- private def apply_defined_methods(object)
61
- defined_methods.each do |methods|
62
- methods.apply(object)
91
+ private def include_defined_dependencies(object)
92
+ defined_dependencies.each do |dependency|
93
+ dependency.apply_include(object)
63
94
  end
64
95
  end
65
96
 
@@ -71,8 +102,8 @@ module Is
71
102
  @__defined_behaviors ||= []
72
103
  end
73
104
 
74
- private def defined_methods
75
- @__defined_methods ||= []
105
+ private def defined_dependencies
106
+ @__defined_dependencies ||= []
76
107
  end
77
108
  end
78
109
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: core-extension
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bryan Powell
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-07-07 00:00:00.000000000 Z
11
+ date: 2021-07-15 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Create mixins with superpowers.
14
14
  email: bryan@metabahn.com