easy_talk 1.0.1 → 1.0.3

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.
@@ -9,6 +9,7 @@ require 'active_support/json'
9
9
  require 'active_model'
10
10
  require_relative 'builders/object_builder'
11
11
  require_relative 'schema_definition'
12
+ require_relative 'active_record_schema_builder'
12
13
 
13
14
  module EasyTalk
14
15
  # The `Model` module is a mixin that provides functionality for defining and accessing the schema of a model.
@@ -38,6 +39,55 @@ module EasyTalk
38
39
  base.include ActiveModel::Validations
39
40
  base.extend ActiveModel::Callbacks
40
41
  base.extend(ClassMethods)
42
+ base.include(InstanceMethods)
43
+
44
+ # Apply ActiveRecord-specific functionality if appropriate
45
+ return unless defined?(ActiveRecord) && base.ancestors.include?(ActiveRecord::Base)
46
+
47
+ base.extend(ActiveRecordClassMethods)
48
+ end
49
+
50
+ module InstanceMethods
51
+ def initialize(attributes = {})
52
+ @additional_properties = {}
53
+ super
54
+ end
55
+
56
+ def method_missing(method_name, *args)
57
+ method_string = method_name.to_s
58
+ if method_string.end_with?('=')
59
+ property_name = method_string.chomp('=')
60
+ if self.class.additional_properties_allowed?
61
+ @additional_properties[property_name] = args.first
62
+ else
63
+ super
64
+ end
65
+ elsif self.class.additional_properties_allowed? && @additional_properties.key?(method_string)
66
+ @additional_properties[method_string]
67
+ else
68
+ super
69
+ end
70
+ end
71
+
72
+ def respond_to_missing?(method_name, include_private = false)
73
+ method_string = method_name.to_s
74
+ method_string.end_with?('=') ? method_string.chomp('=') : method_string
75
+ self.class.additional_properties_allowed? || super
76
+ end
77
+
78
+ # Add to_hash method to convert defined properties to hash
79
+ def to_hash
80
+ return {} unless self.class.properties
81
+
82
+ self.class.properties.each_with_object({}) do |prop, hash|
83
+ hash[prop.to_s] = send(prop)
84
+ end
85
+ end
86
+
87
+ # Override as_json to include both defined and additional properties
88
+ def as_json(_options = {})
89
+ to_hash.merge(@additional_properties)
90
+ end
41
91
  end
42
92
 
43
93
  # Module containing class-level methods for defining and accessing the schema of a model.
@@ -46,7 +96,16 @@ module EasyTalk
46
96
  #
47
97
  # @return [Schema] The schema for the model.
48
98
  def schema
49
- @schema ||= build_schema(schema_definition)
99
+ @schema ||= if defined?(@schema_definition) && @schema_definition
100
+ # Schema defined explicitly via define_schema
101
+ build_schema(@schema_definition)
102
+ elsif respond_to?(:active_record_schema_definition)
103
+ # ActiveRecord model without explicit schema definition
104
+ build_schema(active_record_schema_definition)
105
+ else
106
+ # Default case - empty schema
107
+ {}
108
+ end
50
109
  end
51
110
 
52
111
  # Returns the reference template for the model.
@@ -92,9 +151,12 @@ module EasyTalk
92
151
  @schema_definition ||= {}
93
152
  end
94
153
 
95
- private
154
+ def additional_properties_allowed?
155
+ @schema_definition&.schema&.fetch(:additional_properties, false)
156
+ end
96
157
 
97
158
  # Builds the schema using the provided schema definition.
159
+ # This is the convergence point for all schema generation.
98
160
  #
99
161
  # @param schema_definition [SchemaDefinition] The schema definition.
100
162
  # @return [Schema] The validated schema.
@@ -102,5 +164,34 @@ module EasyTalk
102
164
  Builders::ObjectBuilder.new(schema_definition).build
103
165
  end
104
166
  end
167
+
168
+ # Module containing ActiveRecord-specific methods for schema generation
169
+ module ActiveRecordClassMethods
170
+ # Gets a SchemaDefinition that's built from the ActiveRecord database schema
171
+ #
172
+ # @return [SchemaDefinition] A schema definition built from the database
173
+ def active_record_schema_definition
174
+ @active_record_schema_definition ||= ActiveRecordSchemaBuilder.new(self).build_schema_definition
175
+ end
176
+
177
+ # Store enhancements to be applied to the schema
178
+ #
179
+ # @return [Hash] The schema enhancements
180
+ def schema_enhancements
181
+ @schema_enhancements ||= {}
182
+ end
183
+
184
+ # Enhance the generated schema with additional information
185
+ #
186
+ # @param enhancements [Hash] The schema enhancements
187
+ # @return [void]
188
+ def enhance_schema(enhancements)
189
+ @schema_enhancements = enhancements
190
+ # Clear cached values to force regeneration
191
+ @active_record_schema_definition = nil
192
+ @schema = nil
193
+ @json_schema = nil
194
+ end
195
+ end
105
196
  end
106
197
  end
@@ -76,7 +76,9 @@ module EasyTalk
76
76
  #
77
77
  # @return [Object] The built property.
78
78
  def build
79
- if builder
79
+ if nilable_type?
80
+ build_nilable_schema
81
+ elsif builder
80
82
  args = builder.collection_type? ? [name, type, constraints] : [name, constraints]
81
83
  builder.new(*args).build
82
84
  elsif type.respond_to?(:schema)
@@ -105,5 +107,27 @@ module EasyTalk
105
107
  def builder
