facera 0.1.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.
Files changed (63) hide show
  1. checksums.yaml +7 -0
  2. data/.DS_Store +0 -0
  3. data/.github/workflows/gem-push.yml +42 -0
  4. data/.github/workflows/ruby.yml +35 -0
  5. data/.gitignore +39 -0
  6. data/.rspec +3 -0
  7. data/CHANGELOG.md +137 -0
  8. data/Gemfile +9 -0
  9. data/LICENSE +21 -0
  10. data/README.md +309 -0
  11. data/Rakefile +6 -0
  12. data/examples/01_core_dsl.rb +132 -0
  13. data/examples/02_facet_system.rb +216 -0
  14. data/examples/03_api_generation.rb +117 -0
  15. data/examples/04_auto_mounting.rb +182 -0
  16. data/examples/05_adapters.rb +196 -0
  17. data/examples/README.md +184 -0
  18. data/examples/server/README.md +376 -0
  19. data/examples/server/adapters/payment_adapter.rb +139 -0
  20. data/examples/server/application.rb +17 -0
  21. data/examples/server/config/facera.rb +33 -0
  22. data/examples/server/config.ru +10 -0
  23. data/examples/server/cores/payment_core.rb +82 -0
  24. data/examples/server/facets/external_facet.rb +38 -0
  25. data/examples/server/facets/internal_facet.rb +33 -0
  26. data/examples/server/facets/operator_facet.rb +48 -0
  27. data/facera.gemspec +30 -0
  28. data/img/facera.png +0 -0
  29. data/lib/facera/adapter.rb +83 -0
  30. data/lib/facera/attribute.rb +95 -0
  31. data/lib/facera/auto_mount.rb +124 -0
  32. data/lib/facera/capability.rb +117 -0
  33. data/lib/facera/capability_access.rb +59 -0
  34. data/lib/facera/configuration.rb +83 -0
  35. data/lib/facera/context.rb +29 -0
  36. data/lib/facera/core.rb +65 -0
  37. data/lib/facera/dsl.rb +41 -0
  38. data/lib/facera/entity.rb +50 -0
  39. data/lib/facera/error_formatter.rb +100 -0
  40. data/lib/facera/errors.rb +40 -0
  41. data/lib/facera/executor.rb +265 -0
  42. data/lib/facera/facet.rb +103 -0
  43. data/lib/facera/field_visibility.rb +69 -0
  44. data/lib/facera/generators/core_generator.rb +23 -0
  45. data/lib/facera/generators/facet_generator.rb +25 -0
  46. data/lib/facera/generators/install_generator.rb +64 -0
  47. data/lib/facera/generators/templates/core.rb.tt +49 -0
  48. data/lib/facera/generators/templates/facet.rb.tt +23 -0
  49. data/lib/facera/grape/api_generator.rb +59 -0
  50. data/lib/facera/grape/endpoint_generator.rb +316 -0
  51. data/lib/facera/grape/entity_generator.rb +89 -0
  52. data/lib/facera/grape.rb +14 -0
  53. data/lib/facera/introspection.rb +111 -0
  54. data/lib/facera/introspection_api.rb +66 -0
  55. data/lib/facera/invariant.rb +26 -0
  56. data/lib/facera/loader.rb +153 -0
  57. data/lib/facera/openapi_generator.rb +338 -0
  58. data/lib/facera/railtie.rb +51 -0
  59. data/lib/facera/registry.rb +34 -0
  60. data/lib/facera/tasks/routes.rake +66 -0
  61. data/lib/facera/version.rb +3 -0
  62. data/lib/facera.rb +35 -0
  63. metadata +137 -0
