glue_gun_dsl 0.1.19 → 0.1.21

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 68fc0793c49beee7168734f15f10ef57334c0fbbb0f4373c74cfdad16b416a2f
4
- data.tar.gz: 34ba98c7f1518de7b16ae4c2d1e4cd9f5c8224ad911c3d7bc14ed21dec139fe8
3
+ metadata.gz: 1757d5bf22062e71a309eb62fd4b55aa830d36a7e7cde695f8525b960d5a0a89
4
+ data.tar.gz: 8f591ab19ba6b88118834b65aa210166408bc127afba0551bb2792505e7abc64
5
5
  SHA512:
6
- metadata.gz: 72d8a6751b6ed9d9ea22f8dfb8e89386b2217bbdfc17f922716a208ca9fce8a290762ceb43dcb301d32e9733058c1ea7433b8435a8b6f3b9feb96db83ceaeaaa
7
- data.tar.gz: 4cece38fc3e06acd4c285a4e8d04e19d69c01b39ac80af8815f967b96df7f863ef225bad172fb4e5231552a107ae092445feee107b3419225db3e6c9d26f59b1
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
@@ -0,0 +1,5 @@
1
+ module GlueGun
2
+ module CoreExt
3
+ require_relative "core_ext/hash_extensions"
4
+ end
5
+ end
data/lib/glue_gun/dsl.rb CHANGED
@@ -272,6 +272,10 @@ module GlueGun
272
272
  @dependencies ||= {}
273
273
  end
274
274
 
275
+ def inspect
276
+ "#<#{self.class} #{attributes.map { |k, v| "#{k}: #{v.inspect}" }.join(", ")}>"
277
+ end
278
+
275
279
  def validate_dependencies
276
280
  errors.clear
277
281
  self.class.dependency_definitions.keys.each do |component_type|
@@ -414,7 +418,7 @@ module GlueGun
414
418
  def build_dependency_attributes(option_config, dep_attributes, parent)
415
419
  option_config.attributes.each do |attr_name, attr_config|
416
420
  if dep_attributes.key?(attr_name)
417
- value = dep_attributes[attr_name]
421
+ dep_attributes[attr_name]
418
422
  else
419
423
  value = if attr_config.source && parent.respond_to?(attr_config.source)
420
424
  parent.send(attr_config.source)
@@ -428,7 +432,7 @@ module GlueGun
428
432
  end
429
433
  end
430
434
 
431
- dep_attributes
435
+ dep_attributes.deep_compact
432
436
  end
433
437
 
434
438
  def determine_option_name(init_args, instance)
@@ -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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module GlueGun
4
- VERSION = "0.1.19"
4
+ VERSION = "0.1.21"
5
5
  end
data/lib/glue_gun.rb CHANGED
@@ -4,6 +4,8 @@ require "active_support/concern"
4
4
 
5
5
  module GlueGun
6
6
  require_relative "glue_gun/version"
7
+ require_relative "glue_gun/core_ext"
7
8
  require_relative "glue_gun/types"
8
9
  require_relative "glue_gun/dsl"
10
+ require_relative "glue_gun/model"
9
11
  end
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.19
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-17 00:00:00.000000000 Z
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