easy_talk 1.0.4 → 3.0.0

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.
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.4
4
+ version: 3.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sergio Bayona
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2025-03-12 00:00:00.000000000 Z
10
+ date: 2025-09-03 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: activemodel
@@ -16,6 +16,9 @@ dependencies:
16
16
  - - ">="
17
17
  - !ruby/object:Gem::Version
18
18
  version: '7.0'
19
+ - - "<"
20
+ - !ruby/object:Gem::Version
21
+ version: '9.0'
19
22
  type: :runtime
20
23
  prerelease: false
21
24
  version_requirements: !ruby/object:Gem::Requirement
@@ -23,6 +26,9 @@ dependencies:
23
26
  - - ">="
24
27
  - !ruby/object:Gem::Version
25
28
  version: '7.0'
29
+ - - "<"
30
+ - !ruby/object:Gem::Version
31
+ version: '9.0'
26
32
  - !ruby/object:Gem::Dependency
27
33
  name: activesupport
28
34
  requirement: !ruby/object:Gem::Requirement
@@ -30,168 +36,49 @@ dependencies:
30
36
  - - ">="
31
37
  - !ruby/object:Gem::Version
32
38
  version: '7.0'
33
- type: :runtime
34
- prerelease: false
35
- version_requirements: !ruby/object:Gem::Requirement
36
- requirements:
37
- - - ">="
38
- - !ruby/object:Gem::Version
39
- version: '7.0'
40
- - !ruby/object:Gem::Dependency
41
- name: sorbet-runtime
42
- requirement: !ruby/object:Gem::Requirement
43
- requirements:
44
- - - ">="
39
+ - - "<"
45
40
  - !ruby/object:Gem::Version
46
- version: '0.5'
41
+ version: '9.0'
47
42
  type: :runtime
48
43
  prerelease: false
49
44
  version_requirements: !ruby/object:Gem::Requirement
50
- requirements:
51
- - - ">="
52
- - !ruby/object:Gem::Version
53
- version: '0.5'
54
- - !ruby/object:Gem::Dependency
55
- name: activerecord
56
- requirement: !ruby/object:Gem::Requirement
57
45
  requirements:
58
46
  - - ">="
59
47
  - !ruby/object:Gem::Version
60
48
  version: '7.0'
61
- type: :development
62
- prerelease: false
63
- version_requirements: !ruby/object:Gem::Requirement
64
- requirements:
65
- - - ">="
49
+ - - "<"
66
50
  - !ruby/object:Gem::Version
67
- version: '7.0'
51
+ version: '9.0'
68
52
  - !ruby/object:Gem::Dependency
69
- name: pry-byebug
53
+ name: js_regex
70
54
  requirement: !ruby/object:Gem::Requirement
71
55
  requirements:
72
- - - ">="
73
- - !ruby/object:Gem::Version
74
- version: '3.10'
75
- type: :development
76
- prerelease: false
77
- version_requirements: !ruby/object:Gem::Requirement
78
- requirements:
79
- - - ">="
80
- - !ruby/object:Gem::Version
81
- version: '3.10'
82
- - !ruby/object:Gem::Dependency
83
- name: rake
84
- requirement: !ruby/object:Gem::Requirement
85
- requirements:
86
- - - ">="
87
- - !ruby/object:Gem::Version
88
- version: '13.1'
89
- type: :development
90
- prerelease: false
91
- version_requirements: !ruby/object:Gem::Requirement
92
- requirements:
93
- - - ">="
94
- - !ruby/object:Gem::Version
95
- version: '13.1'
96
- - !ruby/object:Gem::Dependency
97
- name: rspec
98
- requirement: !ruby/object:Gem::Requirement
99
- requirements:
100
- - - ">="
56
+ - - "~>"
101
57
  - !ruby/object:Gem::Version
102
58
  version: '3.0'
103
- type: :development
59
+ type: :runtime
104
60
  prerelease: false
105
61
  version_requirements: !ruby/object:Gem::Requirement
106
62
  requirements:
107
- - - ">="
63
+ - - "~>"
108
64
  - !ruby/object:Gem::Version
109
65
  version: '3.0'
110
66
  - !ruby/object:Gem::Dependency
111
- name: rspec-json_expectations
112
- requirement: !ruby/object:Gem::Requirement
113
- requirements:
114
- - - ">="
115
- - !ruby/object:Gem::Version
116
- version: '2.0'
117
- type: :development
118
- prerelease: false
119
- version_requirements: !ruby/object:Gem::Requirement
120
- requirements:
121
- - - ">="
122
- - !ruby/object:Gem::Version
123
- version: '2.0'
124
- - !ruby/object:Gem::Dependency
125
- name: rspec-mocks
126
- requirement: !ruby/object:Gem::Requirement
127
- requirements:
128
- - - ">="
129
- - !ruby/object:Gem::Version
130
- version: '3.13'
131
- type: :development
132
- prerelease: false
133
- version_requirements: !ruby/object:Gem::Requirement
134
- requirements:
135
- - - ">="
136
- - !ruby/object:Gem::Version
137
- version: '3.13'
138
- - !ruby/object:Gem::Dependency
139
- name: rubocop
140
- requirement: !ruby/object:Gem::Requirement
141
- requirements:
142
- - - ">="
143
- - !ruby/object:Gem::Version
144
- version: '1.21'
145
- type: :development
146
- prerelease: false
147
- version_requirements: !ruby/object:Gem::Requirement
148
- requirements:
149
- - - ">="
150
- - !ruby/object:Gem::Version
151
- version: '1.21'
152
- - !ruby/object:Gem::Dependency
153
- name: rubocop-rake
154
- requirement: !ruby/object:Gem::Requirement
155
- requirements:
156
- - - ">="
157
- - !ruby/object:Gem::Version
158
- version: '0.6'
159
- type: :development
160
- prerelease: false
161
- version_requirements: !ruby/object:Gem::Requirement
162
- requirements:
163
- - - ">="
164
- - !ruby/object:Gem::Version
165
- version: '0.6'
166
- - !ruby/object:Gem::Dependency
167
- name: rubocop-rspec
168
- requirement: !ruby/object:Gem::Requirement
169
- requirements:
170
- - - ">="
171
- - !ruby/object:Gem::Version
172
- version: '2.29'
173
- type: :development
174
- prerelease: false
175
- version_requirements: !ruby/object:Gem::Requirement
176
- requirements:
177
- - - ">="
178
- - !ruby/object:Gem::Version
179
- version: '2.29'
180
- - !ruby/object:Gem::Dependency
181
- name: sqlite3
67
+ name: sorbet-runtime
182
68
  requirement: !ruby/object:Gem::Requirement
183
69
  requirements:
184
- - - ">="
70
+ - - "~>"
185
71
  - !ruby/object:Gem::Version
186
- version: '2'
187
- type: :development
72
+ version: '0.5'
73
+ type: :runtime
188
74
  prerelease: false
189
75
  version_requirements: !ruby/object:Gem::Requirement
190
76
  requirements:
191
- - - ">="
77
+ - - "~>"
192
78
  - !ruby/object:Gem::Version
193
- version: '2'
194
- description: Generate json-schema from plain Ruby classes.
79
+ version: '0.5'
80
+ description: Generate json-schema from plain Ruby classes with ActiveModel integration
81
+ for validations and serialization.
195
82
  email:
196
83
  - bayona.sergio@gmail.com
197
84
  executables: []
@@ -211,8 +98,8 @@ files:
211
98
  - docs/_posts/2024-05-07-welcome-to-jekyll.markdown
212
99
  - docs/about.markdown
213
100
  - docs/index.markdown
101
+ - easy_talk.gemspec
214
102
  - lib/easy_talk.rb
215
- - lib/easy_talk/active_record_schema_builder.rb
216
103
  - lib/easy_talk/builders/base_builder.rb
217
104
  - lib/easy_talk/builders/boolean_builder.rb
