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,196 @@
1
+ #!/usr/bin/env ruby
2
+ require_relative '../lib/facera'
3
+
4
+ puts "=" * 80
5
+ puts "Example 05: Adapters & Business Logic Implementation"
6
+ puts "=" * 80
7
+
8
+ # Example 1: Inline execution blocks (for simple logic)
9
+ puts "\nšŸ“ Example 1: Inline execution blocks"
10
+ puts "-" * 80
11
+
12
+ Facera.define_core(:simple_counter) do
13
+ entity :counter do
14
+ attribute :id, :uuid
15
+ attribute :count, :integer
16
+ end
17
+
18
+ capability :increment_counter, type: :action do
19
+ entity :counter
20
+ requires :id
21
+
22
+ # Simple inline implementation
23
+ execute do |params|
24
+ {
25
+ id: params[:id],
26
+ count: 42, # In reality, fetch and increment
27
+ incremented_at: Time.now
28
+ }
29
+ end
30
+ end
31
+ end
32
+
33
+ counter_core = Facera::Registry.cores[:simple_counter]
34
+ puts "āœ“ Core defined: #{counter_core.name}"
35
+ puts "āœ“ Capability with execute block: #{counter_core.capabilities.keys.first}"
36
+
37
+ # Example 2: Adapter pattern (for complex logic)
38
+ puts "\nšŸ”Œ Example 2: Adapter pattern"
39
+ puts "-" * 80
40
+
41
+ # Define the core
42
+ Facera.define_core(:blog) do
43
+ entity :post do
44
+ attribute :id, :uuid
45
+ attribute :title, :string, required: true
46
+ attribute :content, :string
47
+ attribute :status, :enum, values: [:draft, :published]
48
+ attribute :published_at, :datetime
49
+ end
50
+
51
+ capability :create_post, type: :create do
52
+ entity :post
53
+ requires :title, :content
54
+ end
55
+
56
+ capability :publish_post, type: :action do
57
+ entity :post
58
+ requires :id
59
+ precondition { status == :draft }
60
+ transitions_to :published
61
+ sets published_at: -> { Time.now }
62
+ end
63
+ end
64
+
65
+ # Implement the adapter
66
+ class BlogAdapter
67
+ include Facera::Adapter
68
+
69
+ @@posts = {}
70
+
71
+ def create_post(params)
72
+ post = {
73
+ id: SecureRandom.uuid,
74
+ title: params[:title],
75
+ content: params[:content],
76
+ status: :draft,
77
+ created_at: Time.now
78
+ }
79
+
80
+ @@posts[post[:id]] = post
81
+
82
+ puts " šŸ’¾ Created post: #{post[:title]}"
83
+ post
84
+ end
85
+
86
+ def get_post(params)
87
+ @@posts[params[:id]]
88
+ end
89
+
90
+ def publish_post(params)
91
+ post = get_post(params)
92
+
93
+ # Do the actual publishing
94
+ post[:status] = :published
95
+ post[:published_at] = Time.now
96
+
97
+ # In production, you might:
98
+ # - Clear cache
99
+ # - Update search index
100
+ # - Send notifications
101
+ # - Trigger webhooks
102
+
103
+ puts " šŸ“¢ Published post: #{post[:title]}"
104
+ post
105
+ end
106
+ end
107
+
108
+ # Register the adapter
109
+ Facera::AdapterRegistry.register(:blog, BlogAdapter)
110
+
111
+ blog_core = Facera::Registry.cores[:blog]
112
+ puts "āœ“ Core defined: #{blog_core.name}"
113
+ puts "āœ“ Adapter registered: BlogAdapter"
114
+
115
+ # Example 3: Using both approaches
116
+ puts "\nšŸŽ­ Example 3: Mixed approach (adapter + inline blocks)"
117
+ puts "-" * 80
118
+
119
+ Facera.define_core(:product) do
120
+ entity :product do
121
+ attribute :id, :uuid
122
+ attribute :name, :string
123
+ attribute :price, :money
124
+ attribute :stock, :integer
125
+ end
126
+
127
+ # Complex operation - use adapter
128
+ capability :create_product, type: :create do
129
+ entity :product
130
+ requires :name, :price
131
+ end
132
+
133
+ # Simple operation - use inline block
134
+ capability :check_stock, type: :action do
135
+ entity :product
136
+ requires :id
137
+
138
+ execute do |params|
139
+ # Simple stock check
140
+ {
141
+ id: params[:id],
142
+ stock: 10,
143
+ available: true
144
+ }
145
+ end
146
+ end
147
+ end
148
+
149
+ class ProductAdapter
150
+ include Facera::Adapter
151
+
152
+ def create_product(params)
153
+ {
154
+ id: SecureRandom.uuid,
155
+ name: params[:name],
156
+ price: params[:price],
157
+ stock: 0,
158
+ created_at: Time.now
159
+ }
160
+ end
161
+ end
162
+
163
+ Facera::AdapterRegistry.register(:product, ProductAdapter)
164
+
165
+ product_core = Facera::Registry.cores[:product]
166
+ puts "āœ“ Core defined: #{product_core.name}"
167
+ puts " - create_product: uses ProductAdapter"
168
+ puts " - check_stock: uses inline execute block"
169
+
170
+ # Summary
171
+ puts "\n" + "=" * 80
172
+ puts "✨ Summary"
173
+ puts "=" * 80
174
+ puts "
175
+ Facera supports two implementation patterns:
176
+
177
+ 1. šŸ“ Inline Blocks - For simple, one-off logic
178
+ capability :action do
179
+ execute do |params|
180
+ # Simple logic here
181
+ end
182
+ end
183
+
184
+ 2. šŸ”Œ Adapter Pattern - For complex, testable business logic
185
+ class MyAdapter
186
+ include Facera::Adapter
187
+
188
+ def capability_name(params)
189
+ # Complex logic here
190
+ end
191
+ end
192
+
193
+ Choose the right tool for the job!
194
+ "
195
+
196
+ puts "=" * 80
@@ -0,0 +1,184 @@
1
+ # Facera Examples
2
+
3
+ This directory contains examples demonstrating Facera's capabilities, organized by implementation phase.
4
+
5
+ ## Overview
6
+
7
+ Each example builds on the previous one, showing the progressive development of a multi-facet payment API:
8
+
9
+ ```
10
+ 01_core_dsl.rb → Phase 1: Core semantic definition
11
+ 02_facet_system.rb → Phase 2: Multiple facets from one core
12
+ 03_api_generation.rb → Phase 3: Auto-generated REST APIs
13
+ 04_auto_mounting.rb → Phase 4: Auto-mounting system
14
+ server/ → Runnable HTTP server
15
+ ```
16
+
17
+ ## Running the Examples
18
+
19
+ ### Phase 1: Core DSL
20
+
21
+ Demonstrates the basic building blocks: entities, capabilities, and invariants.
22
+
23
+ ```bash
24
+ ruby 01_core_dsl.rb
25
+ ```
26
+
27
+ **Shows:**
28
+ - Entity definition with typed attributes
29
+ - Capability definitions (create, get, list, actions)
30
+ - Business invariants
31
+ - Parameter requirements and validations
32
+
33
+ ### Phase 2: Facet System
34
+
35
+ Shows how multiple facets can be created from a single core, each with different visibility and access rules.
36
+
37
+ ```bash
38
+ ruby 02_facet_system.rb
39
+ ```
40
+
41
+ **Shows:**
42
+ - 4 different facets (external, internal, operator, agent)
43
+ - Field visibility control per facet
44
+ - Capability access control
45
+ - Computed fields
46
+ - Feature comparison matrix
47
+
48
+ ### Phase 3: API Generation
49
+
50
+ Demonstrates automatic REST API generation using Grape.
51
+
52
+ ```bash
53
+ ruby 03_api_generation.rb
54
+ ```
55
+
56
+ **Shows:**
57
+ - Auto-generated REST endpoints
58
+ - Field serialization based on facet rules
59
+ - Capability-to-endpoint mapping
60
+ - Route comparison across facets
61
+
62
+ ### Phase 4: Auto-Mounting
63
+
64
+ Shows how Facera automatically discovers and mounts all facets with zero configuration.
65
+
66
+ ```bash
67
+ ruby 04_auto_mounting.rb
68
+ ```
69
+
70
+ **Shows:**
71
+ - Zero-config auto-mounting
72
+ - Custom path configuration
73
+ - Facet enabling/disabling
74
+ - Rails integration (Railtie)
75
+ - Rack/Sinatra integration
76
+ - Per-facet authentication
77
+ - Configuration DSL
78
+
79
+ ### Running the Server
80
+
81
+ Launch a live HTTP server with multiple facet APIs:
82
+
83
+ ```bash
84
+ cd server
85
+ rackup -p 9292
86
+ ```
87
+
88
+ Then test the APIs:
89
+
90
+ ```bash
91
+ # Check health
92
+ curl http://localhost:9292/api/v1/health
93
+
94
+ # List payments (external API)
95
+ curl http://localhost:9292/api/v1/payments
96
+
97
+ # Create a payment
98
+ curl -X POST http://localhost:9292/api/v1/payments \
99
+ -H 'Content-Type: application/json' \
100
+ -d '{
101
+ "amount": 100.0,
102
+ "currency": "USD",
103
+ "merchant_id": "550e8400-e29b-41d4-a716-446655440000",
104
+ "customer_id": "6ba7b810-9dad-11d1-80b4-00c04fd430c8"
105
+ }'
106
+
107
+ # Confirm payment (internal API only - not available in external)
108
+ curl -X POST http://localhost:9292/api/internal/v1/payments/{id}/confirm
109
+ ```
110
+
111
+ ## Server Structure
112
+
113
+ ```
114
+ server/
115
+ ā”œā”€ā”€ config.ru # Rack server (just 38 lines!)
116
+ ā”œā”€ā”€ config/
117
+ │ └── facera.rb # Facera configuration
118
+ ā”œā”€ā”€ cores/
119
+ │ └── payment_core.rb # Domain model
120
+ └── facets/
121
+ ā”œā”€ā”€ external_facet.rb
122
+ ā”œā”€ā”€ internal_facet.rb
123
+ └── operator_facet.rb
124
+ ```
125
+
126
+ **Convention over configuration:**
127
+ - `config.ru` - Just loads config and calls `Facera.auto_mount!`
128
+ - `config/facera.rb` - All configuration in one place
129
+ - `cores/` - Drop files here, auto-discovered!
130
+ - `facets/` - Drop files here, auto-discovered!
131
+ - **No manual requires needed** - Facera finds everything automatically
132
+ - Zero boilerplate
133
+
134
+ ## Key Concepts Demonstrated
135
+
136
+ ### 1. Single Source of Truth
137
+ Define your domain model once in the core:
138
+ ```ruby
139
+ Facera.define_core(:payment) do
140
+ entity :payment do
141
+ attribute :amount, :money, required: true
142
+ end
143
+ end
144
+ ```
145
+
146
+ ### 2. Multiple Projections
147
+ Create different views for different consumers:
148
+ ```ruby
149
+ Facera.define_facet(:external, core: :payment) do
150
+ expose :payment do
151
+ fields :id, :amount # Limited fields
152
+ end
153
+ allow_capabilities :create_payment # Limited access
154
+ end
155
+
156
+ Facera.define_facet(:internal, core: :payment) do
157
+ expose :payment do
158
+ fields :all # Full visibility
159
+ end
160
+ allow_capabilities :all # Full access
161
+ end
162
+ ```
163
+
164
+ ### 3. Zero Boilerplate APIs
165
+ Generate complete REST APIs automatically:
166
+ ```ruby
167
+ api = Facera.api_for(:external)
168
+ # Auto-generates: POST /payments, GET /payments/:id, etc.
169
+ ```
170
+
171
+ ## Next Steps
172
+
173
+ After running these examples:
174
+ 1. Explore the generated routes in `03_api_generation.rb`
175
+ 2. Test the live server in `server/`
176
+ 3. Modify `server/payment_api.rb` to add your own facets
177
+ 4. Check the test suite in `spec/` for more examples
178
+
179
+ ## Tips
180
+
181
+ - **Start simple**: Run examples in order (01 → 02 → 03 → server)
182
+ - **Experiment**: Modify `server/payment_api.rb` and restart the server
183
+ - **Compare facets**: Notice how external vs. internal APIs differ
184
+ - **Check visibility**: See how field exposure changes per facet