106
108
  @builder ||= TYPE_TO_BUILDER[type.class.name.to_s] || TYPE_TO_BUILDER[type.name.to_s]
107
109
  end
110
+
111
+ private
112
+
113
+ def nilable_type?
114
+ return unless type.respond_to?(:types)
115
+ return unless type.types.all? { |t| t.respond_to?(:raw_type) }
116
+
117
+ type.types.any? { |t| t.raw_type == NilClass }
118
+ end
119
+
120
+ def build_nilable_schema
121
+ # Extract the non-nil type from the Union
122
+ actual_type = type.types.find { |t| t != NilClass }
123
+
124
+ # Create a property with the actual type
125
+ non_nil_schema = Property.new(name, actual_type, constraints).build
126
+
127
+ # Merge the types into an array
128
+ non_nil_schema.merge(
129
+ type: [non_nil_schema[:type], 'null']
130
+ )
131
+ end
108
132
  end
109
133
  end
@@ -3,8 +3,6 @@
3
3
  require_relative 'keywords'
4
4
 
5
5
  module EasyTalk
6
- class InvalidPropertyNameError < StandardError; end
7
-
8
6
  #
9
7
  #= EasyTalk \SchemaDefinition
10
8
  # SchemaDefinition provides the methods for defining a schema within the define_schema block.
@@ -20,6 +18,7 @@ module EasyTalk
20
18
 
21
19
  def initialize(name, schema = {})
22
20
  @schema = schema
21
+ @schema[:additional_properties] = false unless schema.key?(:additional_properties)
23
22
  @name = name
24
23
  end
25
24
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module EasyTalk
4
- VERSION = '1.0.1'
4
+ VERSION = '1.0.3'
5
5
  end
data/lib/easy_talk.rb CHANGED
@@ -2,9 +2,11 @@
2
2
 
3
3
  # The EasyTalk module is the main namespace for the gem.
4
4
  module EasyTalk
5
- class Error < StandardError; end
6
5
  require 'sorbet-runtime'
7
6
  require 'easy_talk/sorbet_extension'
7
+ require 'easy_talk/errors'
8
+ require 'easy_talk/errors_helper'
9
+ require 'easy_talk/configuration'
8
10
  require 'easy_talk/types/any_of'
9
11
  require 'easy_talk/types/all_of'
10
12
  require 'easy_talk/types/one_of'
@@ -14,6 +16,12 @@ module EasyTalk
14
16
  require 'easy_talk/tools/function_builder'
15
17
  require 'easy_talk/version'
16
18
 
17
- class UnsupportedTypeError < ArgumentError; end
18
- class UnsupportedConstraintError < ArgumentError; end
19
+ def self.assert_valid_property_options(property_name, options, *valid_keys)
20
+ valid_keys.flatten!
21
+ options.each_key do |k|
22
+ next if valid_keys.include?(k)
23
+
24
+ ErrorHelper.raise_unknown_option_error(property_name: property_name, option: options, valid_options: valid_keys)
25
+ end
26
+ end
19
27
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: easy_talk
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 1.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sergio Bayona
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2025-01-09 00:00:00.000000000 Z
10
+ date: 2025-03-11 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: activemodel
@@ -51,6 +51,20 @@ dependencies:
51
51
  - - ">="
52
52
  - !ruby/object:Gem::Version
53
53
  version: '0.5'
54
+ - !ruby/object:Gem::Dependency
55
+ name: activerecord
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: '7.0'
61
+ type: :development
62
+ prerelease: false
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: '7.0'
54
68
  - !ruby/object:Gem::Dependency
55
69
  name: pry-byebug
56
70
  requirement: !ruby/object:Gem::Requirement
@@ -163,6 +177,20 @@ dependencies:
163
177
  - - ">="
164
178
  - !ruby/object:Gem::Version
165
179
  version: '2.29'
180
+ - !ruby/object:Gem::Dependency
181
+ name: sqlite3
182
+ requirement: !ruby/object:Gem::Requirement
183
+ requirements:
184
+ - - ">="
185
+ - !ruby/object:Gem::Version
186
+ version: '2'
187
+ type: :development
188
+ prerelease: false
189
+ version_requirements: !ruby/object:Gem::Requirement
190
+ requirements:
191
+ - - ">="
192
+ - !ruby/object:Gem::Version
193
+ version: '2'
166
194
  description: Generate json-schema from plain Ruby classes.
167
195
  email:
168
196
  - bayona.sergio@gmail.com
@@ -184,6 +212,7 @@ files:
184
212
  - docs/about.markdown
185
213
  - docs/index.markdown
186
214
  - lib/easy_talk.rb
215
+ - lib/easy_talk/active_record_schema_builder.rb
187
216
  - lib/easy_talk/builders/all_of_builder.rb
188
217
  - lib/easy_talk/builders/any_of_builder.rb
189
218
  - lib/easy_talk/builders/base_builder.rb
@@ -201,6 +230,9 @@ files:
201
230
  - lib/easy_talk/builders/time_builder.rb
202
231
  - lib/easy_talk/builders/typed_array_builder.rb
203
232
  - lib/easy_talk/builders/union_builder.rb
233
+ - lib/easy_talk/configuration.rb
234
+ - lib/easy_talk/errors.rb
235
+ - lib/easy_talk/errors_helper.rb
204
236
  - lib/easy_talk/keywords.rb
205
237
  - lib/easy_talk/model.rb
206
238
  - lib/easy_talk/property.rb