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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d372ee94e1c1e2eea155447eb1cdb85a2e0b064e2645528dc8c624cc48a10825
4
- data.tar.gz: 766a7cdad82a723ad9e0dd38bc151be81782ef6f3420473c5a912319fe648691
3
+ metadata.gz: 1757d5bf22062e71a309eb62fd4b55aa830d36a7e7cde695f8525b960d5a0a89
4
+ data.tar.gz: 8f591ab19ba6b88118834b65aa210166408bc127afba0551bb2792505e7abc64
5
5
  SHA512:
6
- metadata.gz: 8776e38f53fa08187a26349fced6d516b57afb72469dca06421d583aa9c50b0d53cef3be513bb9bdb131c7b69e44ba4637afc0ec66634aa06f3527f3808ad8ac
7
- data.tar.gz: 15faafc27a3812f4ec9ddb9b76d56c79512737e2960b8f746e680bd88954484b1274ef79f735b45fa116edd831a18ea564962e489cdba2983b2923a421d81737
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
@@ -432,7 +432,7 @@ module GlueGun
432
432
  end
433
433
  end
434
434
 
435
- dep_attributes
435
+ dep_attributes.deep_compact
436
436
  end
437
437
 
438
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.20"
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.20
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-18 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