@@ -0,0 +1,338 @@
1
+ module Facera
2
+ class OpenAPIGenerator
3
+ attr_reader :facet_name, :facet, :core
4
+
5
+ def initialize(facet_name)
6
+ @facet_name = facet_name
7
+ @facet = Registry.facets[facet_name]
8
+ raise "Facet #{facet_name} not found" unless @facet
9
+ @core = Registry.cores[@facet.core_name]
10
+ raise "Core #{@facet.core_name} not found" unless @core
11
+ end
12
+
13
+ def generate
14
+ {
15
+ openapi: '3.0.3',
16
+ info: info_section,
17
+ servers: servers_section,
18
+ paths: paths_section,
19
+ components: components_section
20
+ }
21
+ end
22
+
23
+ private
24
+
25
+ def info_section
26
+ {
27
+ title: "#{@facet_name.to_s.capitalize} API",
28
+ description: @facet.description || "#{@facet_name} facet of #{@core.name} core",
29
+ version: Facera::VERSION,
30
+ contact: {
31
+ name: 'Facera Framework'
32
+ }
33
+ }
34
+ end
35
+
36
+ def servers_section
37
+ config = Facera.configuration
38
+ base_path = config.base_path
39
+ facet_path = config.path_for_facet(@facet_name)
40
+
41
+ [{
42
+ url: "#{base_path}#{facet_path}",
43
+ description: "#{@facet_name.to_s.capitalize} API"
44
+ }]
45
+ end
46
+
47
+ def paths_section
48
+ paths = {}
49
+
50
+ # Health endpoint
51
+ paths['/health'] = {
52
+ get: {
53
+ summary: 'Health check',
54
+ tags: ['Health'],
55
+ responses: {
56
+ '200' => {
57
+ description: 'Service is healthy',
58
+ content: {
59
+ 'application/json' => {
60
+ schema: {
61
+ type: 'object',
62
+ properties: {
63
+ status: { type: 'string', example: 'ok' },
64
+ facet: { type: 'string', example: @facet_name.to_s },
65
+ core: { type: 'string', example: @core.name.to_s },
66
+ timestamp: { type: 'string', format: 'date-time' }
67
+ }
68
+ }
69
+ }
70
+ }
71
+ }
72
+ }
73
+ }
74
+ }
75
+
76
+ # Entity endpoints
77
+ @facet.field_visibilities.each do |entity_name, visibility|
78
+ entity = @core.entities[entity_name]
79
+
80
+ # Collection endpoints
81
+ collection_path = "/#{entity_name}s"
82
+ paths[collection_path] = {}
83
+
84
+ # POST (create)
85
+ if capability_allowed?("create_#{entity_name}")
86
+ paths[collection_path][:post] = create_operation(entity_name, entity, visibility)
87
+ end
88
+
89
+ # GET (list)
90
+ if capability_allowed?("list_#{entity_name}s")
91
+ paths[collection_path][:get] = list_operation(entity_name, entity, visibility)
92
+ end
93
+
94
+ # Single resource endpoints
95
+ resource_path = "/#{entity_name}s/{id}"
96
+ paths[resource_path] = {}
97
+
98
+ # GET (retrieve)
99
+ if capability_allowed?("get_#{entity_name}")
100
+ paths[resource_path][:get] = get_operation(entity_name, entity, visibility)
101
+ end
102
+
103
+ # Action endpoints
104
+ @core.capabilities.each do |cap_name, capability|
105
+ next unless capability.type == :action
106
+ next unless capability.entity == entity_name
107
+ next unless capability_allowed?(cap_name)
108
+
109
+ action_name = cap_name.to_s.sub("#{entity_name}_", '')
110
+ action_path = "/#{entity_name}s/{id}/#{action_name}"
111
+
112
+ paths[action_path] = {
113
+ post: action_operation(cap_name, capability, entity, visibility)
114
+ }
115
+ end
116
+ end
117
+
118
+ paths
119
+ end
120
+
121
+ def create_operation(entity_name, entity, visibility)
122
+ {
123
+ summary: "Create a new #{entity_name}",
124
+ tags: [entity_name.to_s.capitalize],
125
+ requestBody: {
126
+ required: true,
127
+ content: {
128
+ 'application/json' => {
129
+ schema: request_schema(entity_name, entity, visibility)
130
+ }
131
+ }
132
+ },
133
+ responses: {
134
+ '201' => {
135
+ description: "#{entity_name.to_s.capitalize} created successfully",
136
+ content: {
137
+ 'application/json' => {
138
+ schema: response_schema(entity_name, entity, visibility)
139
+ }
140
+ }
141
+ },
142
+ '400' => error_response('Validation error'),
143
+ '401' => error_response('Unauthorized')
144
+ }
145
+ }
146
+ end
147
+
148
+ def list_operation(entity_name, entity, visibility)
149
+ {
150
+ summary: "List #{entity_name}s",
151
+ tags: [entity_name.to_s.capitalize],
152
+ parameters: [
153
+ { name: 'limit', in: 'query', schema: { type: 'integer', default: 20 }, description: 'Number of results' },
154
+ { name: 'offset', in: 'query', schema: { type: 'integer', default: 0 }, description: 'Pagination offset' }
155
+ ],
156
+ responses: {
157
+ '200' => {
158
+ description: "List of #{entity_name}s",
159
+ content: {
160
+ 'application/json' => {
161
+ schema: {
162
+ type: 'array',
163
+ items: response_schema(entity_name, entity, visibility)
164
+ }
165
+ }
166
+ }
167
+ },
168
+ '401' => error_response('Unauthorized')
169
+ }
170
+ }
171
+ end
172
+
173
+ def get_operation(entity_name, entity, visibility)
174
+ {
175
+ summary: "Get a #{entity_name}",
176
+ tags: [entity_name.to_s.capitalize],
177
+ parameters: [
178
+ { name: 'id', in: 'path', required: true, schema: { type: 'string', format: 'uuid' } }
179
+ ],
180
+ responses: {
181
+ '200' => {
182
+ description: "#{entity_name.to_s.capitalize} details",
183
+ content: {
184
+ 'application/json' => {
185
+ schema: response_schema(entity_name, entity, visibility)
186
+ }
187
+ }
188
+ },
189
+ '404' => error_response('Not found'),
190
+ '401' => error_response('Unauthorized')
191
+ }
192
+ }
193
+ end
194
+
195
+ def action_operation(cap_name, capability, entity, visibility)
196
+ action_name = cap_name.to_s.sub("#{capability.entity}_", '')
197
+
198
+ {
199
+ summary: "#{action_name.capitalize} a #{capability.entity}",
200
+ tags: [capability.entity.to_s.capitalize],
201
+ parameters: [
202
+ { name: 'id', in: 'path', required: true, schema: { type: 'string', format: 'uuid' } }
203
+ ],
204
+ requestBody: capability.required_params.any? || capability.optional_params.any? ? {
205
+ content: {
206
+ 'application/json' => {
207
+ schema: {
208
+ type: 'object',
209
+ properties: (capability.required_params + capability.optional_params)
210
+ .reject { |p| p == :id }
211
+ .map { |param| [param, { type: 'string' }] }
212
+ .to_h,
213
+ required: capability.required_params.reject { |p| p == :id }
214
+ }
215
+ }
216
+ }
217
+ } : nil,
218
+ responses: {
219
+ '200' => {
220
+ description: "#{capability.entity.to_s.capitalize} #{action_name}d successfully",
221
+ content: {
222
+ 'application/json' => {
223
+ schema: response_schema(capability.entity, entity, visibility)
224
+ }
225
+ }
226
+ },
227
+ '400' => error_response('Validation error'),
228
+ '404' => error_response('Not found'),
229
+ '401' => error_response('Unauthorized')
230
+ }
231
+ }.compact
232
+ end
233
+
234
+ def request_schema(entity_name, entity, visibility)
235
+ properties = {}
236
+ required = []
237
+
238
+ entity.attributes.each do |attr_name, attr|
239
+ next if attr.immutable? && attr_name != :id
240
+
241
+ properties[attr_name] = attribute_to_schema(attr)
242
+ required << attr_name if attr.required?
243
+ end
244
+
245
+ {
246
+ type: 'object',
247
+ properties: properties,
248
+ required: required
249
+ }
250
+ end
251
+
252
+ def response_schema(entity_name, entity, visibility)
253
+ properties = {}
254
+ visible_fields = visibility.visible_field_names(entity)
255
+
256
+ visible_fields.each do |field_name|
257
+ if entity.attributes[field_name]
258
+ properties[field_name] = attribute_to_schema(entity.attributes[field_name])
259
+ end
260
+ end
261
+
262
+ # Add computed fields
263
+ visibility.computed_fields.each do |computed_name, _block|
264
+ properties[computed_name] = { type: 'string', description: 'Computed field' }
265
+ end
266
+
267
+ {
268
+ type: 'object',
269
+ properties: properties
270
+ }
271
+ end
272
+
273
+ def attribute_to_schema(attr)
274
+ schema = { type: type_to_openapi(attr.type) }
275
+ schema[:enum] = attr.enum_values if attr.enum_values.any?
276
+ schema[:format] = 'uuid' if attr.type == :uuid
277
+ schema[:format] = 'date-time' if attr.type == :datetime
278
+ schema
279
+ end
280
+
281
+ def type_to_openapi(type)
282
+ case type
283
+ when :string, :text, :uuid then 'string'
284
+ when :integer then 'integer'
285
+ when :decimal, :money then 'number'
286
+ when :boolean then 'boolean'
287
+ when :datetime, :date then 'string'
288
+ when :json, :jsonb then 'object'
289
+ else 'string'
290
+ end
291
+ end
292
+
293
+ def error_response(description)
294
+ {
295
+ description: description,
296
+ content: {
297
+ 'application/json' => {
298
+ schema: {
299
+ type: 'object',
300
+ properties: {
301
+ error: { type: 'string' },
302
+ message: { type: 'string' }
303
+ }
304
+ }
305
+ }
306
+ }
307
+ }
308
+ end
309
+
310
+ def capability_allowed?(cap_name)
311
+ @facet.capability_allowed?(cap_name.to_sym)
312
+ end
313
+
314
+ def components_section
315
+ {
316
+ securitySchemes: {
317
+ bearerAuth: {
318
+ type: 'http',
319
+ scheme: 'bearer',
320
+ bearerFormat: 'JWT'
321
+ }
322
+ }
323
+ }
324
+ end
325
+
326
+ class << self
327
+ def for_facet(facet_name)
328
+ new(facet_name).generate
329
+ end
330
+
331
+ def generate_all
332
+ Registry.facets.keys.map do |facet_name|
333
+ [facet_name, for_facet(facet_name)]
334
+ end.to_h
335
+ end
336
+ end
337
+ end
338
+ end
@@ -0,0 +1,51 @@
1
+ if defined?(Rails)
2
+ module Facera
3
+ class Railtie < Rails::Railtie
4
+ railtie_name :facera
5
+
6
+ config.facera = Facera.configuration
7
+
8
+ # Auto-load facet and core definitions
9
+ initializer "facera.load_definitions", before: :load_config_initializers do
10
+ # Use Facera's loader for automatic discovery
11
+ Facera.load_all!
12
+ end
13
+
14
+ # Auto-mount facets after initialization
15
+ initializer "facera.auto_mount", after: :load_config_initializers do |app|
16
+ # Check if user has created a facera initializer
17
+ facera_initializer = Rails.root.join('config/initializers/facera.rb')
18
+
19
+ if File.exist?(facera_initializer)
20
+ # User has control via initializer, don't auto-mount unless they call it
21
+ Rails.logger.info "Facera: Configuration found at config/initializers/facera.rb"
22
+ else
23
+ # No initializer, auto-mount with defaults
24
+ if Registry.facets.any?
25
+ Rails.logger.info "Facera: Auto-mounting #{Registry.facets.count} facets..."
26
+ Facera.auto_mount!(app)
27
+ end
28
+ end
29
+ end
30
+
31
+ # Reload facets in development mode
32
+ config.to_prepare do
33
+ if Rails.env.development?
34
+ Facera.load_all!
35
+ end
36
+ end
37
+
38
+ # Add rake tasks
39
+ rake_tasks do
40
+ load 'facera/tasks/routes.rake'
41
+ end
42
+
43
+ # Add generators
44
+ generators do
45
+ require 'facera/generators/install_generator'
46
+ require 'facera/generators/core_generator'
47
+ require 'facera/generators/facet_generator'
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,34 @@
1
+ module Facera
2
+ module Registry
3
+ class << self
4
+ def cores
5
+ @cores ||= {}
6
+ end
7
+
8
+ def facets
9
+ @facets ||= {}
10
+ end
11
+
12
+ def register_core(name, core)
13
+ cores[name.to_sym] = core
14
+ end
15
+
16
+ def register_facet(name, facet)
17
+ facets[name.to_sym] = facet
18
+ end
19
+
20
+ def find_core(name)
21
+ cores[name.to_sym] or raise Error, "Core '#{name}' not found"
22
+ end
23
+
24
+ def find_facet(name)
25
+ facets[name.to_sym] or raise Error, "Facet '#{name}' not found"
26
+ end
27
+
28
+ def reset!
29
+ @cores = {}
30
+ @facets = {}
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,66 @@
1
+ namespace :facera do
2
+ desc "Show all Facera routes"
3
+ task routes: :environment do
4
+ puts "\n" + "=" * 80
5
+ puts "Facera Routes"
6
+ puts "=" * 80
7
+
8
+ if Facera::Registry.facets.empty?
9
+ puts "\nNo facets defined."
10
+ puts "Create facets in app/facets/ or use 'rails g facera:facet NAME --core=CORE'"
11
+ puts "=" * 80 + "\n"
12
+ next
13
+ end
14
+
15
+ Facera::Registry.facets.each do |name, facet|
16
+ config = Facera.configuration
17
+ next unless config.facet_enabled?(name)
18
+
19
+ path_prefix = "#{config.base_path}#{config.path_for_facet(name)}"
20
+ api = Facera::Grape::APIGenerator.for_facet(name)
21
+
22
+ puts "\n#{name.to_s.upcase} (#{facet.description})"
23
+ puts " Base: #{path_prefix}"
24
+ puts " Capabilities: #{facet.allowed_capabilities.count}/#{facet.core.capabilities.count}"
25
+ puts " Routes:"
26
+
27
+ # Group routes by resource
28
+ api.routes.group_by { |r| r.path.split('/')[1] }.each do |resource, routes|
29
+ routes.each do |route|
30
+ method = route.request_method.ljust(7)
31
+ path = "#{path_prefix}#{route.path.gsub('(.:format)', '')}"
32
+ puts " #{method} #{path}"
33
+ end
34
+ end
35
+ end
36
+
37
+ puts "\n" + "=" * 80 + "\n"
38
+ end
39
+
40
+ desc "Show Facera configuration"
41
+ task config: :environment do
42
+ config = Facera.configuration
43
+
44
+ puts "\n" + "=" * 80
45
+ puts "Facera Configuration"
46
+ puts "=" * 80
47
+ puts "\nGeneral:"
48
+ puts " Base path: #{config.base_path}"
49
+ puts " Version: #{config.version}"
50
+ puts " Dashboard: #{config.dashboard}"
51
+ puts " Generate docs: #{config.generate_docs}"
52
+
53
+ puts "\nFacets:"
54
+ puts " Defined: #{Facera::Registry.facets.count}"
55
+ puts " Enabled: #{Facera::Registry.facets.count - config.disabled_facets.count}"
56
+ puts " Disabled: #{config.disabled_facets.join(', ')}" if config.disabled_facets.any?
57
+
58
+ puts "\nCores:"
59
+ puts " Defined: #{Facera::Registry.cores.count}"
60
+ Facera::Registry.cores.each do |name, core|
61
+ puts " #{name}: #{core.entities.count} entities, #{core.capabilities.count} capabilities"
62
+ end
63
+
64
+ puts "\n" + "=" * 80 + "\n"
65
+ end
66
+ end
@@ -0,0 +1,3 @@
1
+ module Facera
2
+ VERSION = "0.1.0"
3
+ end
data/lib/facera.rb ADDED
@@ -0,0 +1,35 @@
1
+ require_relative "facera/version"
2
+ require_relative "facera/registry"
3
+ require_relative "facera/context"
4
+ require_relative "facera/attribute"
5
+ require_relative "facera/entity"
6
+ require_relative "facera/invariant"
7
+ require_relative "facera/capability"
8
+ require_relative "facera/core"
9
+ require_relative "facera/errors"
10
+ require_relative "facera/error_formatter"
11
+ require_relative "facera/field_visibility"
12
+ require_relative "facera/capability_access"
13
+ require_relative "facera/facet"
14
+ require_relative "facera/adapter"
15
+ require_relative "facera/dsl"
16
+ require_relative "facera/executor"
17
+ require_relative "facera/grape"
18
+ require_relative "facera/configuration"
19
+ require_relative "facera/loader"
20
+ require_relative "facera/introspection"
21
+ require_relative "facera/openapi_generator"
22
+ require_relative "facera/introspection_api"
23
+ require_relative "facera/auto_mount"
24
+
25
+ # Load Rails integration if Rails is present
26
+ require_relative "facera/railtie" if defined?(Rails)
27
+
28
+ module Facera
29
+ class Error < StandardError; end
30
+
31
+ # Convenience method to create APIs
32
+ def self.api_for(facet_name)
33
+ Grape::APIGenerator.for_facet(facet_name)
34
+ end
35
+ end
metadata ADDED
@@ -0,0 +1,137 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: facera
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Juan Carlos Garcia
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2026-03-09 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: grape
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: grape-entity
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.0'
41
+ description: Facera allows you to define your system once as a semantic core and expose
42
+ it through multiple facets, each tailored to different consumers while remaining
43
+ logically consistent.
44
+ email:
45
+ - jugade92@gmail.com
46
+ executables: []
47
+ extensions: []
48
+ extra_rdoc_files: []
49
+ files:
50
+ - ".DS_Store"
51
+ - ".github/workflows/gem-push.yml"
52
+ - ".github/workflows/ruby.yml"
53
+ - ".gitignore"
54
+ - ".rspec"
55
+ - CHANGELOG.md
56
+ - Gemfile
57
+ - LICENSE
58
+ - README.md
59
+ - Rakefile
60
+ - examples/01_core_dsl.rb
61
+ - examples/02_facet_system.rb
62
+ - examples/03_api_generation.rb
63
+ - examples/04_auto_mounting.rb
64
+ - examples/05_adapters.rb
65
+ - examples/README.md
66
+ - examples/server/README.md
67
+ - examples/server/adapters/payment_adapter.rb
68
+ - examples/server/application.rb
69
+ - examples/server/config.ru
70
+ - examples/server/config/facera.rb
71
+ - examples/server/cores/payment_core.rb
72
+ - examples/server/facets/external_facet.rb
73
+ - examples/server/facets/internal_facet.rb
74
+ - examples/server/facets/operator_facet.rb
75
+ - facera.gemspec
76
+ - img/facera.png
77
+ - lib/facera.rb
78
+ - lib/facera/adapter.rb
79
+ - lib/facera/attribute.rb
80
+ - lib/facera/auto_mount.rb
81
+ - lib/facera/capability.rb
82
+ - lib/facera/capability_access.rb
83
+ - lib/facera/configuration.rb
84
+ - lib/facera/context.rb
85
+ - lib/facera/core.rb
86
+ - lib/facera/dsl.rb
87
+ - lib/facera/entity.rb
88
+ - lib/facera/error_formatter.rb
89
+ - lib/facera/errors.rb
90
+ - lib/facera/executor.rb
91
+ - lib/facera/facet.rb
92
+ - lib/facera/field_visibility.rb
93
+ - lib/facera/generators/core_generator.rb
94
+ - lib/facera/generators/facet_generator.rb
95
+ - lib/facera/generators/install_generator.rb
96
+ - lib/facera/generators/templates/core.rb.tt
97
+ - lib/facera/generators/templates/facet.rb.tt
98
+ - lib/facera/grape.rb
99
+ - lib/facera/grape/api_generator.rb
100
+ - lib/facera/grape/endpoint_generator.rb
101
+ - lib/facera/grape/entity_generator.rb
102
+ - lib/facera/introspection.rb
103
+ - lib/facera/introspection_api.rb
104
+ - lib/facera/invariant.rb
105
+ - lib/facera/loader.rb
106
+ - lib/facera/openapi_generator.rb
107
+ - lib/facera/railtie.rb
108
+ - lib/facera/registry.rb
109
+ - lib/facera/tasks/routes.rake
110
+ - lib/facera/version.rb
111
+ homepage: https://github.com/jcagarcia/facera
112
+ licenses:
113
+ - MIT
114
+ metadata:
115
+ homepage_uri: https://github.com/jcagarcia/facera
116
+ source_code_uri: https://github.com/jcagarcia/facera
117
+ changelog_uri: https://github.com/jcagarcia/facera/blob/main/CHANGELOG.md
118
+ post_install_message:
119
+ rdoc_options: []
120
+ require_paths:
121
+ - lib
122
+ required_ruby_version: !ruby/object:Gem::Requirement
123
+ requirements:
124
+ - - ">="
125
+ - !ruby/object:Gem::Version
126
+ version: 3.2.0
127
+ required_rubygems_version: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ requirements: []
133
+ rubygems_version: 3.4.19
134
+ signing_key:
135
+ specification_version: 4
136
+ summary: A Ruby framework for building multi-facet APIs from a single semantic core
137
+ test_files: []