composable-core 0.0.4 → 0.0.5
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/lib/composable/core/attribute_dsl.rb +91 -0
- data/lib/composable/core/callbacks.rb +15 -0
- data/lib/composable/core/command.rb +57 -0
- data/lib/composable/core/composable_dsl.rb +221 -0
- data/lib/composable/core/gem_version.rb +1 -1
- data/lib/composable/core/inheritable_attributes.rb +66 -0
- data/lib/composable/core/record_invalid.rb +43 -0
- data/lib/composable/core.rb +11 -0
- metadata +38 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a79063aeae6d1075137b90dd1f4979cd3ce45a217e0060ccdf3dea2653fb5cfe
|
4
|
+
data.tar.gz: 48941772dda21b2f50a0951be88764fe2084eac74447a91fab07c3c42c7e9ed9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a4b2074df7a43304080cdbff56f64b5bcf2bde0fcd6c8f7231ae52f3645ac3f94779b3e651c349c7d572010bbcf459be64b7f7a4b61af08c90d60651c61d24ee
|
7
|
+
data.tar.gz: 752c88d42e650202e19e65a6709dc65dda68b3806d3d52787a08c80f2ffecf0efc93d21dc8c5c7fdb2e13d5908c6233861e48b2153c5815b6cd93a8a2fd6cb77
|
@@ -0,0 +1,91 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Composable
|
4
|
+
module Core
|
5
|
+
module AttributeDSL
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
include InheritableAttributes
|
8
|
+
|
9
|
+
included do
|
10
|
+
inheritable_attributes :attributes, default: Set.new
|
11
|
+
inheritable_attributes :_attribute_options, default: {}
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize(options = {})
|
15
|
+
unless options.respond_to?(:has_key?)
|
16
|
+
raise ArgumentError, "When assigning attributes, you must pass a hash" \
|
17
|
+
" as an argument, #{options.class} passed."
|
18
|
+
end
|
19
|
+
|
20
|
+
attributes.each do |attribute|
|
21
|
+
# Try a `string` key if `symbol` key does not exist
|
22
|
+
value = options.fetch(attribute.to_sym, options[attribute.to_s])
|
23
|
+
send("#{attribute}=", value) unless value.nil?
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def attributes
|
28
|
+
self.class.attributes
|
29
|
+
end
|
30
|
+
|
31
|
+
def params
|
32
|
+
attributes.each_with_object({}) do |attribute, hash|
|
33
|
+
hash[attribute] = send(attribute)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
module ClassMethods
|
38
|
+
def attribute(*attrs)
|
39
|
+
options = attrs.extract_options!
|
40
|
+
|
41
|
+
return attributes if attrs.empty?
|
42
|
+
|
43
|
+
attrs.each do |attribute|
|
44
|
+
_save_options_for(attribute, **options)
|
45
|
+
|
46
|
+
next if attributes.include?(attribute.to_sym)
|
47
|
+
|
48
|
+
_define_getter(attribute)
|
49
|
+
_define_setter(attribute)
|
50
|
+
_define_question_mark_method(attribute)
|
51
|
+
attributes << attribute.to_sym
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def _define_getter(attribute)
|
58
|
+
define_method(attribute) do
|
59
|
+
options = self.class._attribute_options.fetch(attribute.to_sym, {})
|
60
|
+
value = if instance_variable_defined?("@#{attribute}")
|
61
|
+
instance_variable_get("@#{attribute}")
|
62
|
+
else
|
63
|
+
instance_variable_set("@#{attribute}", options[:default].deep_dup)
|
64
|
+
end
|
65
|
+
|
66
|
+
options[:type] ? options[:type].cast(value) : value
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def _define_setter(attribute)
|
71
|
+
define_method("#{attribute}=") do |value|
|
72
|
+
options = self.class._attribute_options.fetch(attribute.to_sym, {})
|
73
|
+
instance_variable_set("@#{attribute}", options[:type] ? options[:type].cast(value) : value)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def _define_question_mark_method(attribute)
|
78
|
+
define_method("#{attribute}?") do
|
79
|
+
send(attribute).present?
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def _save_options_for(attribute, type: nil, default: nil, **typed_options)
|
84
|
+
options = _attribute_options[attribute.to_sym] ||= {}
|
85
|
+
options[:default] = default unless default.nil?
|
86
|
+
options[:type] = ActiveModel::Type.lookup(type, **typed_options) unless type.nil?
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Composable
|
4
|
+
module Core
|
5
|
+
module Command
|
6
|
+
class NotImplementedError < ::StandardError; end
|
7
|
+
|
8
|
+
attr_reader :result
|
9
|
+
|
10
|
+
module ClassMethods
|
11
|
+
def call(*args, **kwargs)
|
12
|
+
new(*args, **kwargs).call
|
13
|
+
end
|
14
|
+
|
15
|
+
def call!(*args, **kwargs)
|
16
|
+
new(*args, **kwargs).call(raise_exception: true)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.prepended(base)
|
21
|
+
base.extend ClassMethods
|
22
|
+
|
23
|
+
base.include ActiveModel::Validations
|
24
|
+
base.include ActiveSupport::Rescuable
|
25
|
+
end
|
26
|
+
|
27
|
+
def call(raise_exception: false)
|
28
|
+
fail NotImplementedError unless defined?(super)
|
29
|
+
|
30
|
+
@called = true
|
31
|
+
@result = super()
|
32
|
+
|
33
|
+
raise Error, errors.full_messages.to_sentence if raise_exception && failure?
|
34
|
+
|
35
|
+
self
|
36
|
+
rescue Exception => exception
|
37
|
+
raise if raise_exception || !rescue_with_handler(exception)
|
38
|
+
|
39
|
+
self
|
40
|
+
end
|
41
|
+
|
42
|
+
def success?
|
43
|
+
called? && !failure?
|
44
|
+
end
|
45
|
+
|
46
|
+
def failure?
|
47
|
+
called? && errors.any?
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def called?
|
53
|
+
@called ||= false
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,221 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Composable
|
4
|
+
module Core
|
5
|
+
module ComposableDSL
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
include InheritableAttributes
|
8
|
+
|
9
|
+
included do
|
10
|
+
inheritable_attributes :composables, default: {}
|
11
|
+
end
|
12
|
+
|
13
|
+
module ClassMethods
|
14
|
+
def composable(attribute, **options, &block)
|
15
|
+
composables[attribute.to_sym] ||= Composable.new(attribute)
|
16
|
+
composables[attribute.to_sym].evaluate(**options, &block)
|
17
|
+
|
18
|
+
return if attributes.include?(attribute.to_sym)
|
19
|
+
|
20
|
+
attribute attribute
|
21
|
+
|
22
|
+
alias_method "#{attribute}_original_setter=", "#{attribute}="
|
23
|
+
|
24
|
+
define_method("#{attribute}=") do |value|
|
25
|
+
send("#{attribute}_original_setter=", value)
|
26
|
+
composables[attribute.to_sym].sync_attributes(
|
27
|
+
self, composable_record_for(attribute), reverse: true
|
28
|
+
)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# In order to prevent attribute values passed in at initialize from being
|
34
|
+
# overridden by composable_record values, composable_record mapping methods
|
35
|
+
# have to be synced first.
|
36
|
+
def initialize(options = {})
|
37
|
+
composable_keys.each do |attribute|
|
38
|
+
value = options.delete(attribute) || options.delete(attribute.to_s)
|
39
|
+
send("#{attribute}=", value) if value
|
40
|
+
end
|
41
|
+
|
42
|
+
super
|
43
|
+
end
|
44
|
+
|
45
|
+
def composables
|
46
|
+
self.class.composables
|
47
|
+
end
|
48
|
+
|
49
|
+
def save_composables
|
50
|
+
multi_db_transaction do
|
51
|
+
sync_composable_records
|
52
|
+
save_composable_records
|
53
|
+
|
54
|
+
yield if block_given?
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
def sync_composable_records
|
61
|
+
composables.each do |attribute, composable|
|
62
|
+
record = composable_record_for(attribute)
|
63
|
+
composable.sync_attributes(self, record) if composable.valid?(self, record)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def save_composable_records
|
68
|
+
composables.each do |attribute, composable|
|
69
|
+
record = composable_record_for(attribute)
|
70
|
+
record.save! if composable.valid?(self, record)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# Ensures `save!` queries are wrapped within multiple db transactions.
|
75
|
+
# It might be the case that our service is working with several composable
|
76
|
+
# objects that are gonna be saved into diffent databases, if there is an
|
77
|
+
# error, all transactions are gonna be rollback regardless of the DB.
|
78
|
+
def multi_db_transaction(attribute: composable_keys.first, &block)
|
79
|
+
return yield if attribute.nil?
|
80
|
+
|
81
|
+
index = composable_keys.index(attribute)
|
82
|
+
record = composable_record_for(attribute)
|
83
|
+
|
84
|
+
multi_db_transaction(attribute: composable_keys[index + 1]) do
|
85
|
+
record.respond_to?(:transaction) ? record.transaction(&block) : yield
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def composable_keys
|
90
|
+
@composable_keys ||= composables.keys.freeze
|
91
|
+
end
|
92
|
+
|
93
|
+
def composable_record_for(attribute)
|
94
|
+
send(attribute) or
|
95
|
+
raise ArgumentError, "required composable argument: #{attribute}"
|
96
|
+
end
|
97
|
+
|
98
|
+
class Composable
|
99
|
+
attr_reader :attribute, :mapping, :conditions
|
100
|
+
|
101
|
+
def initialize(attribute)
|
102
|
+
@attribute = attribute
|
103
|
+
@mapping = {}
|
104
|
+
@conditions = Conditions.new
|
105
|
+
end
|
106
|
+
|
107
|
+
def initialize_copy(original_object)
|
108
|
+
@mapping = original_object.mapping.deep_dup
|
109
|
+
@conditions = original_object.conditions.deep_dup
|
110
|
+
end
|
111
|
+
|
112
|
+
def evaluate(**options, &block)
|
113
|
+
conditions.merge(**options)
|
114
|
+
|
115
|
+
instance_eval(&block) if block
|
116
|
+
end
|
117
|
+
|
118
|
+
def sync_attributes(form, record, reverse: false)
|
119
|
+
mapping.each_value do |instance|
|
120
|
+
instance.sync(form, record, reverse: reverse)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def valid?(...)
|
125
|
+
conditions.valid?(...)
|
126
|
+
end
|
127
|
+
|
128
|
+
private
|
129
|
+
|
130
|
+
# DSL method
|
131
|
+
def sync(*args, to: nil, **options)
|
132
|
+
args.extract_options!
|
133
|
+
|
134
|
+
args.compact.uniq.each do |name|
|
135
|
+
mapping[name.to_sym] ||= Sync.new(from: name)
|
136
|
+
mapping[name.to_sym].merge(to: (to || name), **options)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
class Sync
|
141
|
+
attr_reader :from, :to, :conditions
|
142
|
+
|
143
|
+
def initialize(from:)
|
144
|
+
@from = from.to_sym
|
145
|
+
@conditions = Conditions.new
|
146
|
+
end
|
147
|
+
|
148
|
+
def initialize_copy(original_object)
|
149
|
+
@conditions = original_object.conditions.deep_dup
|
150
|
+
end
|
151
|
+
|
152
|
+
def merge(to:, **options)
|
153
|
+
@to = to.to_sym
|
154
|
+
|
155
|
+
conditions.merge(**options)
|
156
|
+
end
|
157
|
+
|
158
|
+
def sync(form, record, reverse: false)
|
159
|
+
if reverse
|
160
|
+
form.send("#{from}=", record.send(to))
|
161
|
+
else
|
162
|
+
value = form.send(from)
|
163
|
+
record.send("#{to}=", value) if conditions.valid?(form, record)
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
class Conditions
|
169
|
+
attr_reader :conditions
|
170
|
+
|
171
|
+
def initialize
|
172
|
+
@conditions = {}
|
173
|
+
end
|
174
|
+
|
175
|
+
def initialize_copy(original_object)
|
176
|
+
@conditions = original_object.conditions.deep_dup
|
177
|
+
end
|
178
|
+
|
179
|
+
def merge(**options)
|
180
|
+
options.symbolize_keys.slice(:if, :unless).each do |statement, condition|
|
181
|
+
conditions[statement] = Condition.new(statement, condition)
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
def valid?(form, record)
|
186
|
+
conditions.values.all? { |condition| condition.call(form, record) }
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
class Condition
|
191
|
+
attr_reader :condition, :statement, :callable
|
192
|
+
|
193
|
+
def initialize(statement, condition)
|
194
|
+
@statement = statement.to_sym
|
195
|
+
@condition = condition
|
196
|
+
@callable = make_lambda
|
197
|
+
end
|
198
|
+
|
199
|
+
def call(form, record)
|
200
|
+
callable.call(form, record).eql?(statement == :if)
|
201
|
+
end
|
202
|
+
|
203
|
+
private
|
204
|
+
|
205
|
+
def make_lambda
|
206
|
+
case condition
|
207
|
+
when Symbol
|
208
|
+
->(form, _record) { form.send(condition) }
|
209
|
+
when ::Proc
|
210
|
+
lambda { |form, record|
|
211
|
+
form.instance_exec(*[record][0...condition.arity], &condition)
|
212
|
+
}
|
213
|
+
else
|
214
|
+
->(*) { true }
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Composable
|
4
|
+
module Core
|
5
|
+
module InheritableAttributes
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
module ClassMethods
|
9
|
+
def inheritable_attributes(*attributes)
|
10
|
+
options = attributes.extract_options!
|
11
|
+
|
12
|
+
@inheritable_attributes ||= [{attribute: :inheritable_attributes}]
|
13
|
+
attributes.each do |attribute|
|
14
|
+
@inheritable_attributes << {attribute: attribute, default: options[:default]}
|
15
|
+
|
16
|
+
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
17
|
+
class << self; attr_accessor :#{attribute} end
|
18
|
+
RUBY
|
19
|
+
send("#{attribute}=", options[:default]) unless options[:default].nil?
|
20
|
+
end
|
21
|
+
@inheritable_attributes
|
22
|
+
end
|
23
|
+
|
24
|
+
def uninheritable_attributes(*attributes)
|
25
|
+
options = attributes.extract_options!
|
26
|
+
|
27
|
+
@uninheritable_attributes ||= [{attribute: :uninheritable_attributes}]
|
28
|
+
|
29
|
+
attributes.each do |attribute|
|
30
|
+
@uninheritable_attributes << {attribute: attribute, default: options[:default]}
|
31
|
+
|
32
|
+
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
33
|
+
class << self; attr_accessor :#{attribute} end
|
34
|
+
RUBY
|
35
|
+
send("#{attribute}=", options[:default]) unless options[:default].nil?
|
36
|
+
end
|
37
|
+
|
38
|
+
@uninheritable_attributes
|
39
|
+
end
|
40
|
+
|
41
|
+
def inherited(subclass)
|
42
|
+
super
|
43
|
+
|
44
|
+
Array(@inheritable_attributes).each do |hash|
|
45
|
+
inheritable_attribute = hash[:attribute]
|
46
|
+
instance_name = "@#{inheritable_attribute}"
|
47
|
+
instance_value = instance_variable_get(instance_name).deep_dup
|
48
|
+
subclass.instance_variable_set(instance_name, instance_value || hash[:default].deep_dup)
|
49
|
+
end
|
50
|
+
|
51
|
+
Array(@uninheritable_attributes).each do |hash|
|
52
|
+
uninheritable_attribute = hash[:attribute]
|
53
|
+
instance_name = "@#{uninheritable_attribute}"
|
54
|
+
|
55
|
+
if instance_name == "@uninheritable_attributes"
|
56
|
+
instance_value = instance_variable_get(instance_name).deep_dup
|
57
|
+
subclass.instance_variable_set(instance_name, instance_value)
|
58
|
+
else
|
59
|
+
subclass.instance_variable_set(instance_name, hash[:default].deep_dup)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Composable
|
4
|
+
module Core
|
5
|
+
module RecordInvalid
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
include ActiveSupport::Rescuable
|
8
|
+
|
9
|
+
class ErrorWithRecord < Exception
|
10
|
+
attr_reader :record
|
11
|
+
|
12
|
+
def initialize(record)
|
13
|
+
@record = record
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
included do
|
18
|
+
prepend ComposableDSLExt
|
19
|
+
|
20
|
+
rescue_from(ErrorWithRecord) do |exception|
|
21
|
+
exception.record.errors.delete(:base)&.each { |m| errors.add(:base, m) }
|
22
|
+
exception.record.errors.each do |error|
|
23
|
+
if error.attribute.to_sym.in?(attributes)
|
24
|
+
errors.add(error.attribute, error.message)
|
25
|
+
else
|
26
|
+
errors.add(:base, exception.record.errors.full_message(error.attribute, error.message))
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
module ComposableDSLExt
|
33
|
+
def save_composable_records
|
34
|
+
super
|
35
|
+
rescue ActiveRecord::RecordInvalid => exception
|
36
|
+
raise ErrorWithRecord.new(exception.record)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
Composable::Core::ComposableDSL.include Composable::Core::RecordInvalid
|
data/lib/composable/core.rb
CHANGED
@@ -1,9 +1,20 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative "core/version"
|
4
|
+
require "active_support"
|
5
|
+
require "active_support/core_ext/object/deep_dup"
|
6
|
+
require "active_model"
|
4
7
|
|
5
8
|
module Composable
|
6
9
|
module Core
|
7
10
|
class Error < StandardError; end
|
11
|
+
|
12
|
+
autoload :AttributeDSL, "composable/core/attribute_dsl"
|
13
|
+
autoload :Callbacks, "composable/core/callbacks"
|
14
|
+
autoload :Command, "composable/core/command"
|
15
|
+
autoload :ComposableDSL, "composable/core/composable_dsl"
|
16
|
+
autoload :InheritableAttributes, "composable/core/inheritable_attributes"
|
8
17
|
end
|
9
18
|
end
|
19
|
+
|
20
|
+
require "composable/core/record_invalid" if defined?(ActiveRecord)
|
metadata
CHANGED
@@ -1,15 +1,43 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: composable-core
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jairo Vazquez
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
12
|
-
dependencies:
|
11
|
+
date: 2022-04-06 00:00:00.000000000 Z
|
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: '6.1'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '6.1'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: activemodel
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '6.1'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '6.1'
|
13
41
|
description: Core composable object to work with ruby services and forms
|
14
42
|
email:
|
15
43
|
- jairovm20@gmail.com
|
@@ -21,7 +49,13 @@ files:
|
|
21
49
|
- LICENSE.txt
|
22
50
|
- README.md
|
23
51
|
- lib/composable/core.rb
|
52
|
+
- lib/composable/core/attribute_dsl.rb
|
53
|
+
- lib/composable/core/callbacks.rb
|
54
|
+
- lib/composable/core/command.rb
|
55
|
+
- lib/composable/core/composable_dsl.rb
|
24
56
|
- lib/composable/core/gem_version.rb
|
57
|
+
- lib/composable/core/inheritable_attributes.rb
|
58
|
+
- lib/composable/core/record_invalid.rb
|
25
59
|
- lib/composable/core/version.rb
|
26
60
|
homepage: https://github.com/jairovm/composables
|
27
61
|
licenses:
|
@@ -45,7 +79,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
45
79
|
- !ruby/object:Gem::Version
|
46
80
|
version: '0'
|
47
81
|
requirements: []
|
48
|
-
rubygems_version: 3.
|
82
|
+
rubygems_version: 3.3.3
|
49
83
|
signing_key:
|
50
84
|
specification_version: 4
|
51
85
|
summary: Core Composable Object
|