glue_gun_dsl 0.1.20 → 0.1.21
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/glue_gun/core_ext/hash_extensions.rb +22 -0
- data/lib/glue_gun/core_ext.rb +5 -0
- data/lib/glue_gun/dsl.rb +1 -1
- data/lib/glue_gun/model.rb +164 -0
- data/lib/glue_gun/my_model.rb +73 -0
- data/lib/glue_gun/version.rb +1 -1
- data/lib/glue_gun.rb +2 -0
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1757d5bf22062e71a309eb62fd4b55aa830d36a7e7cde695f8525b960d5a0a89
|
4
|
+
data.tar.gz: 8f591ab19ba6b88118834b65aa210166408bc127afba0551bb2792505e7abc64
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c25769bebf3f9e450970506b45fa13c82281d223f3da07f1d3e667597678903da636422c24924525456f1063b4d142722729fc9bb240eec9801a6d67a1a5fae9
|
7
|
+
data.tar.gz: 13e57e0b3cc63e2ed15e26d480d0622e6413476dae3fff9ba8bf1686fc6fa4c314baad22452b157982cd10699cbb6f6574c84130168d83874d817aef0dbc98e9
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module GlueGun
|
2
|
+
module HashExtensions
|
3
|
+
def deep_compact
|
4
|
+
each_with_object({}) do |(key, value), result|
|
5
|
+
next if value.nil?
|
6
|
+
|
7
|
+
compacted = if value.is_a?(Hash)
|
8
|
+
value.deep_compact
|
9
|
+
elsif value.is_a?(Array)
|
10
|
+
value.map { |v| v.is_a?(Hash) ? v.deep_compact : v }.compact
|
11
|
+
else
|
12
|
+
value
|
13
|
+
end
|
14
|
+
|
15
|
+
result[key] = compacted unless compacted.blank?
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# Extend Hash class with our custom method
|
22
|
+
Hash.include GlueGun::HashExtensions
|
data/lib/glue_gun/dsl.rb
CHANGED
@@ -0,0 +1,164 @@
|
|
1
|
+
module GlueGun
|
2
|
+
module Model
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
before_save :serialize_service_object
|
7
|
+
after_find :deserialize_service_object
|
8
|
+
|
9
|
+
class_attribute :service_class_resolver
|
10
|
+
class_attribute :service_class
|
11
|
+
class_attribute :service_attribute_name
|
12
|
+
|
13
|
+
# Set default service attribute name based on the class name
|
14
|
+
self.service_attribute_name = "#{name.demodulize.underscore}_service".to_sym
|
15
|
+
end
|
16
|
+
|
17
|
+
class_methods do
|
18
|
+
def service(class_or_proc = nil, &block)
|
19
|
+
if class_or_proc.is_a?(Class)
|
20
|
+
self.service_class = class_or_proc
|
21
|
+
elsif block_given?
|
22
|
+
self.service_class_resolver = block
|
23
|
+
else
|
24
|
+
raise ArgumentError, "You must provide a service class, factory, or a block to resolve the service class."
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def initialize(attributes = {})
|
30
|
+
attributes = attributes.deep_symbolize_keys
|
31
|
+
db_attributes = extract_db_attributes(attributes)
|
32
|
+
super(db_attributes)
|
33
|
+
self.class.send(:attr_reader, service_attribute_name)
|
34
|
+
build_service_object(attributes)
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def extract_db_attributes(attributes)
|
40
|
+
# Extract attributes that correspond to database columns or associations
|
41
|
+
column_names = self.class.column_names.map(&:to_sym)
|
42
|
+
association_names = self.class.reflect_on_all_associations.map(&:name)
|
43
|
+
|
44
|
+
attributes.slice(*(column_names + association_names))
|
45
|
+
end
|
46
|
+
|
47
|
+
def build_service_object(attributes)
|
48
|
+
service_class = resolve_service_class(attributes)
|
49
|
+
service_attributes = extract_service_attributes(attributes, service_class)
|
50
|
+
service_instance = service_class.new(service_attributes)
|
51
|
+
instance_variable_set("@#{service_attribute_name}", service_instance)
|
52
|
+
end
|
53
|
+
|
54
|
+
def resolve_service_class(attributes)
|
55
|
+
if self.class.service_class
|
56
|
+
self.class.service_class
|
57
|
+
elsif self.class.service_class_resolver
|
58
|
+
self.class.service_class_resolver.call(attributes)
|
59
|
+
else
|
60
|
+
raise "Service class not defined for #{self.class.name}"
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def extract_service_attributes(attributes, service_class)
|
65
|
+
allowed_attrs = service_attributes(service_class)
|
66
|
+
attrs_and_associations(attributes).slice(*allowed_attrs)
|
67
|
+
end
|
68
|
+
|
69
|
+
def service_attributes(service_class)
|
70
|
+
service_class.dependency_definitions.keys.concat(
|
71
|
+
service_class.attribute_definitions.keys
|
72
|
+
)
|
73
|
+
end
|
74
|
+
|
75
|
+
def attrs_and_associations(attributes)
|
76
|
+
foreign_keys = foreign_key_map
|
77
|
+
attributes.inject({}) do |h, (k, v)|
|
78
|
+
h.tap do
|
79
|
+
if foreign_keys.include?(k)
|
80
|
+
assoc_name = foreign_keys[k]
|
81
|
+
h[assoc_name] = send(assoc_name)
|
82
|
+
else
|
83
|
+
h[k] = v
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def foreign_key_map
|
90
|
+
self.class.reflect_on_all_associations.inject({}) do |h, assoc|
|
91
|
+
h.tap do
|
92
|
+
h[assoc.foreign_key] = assoc.name
|
93
|
+
end
|
94
|
+
end.symbolize_keys
|
95
|
+
end
|
96
|
+
|
97
|
+
def serialize_service_object
|
98
|
+
service_object = instance_variable_get("@#{service_attribute_name}")
|
99
|
+
service_klass = service_object.class
|
100
|
+
attrs = service_klass.respond_to?(:serialize) ? service_klass.serialize(service_object) : service_object.attributes
|
101
|
+
deps = allowed_names(service_object.dependency_definitions.keys).inject({}) do |hash, dep|
|
102
|
+
hash.tap do
|
103
|
+
this_dep = service_object.send(dep)
|
104
|
+
next unless this_dep.present?
|
105
|
+
|
106
|
+
opts = service_object.dependency_definitions[dep].option_configs
|
107
|
+
selected_option = opts.detect do |_k, v|
|
108
|
+
this_dep.class == v.class_name
|
109
|
+
end&.first
|
110
|
+
unless selected_option.present?
|
111
|
+
raise "Don't know how to serialize dependency of type #{dep}, available options are #{opts.keys}. You didn't specify an option."
|
112
|
+
end
|
113
|
+
|
114
|
+
hash[dep] = {
|
115
|
+
selected_option => service_object.send(dep).attributes
|
116
|
+
}
|
117
|
+
end
|
118
|
+
end
|
119
|
+
json = attrs.merge(deps.deep_compact).deep_symbolize_keys
|
120
|
+
write_attribute(:configuration, json.to_json)
|
121
|
+
end
|
122
|
+
|
123
|
+
def deserialize_service_object
|
124
|
+
serialized_data = JSON.parse(read_attribute(:configuration) || "{}")
|
125
|
+
serialized_data.deep_symbolize_keys!
|
126
|
+
service_class = resolve_service_class(serialized_data)
|
127
|
+
serialized_data = service_class.deserialize(serialized_data) if service_class.respond_to?(:deserialize)
|
128
|
+
service_instance = service_class.new(serialized_data)
|
129
|
+
instance_variable_set("@#{service_attribute_name}", service_instance)
|
130
|
+
end
|
131
|
+
|
132
|
+
def allowed_names(names)
|
133
|
+
assoc_names = self.class.reflect_on_all_associations.map(&:name)
|
134
|
+
[names.map(&:to_sym) - assoc_names.map(&:to_sym)].flatten
|
135
|
+
end
|
136
|
+
|
137
|
+
def serialize
|
138
|
+
dataset_service.attributes
|
139
|
+
write_attribute(:configuration, json.to_json)
|
140
|
+
end
|
141
|
+
|
142
|
+
def deserialize
|
143
|
+
options = JSON.parse(read_attribute(:configuration))
|
144
|
+
options.deep_symbolize_keys!
|
145
|
+
|
146
|
+
build_dataset_service(options)
|
147
|
+
end
|
148
|
+
|
149
|
+
def method_missing(method_name, *args, &block)
|
150
|
+
service_object = instance_variable_get("@#{service_attribute_name}")
|
151
|
+
|
152
|
+
if service_object && service_object.respond_to?(method_name)
|
153
|
+
service_object.send(method_name, *args, &block)
|
154
|
+
else
|
155
|
+
super
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
def respond_to_missing?(method_name, include_private = false)
|
160
|
+
service_object = instance_variable_get("@#{service_attribute_name}")
|
161
|
+
service_object && service_object.respond_to?(method_name) || super
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
module EasyML
|
2
|
+
class Dataset < ActiveRecord::Base
|
3
|
+
before_save :serialize
|
4
|
+
after_find :deserialize
|
5
|
+
|
6
|
+
validates :name, presence: true
|
7
|
+
belongs_to :datasource,
|
8
|
+
foreign_key: :datasource_id,
|
9
|
+
class_name: "EasyML::Datasource"
|
10
|
+
|
11
|
+
def initialize(options = {})
|
12
|
+
super(EasyML::DbOptions.parse(options, self))
|
13
|
+
build_dataset_service(options)
|
14
|
+
end
|
15
|
+
|
16
|
+
attr_accessor :dataset_service
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def build_dataset_service(options)
|
21
|
+
options.deep_symbolize_keys!
|
22
|
+
service_klass = EasyML::Data::Dataset
|
23
|
+
|
24
|
+
allowed_attr_names = service_klass.dependency_definitions.keys.concat(
|
25
|
+
service_klass.attribute_definitions.keys
|
26
|
+
)
|
27
|
+
|
28
|
+
allowed_attrs = options.slice(*allowed_attr_names)
|
29
|
+
@dataset_service = service_klass.new(allowed_attrs)
|
30
|
+
define_dataset_service_delegators(allowed_attr_names)
|
31
|
+
end
|
32
|
+
|
33
|
+
def define_dataset_service_delegators(attr_names)
|
34
|
+
allowed_names(attr_names).each do |attr_name|
|
35
|
+
define_singleton_method(attr_name) do
|
36
|
+
dataset_service.send(attr_name)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def allowed_names(names)
|
42
|
+
assoc_names = self.class.reflect_on_all_associations.map(&:name)
|
43
|
+
[names.map(&:to_sym) - assoc_names.map(&:to_sym)].flatten
|
44
|
+
end
|
45
|
+
|
46
|
+
def serialize
|
47
|
+
attrs = dataset_service.attributes
|
48
|
+
deps = allowed_names(dataset_service.dependency_definitions.keys).inject({}) do |hash, dep|
|
49
|
+
hash.tap do
|
50
|
+
this_dep = dataset_service.send(dep)
|
51
|
+
next unless this_dep.present?
|
52
|
+
|
53
|
+
opts = dataset_service.dependency_definitions[dep].option_configs
|
54
|
+
selected_option = opts.detect do |_k, v|
|
55
|
+
this_dep.class == v.class_name
|
56
|
+
end.first
|
57
|
+
hash[dep] = {
|
58
|
+
selected_option => dataset_service.send(dep).attributes
|
59
|
+
}
|
60
|
+
end
|
61
|
+
end
|
62
|
+
json = attrs.merge(deps).deep_symbolize_keys.deep_compact
|
63
|
+
write_attribute(:configuration, json.to_json)
|
64
|
+
end
|
65
|
+
|
66
|
+
def deserialize
|
67
|
+
options = JSON.parse(read_attribute(:configuration))
|
68
|
+
options.deep_symbolize_keys!
|
69
|
+
|
70
|
+
build_dataset_service(options)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
data/lib/glue_gun/version.rb
CHANGED
data/lib/glue_gun.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: glue_gun_dsl
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.21
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Brett Shollenberger
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-10-
|
11
|
+
date: 2024-10-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activemodel
|
@@ -190,7 +190,11 @@ extensions: []
|
|
190
190
|
extra_rdoc_files: []
|
191
191
|
files:
|
192
192
|
- lib/glue_gun.rb
|
193
|
+
- lib/glue_gun/core_ext.rb
|
194
|
+
- lib/glue_gun/core_ext/hash_extensions.rb
|
193
195
|
- lib/glue_gun/dsl.rb
|
196
|
+
- lib/glue_gun/model.rb
|
197
|
+
- lib/glue_gun/my_model.rb
|
194
198
|
- lib/glue_gun/types.rb
|
195
199
|
- lib/glue_gun/types/array_type.rb
|
196
200
|
- lib/glue_gun/types/date_time_type.rb
|