218
105
  - lib/easy_talk/builders/collection_helpers.rb
@@ -236,6 +123,7 @@ files:
236
123
  - lib/easy_talk/tools/function_builder.rb
237
124
  - lib/easy_talk/types/base_composer.rb
238
125
  - lib/easy_talk/types/composer.rb
126
+ - lib/easy_talk/validation_builder.rb
239
127
  - lib/easy_talk/version.rb
240
128
  homepage: https://github.com/sergiobayona/easy_talk
241
129
  licenses:
@@ -243,8 +131,8 @@ licenses:
243
131
  metadata:
244
132
  allowed_push_host: https://rubygems.org
245
133
  homepage_uri: https://github.com/sergiobayona/easy_talk
246
- source_code_uri: https://github.com/sergiobayona/easy_talk
247
134
  changelog_uri: https://github.com/sergiobayona/easy_talk/blob/main/CHANGELOG.md
135
+ rubygems_mfa_required: 'true'
248
136
  rdoc_options: []
249
137
  require_paths:
250
138
  - lib
@@ -261,5 +149,5 @@ required_rubygems_version: !ruby/object:Gem::Requirement
261
149
  requirements: []
262
150
  rubygems_version: 3.6.2
263
151
  specification_version: 4
264
- summary: Generate json-schema from Ruby classes.
152
+ summary: Generate json-schema from Ruby classes with ActiveModel integration.
265
153
  test_files: []
@@ -1,295 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module EasyTalk
4
- # This class is responsible for building a SchemaDefinition from an ActiveRecord model
5
- # It analyzes the database schema and creates a SchemaDefinition that can be
6
- # passed to ObjectBuilder for final schema generation
7
- class ActiveRecordSchemaBuilder
8
- # Mapping of ActiveRecord column types to Ruby classes
9
- COLUMN_TYPE_MAP = {
10
- string: String,
11
- text: String,
12
- integer: Integer,
13
- bigint: Integer,
14
- float: Float,
15
- decimal: Float,
16
- boolean: T::Boolean,
17
- date: Date,
18
- datetime: DateTime,
19
- timestamp: DateTime,
20
- time: Time,
21
- json: Hash,
22
- jsonb: Hash
23
- }.freeze
24
-
25
- # Mapping for format constraints based on column type
26
- FORMAT_MAP = {
27
- date: 'date',
28
- datetime: 'date-time',
29
- timestamp: 'date-time',
30
- time: 'time'
31
- }.freeze
32
-
33
- attr_reader :model
34
-
35
- # Initialize the builder with an ActiveRecord model
36
- #
37
- # @param model [Class] An ActiveRecord model class
38
- # @raise [ArgumentError] If the provided class is not an ActiveRecord model
39
- def initialize(model)
40
- raise ArgumentError, 'Class must be an ActiveRecord model' unless model.ancestors.include?(ActiveRecord::Base)
41
-
42
- @model = model
43
- end
44
-
45
- # Build a SchemaDefinition object from the ActiveRecord model
46
- #
47
- # @return [EasyTalk::SchemaDefinition] A schema definition built from the database structure
48
- def build_schema_definition
49
- schema_def = SchemaDefinition.new(model.name)
50
-
51
- # Apply basic schema metadata
52
- apply_schema_metadata(schema_def)
53
-
54
- # Add all database columns as properties
55
- add_column_properties(schema_def)
56
-
57
- # Add model associations as properties
58
- add_association_properties(schema_def) unless EasyTalk.configuration.exclude_associations
59
-
60
- # Add virtual properties defined in schema_enhancements
61
- add_virtual_properties(schema_def)
62
-
63
- schema_def
64
- end
65
-
66
- private
67
-
68
- # Set top-level schema metadata like title, description, and additionalProperties
69
- #
70
- # @param schema_def [EasyTalk::SchemaDefinition] The schema definition to modify
71
- def apply_schema_metadata(schema_def)
72
- # Set title (from enhancements or derive from model name)
73
- title = schema_enhancements['title'] || model.name.demodulize.humanize
74
- schema_def.title(title)
75
-
76
- # Set description if provided
77
- if (description = schema_enhancements['description'])
78
- schema_def.description(description)
79
- end
80
-
81
- # Set additionalProperties (from enhancements or configuration default)
82
- additional_props = if schema_enhancements.key?('additionalProperties')
83
- schema_enhancements['additionalProperties']
84
- else
85
- EasyTalk.configuration.default_additional_properties
86
- end
87
- schema_def.additional_properties(additional_props)
88
- end
89
-
90
- # Add properties based on database columns
91
- #
92
- # @param schema_def [EasyTalk::SchemaDefinition] The schema definition to modify
93
- def add_column_properties(schema_def)
94
- filtered_columns.each do |column|
95
- # Get column enhancement info if it exists
96
- column_enhancements = schema_enhancements.dig('properties', column.name.to_s) || {}
97
-
98
- # Map the database type to Ruby type
99
- ruby_type = COLUMN_TYPE_MAP.fetch(column.type, String)
100
-
101
- # If the column is nullable, wrap the type in a Union with NilClass
102
- ruby_type = T::Types::Union.new([ruby_type, NilClass]) if column.null
103
-
104
- # Build constraints hash for this column
105
- constraints = build_column_constraints(column, column_enhancements)
106
-
107
- # Add the property to schema definition
108
- schema_def.property(column.name.to_sym, ruby_type, constraints)
109
- end
110
- end
111
-
112
- # Build constraints hash for a database column
113
- #
114
- # @param column [ActiveRecord::ConnectionAdapters::Column] The database column
115
- # @param enhancements [Hash] Any schema enhancements for this column
116
- # @return [Hash] The constraints hash
117
- def build_column_constraints(column, enhancements)
118
- constraints = {
119
- optional: enhancements['optional'],
120
- description: enhancements['description'],
121
- title: enhancements['title']
122
- }
123
-
124
- # Add format constraint for date/time columns
125
- if (format = FORMAT_MAP[column.type])
126
- constraints[:format] = format
127
- end
128
-
129
- # Add max_length for string columns with limits
130
- constraints[:max_length] = column.limit if column.type == :string && column.limit
131
-
132
- # Add precision/scale for numeric columns
133
- if column.type == :decimal && column.precision
134
- constraints[:precision] = column.precision
135
- constraints[:scale] = column.scale if column.scale
136
- end
137
-
138
- # Add default value if present and not a proc
139
- constraints[:default] = column.default if column.default && !column.default.is_a?(Proc)
140
-
141
- # Remove nil values
142
- constraints.compact
143
- end
144
-
145
- # Add properties based on ActiveRecord associations
146
- #
147
- # @param schema_def [EasyTalk::SchemaDefinition] The schema definition to modify
148
- def add_association_properties(schema_def)
149
- model.reflect_on_all_associations.each do |association|
150
- # Skip if we can't determine the class or it's in the association exclusion list
151
- next if association_excluded?(association)
152
-
153
- # Get association enhancement info if it exists
154
- assoc_enhancements = schema_enhancements.dig('properties', association.name.to_s) || {}
155
-
156
- case association.macro
157
- when :belongs_to, :has_one
158
- schema_def.property(
159
- association.name,
160
- association.klass,
161
- { optional: assoc_enhancements['optional'], description: assoc_enhancements['description'] }.compact
162
- )
163
- when :has_many, :has_and_belongs_to_many
164
- schema_def.property(
165
- association.name,
166
- T::Array[association.klass],
167
- { optional: assoc_enhancements['optional'], description: assoc_enhancements['description'] }.compact
168
- )
169
- end
170
- end
171
- end
172
-
173
- # Add virtual properties defined in schema_enhancements
174
- #
175
- # @param schema_def [EasyTalk::SchemaDefinition] The schema definition to modify
176
- def add_virtual_properties(schema_def)
177
- return unless schema_enhancements['properties']
178
-
179
- schema_enhancements['properties'].each do |name, options|
180
- next unless options['virtual']
181
-
182
- # Map string type name to Ruby class
183
- ruby_type = map_type_string_to_ruby_class(options['type'] || 'string')
184
-
185
- # Build constraints for virtual property
186
- constraints = {
187
- description: options['description'],
188
- title: options['title'],
189
- optional: options['optional'],
190
- format: options['format'],
191
- default: options['default'],
192
- min_length: options['minLength'],
193
- max_length: options['maxLength'],
194
- enum: options['enum']
195
- }.compact
196
-
197
- # Add the virtual property
198
- schema_def.property(name.to_sym, ruby_type, constraints)
199
- end
200
- end
201
-
202
- # Map a type string to a Ruby class
203
- #
204
- # @param type_str [String] The type string (e.g., 'string', 'integer')
205
- # @return [Class] The corresponding Ruby class
206
- def map_type_string_to_ruby_class(type_str)
207
- case type_str.to_s.downcase
208
- when 'string' then String
209
- when 'integer' then Integer
210
- when 'number' then Float
211
- when 'boolean' then T::Boolean
212
- when 'object' then Hash
213
- when 'array' then Array
214
- when 'date' then Date
215
- when 'datetime' then DateTime
216
- when 'time' then Time
217
- else String # Default fallback
218
- end
219
- end
220
-
221
- # Get all columns that should be included in the schema
222
- #
223
- # @return [Array<ActiveRecord::ConnectionAdapters::Column>] Filtered columns
224
- def filtered_columns
225
- model.columns.reject do |column|
226
- config = EasyTalk.configuration
227
- excluded_columns.include?(column.name.to_sym) ||
228
- (config.exclude_primary_key && column.name == model.primary_key) ||
229
- (config.exclude_timestamps && timestamp_column?(column.name)) ||
230
- (config.exclude_foreign_keys && foreign_key_column?(column.name))
231
- end
232
- end
233
-
234
- # Check if a column is a timestamp column
235
- #
236
- # @param column_name [String] The column name
237
- # @return [Boolean] True if the column is a timestamp column
238
- def timestamp_column?(column_name)
239
- %w[created_at updated_at].include?(column_name)
240
- end
241
-
242
- # Check if a column is a foreign key column
243
- #
244
- # @param column_name [String] The column name
245
- # @return [Boolean] True if the column is a foreign key column
246
- def foreign_key_column?(column_name)
247
- column_name.end_with?('_id')
248
- end
249
-
250
- # Check if an association should be excluded
251
- #
252
- # @param association [ActiveRecord::Reflection::AssociationReflection] The association
253
- # @return [Boolean] True if the association should be excluded
254
- def association_excluded?(association)
255
- !association.klass ||
256
- excluded_associations.include?(association.name.to_sym) ||
257
- association.options[:polymorphic] # Skip polymorphic associations (complex to model)
258
- end
259
-
260
- # Get schema enhancements
261
- #
262
- # @return [Hash] Schema enhancements
263
- def schema_enhancements
264
- @schema_enhancements ||= if model.respond_to?(:schema_enhancements)
265
- model.schema_enhancements.deep_transform_keys(&:to_s)
266
- else
267
- {}
268
- end
269
- end
270
-
271
- # Get all excluded columns
272
- #
273
- # @return [Array<Symbol>] Excluded column names
274
- def excluded_columns
275
- @excluded_columns ||= begin
276
- config = EasyTalk.configuration
277
- global_exclusions = config.excluded_columns || []
278
- model_exclusions = schema_enhancements['ignore'] || []
279
-
280
- # Combine and convert to symbols for consistent comparison
281
- (global_exclusions + model_exclusions).map(&:to_sym)
282
- end
283
- end
284
-
285
- # Get all excluded associations
286
- #
287
- # @return [Array<Symbol>] Excluded association names
288
- def excluded_associations
289
- @excluded_associations ||= begin
290
- model_exclusions = schema_enhancements['ignore_associations'] || []
291
- model_exclusions.map(&:to_sym)
292
- end
293
- end
294
- end
295
- end