hati-rails-api 0.1.0.beta1

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 (32) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -0
  3. data/README.md +237 -0
  4. data/hati-rails-api.gemspec +39 -0
  5. data/lib/generators/hati_rails_api/context_generator.rb +270 -0
  6. data/lib/hati_rails_api/context/configuration/configuration.rb +50 -0
  7. data/lib/hati_rails_api/context/configuration/domain_configuration.rb +55 -0
  8. data/lib/hati_rails_api/context/core/error_handler.rb +76 -0
  9. data/lib/hati_rails_api/context/core/loader.rb +54 -0
  10. data/lib/hati_rails_api/context/core/migration.rb +68 -0
  11. data/lib/hati_rails_api/context/core/public_api.rb +114 -0
  12. data/lib/hati_rails_api/context/core.rb +18 -0
  13. data/lib/hati_rails_api/context/extensions/string_extensions.rb +11 -0
  14. data/lib/hati_rails_api/context/generators/base_generator.rb +33 -0
  15. data/lib/hati_rails_api/context/generators/domain_generator.rb +219 -0
  16. data/lib/hati_rails_api/context/generators/file_generation.rb +72 -0
  17. data/lib/hati_rails_api/context/generators/generator.rb +212 -0
  18. data/lib/hati_rails_api/context/generators/layer_component_generator.rb +88 -0
  19. data/lib/hati_rails_api/context/generators/model_endpoint_generator.rb +97 -0
  20. data/lib/hati_rails_api/context/generators/operation_generator.rb +182 -0
  21. data/lib/hati_rails_api/context/layers/operation_layer.rb +45 -0
  22. data/lib/hati_rails_api/context/layers/standard_layer.rb +44 -0
  23. data/lib/hati_rails_api/context/managers/rollback_manager.rb +123 -0
  24. data/lib/hati_rails_api/context/shared/content_generators.rb +125 -0
  25. data/lib/hati_rails_api/context/shared/layer_factory.rb +37 -0
  26. data/lib/hati_rails_api/context.rb +30 -0
  27. data/lib/hati_rails_api/errors/unsupported_operation_error.rb +11 -0
  28. data/lib/hati_rails_api/macro/serializer_macro.rb +13 -0
  29. data/lib/hati_rails_api/response_handler.rb +71 -0
  30. data/lib/hati_rails_api/version.rb +5 -0
  31. data/lib/hati_rails_api.rb +15 -0
  32. metadata +121 -0
@@ -0,0 +1,123 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'fileutils'
4
+ require 'yaml'
5
+
6
+ module HatiRailsApi
7
+ module Context
8
+ # Manages rollback functionality for generated files
9
+ # Follows Single Responsibility Principle - only handles rollback operations
10
+ class RollbackManager
11
+ TRACKING_FILE = 'config/contexts/.hati_generations.yml'
12
+
13
+ def initialize
14
+ ensure_tracking_directory
15
+ end
16
+
17
+ def track_generation(timestamp, files)
18
+ tracking_data = load_tracking_data
19
+ tracking_data[timestamp] = {
20
+ 'generated_at' => Time.now.to_s,
21
+ 'files' => files
22
+ }
23
+ save_tracking_data(tracking_data)
24
+ end
25
+
26
+ def rollback_by_timestamp?(timestamp)
27
+ tracking_data = load_tracking_data
28
+ generation = tracking_data[timestamp]
29
+
30
+ unless generation
31
+ puts "No generation found with timestamp: #{timestamp}"
32
+ puts "Available generations: #{tracking_data.keys.join(', ')}"
33
+ return false
34
+ end
35
+
36
+ rollback_files(generation['files'], timestamp)
37
+
38
+ # Remove from tracking
39
+ tracking_data.delete(timestamp)
40
+ save_tracking_data(tracking_data)
41
+
42
+ puts "Rollback completed for timestamp: #{timestamp}"
43
+ true
44
+ end
45
+
46
+ def rollback_last?
47
+ tracking_data = load_tracking_data
48
+
49
+ if tracking_data.empty?
50
+ puts 'No generations to rollback'
51
+ return false
52
+ end
53
+
54
+ last_timestamp = tracking_data.keys.sort.last
55
+ rollback_by_timestamp?(last_timestamp)
56
+ end
57
+
58
+ def list_generations
59
+ tracking_data = load_tracking_data
60
+
61
+ if tracking_data.empty?
62
+ puts 'No context generations found'
63
+ return
64
+ end
65
+
66
+ puts "\nContext Generations:"
67
+ puts '=' * 50
68
+
69
+ tracking_data.each do |timestamp, data|
70
+ puts "#{timestamp} (#{data['generated_at']})"
71
+ puts " Files: #{data['files'].size} files"
72
+ data['files'].each { |file| puts " - #{file}" }
73
+ puts ''
74
+ end
75
+ end
76
+
77
+ private
78
+
79
+ def load_tracking_data
80
+ return {} unless File.exist?(TRACKING_FILE)
81
+
82
+ YAML.load_file(TRACKING_FILE) || {}
83
+ rescue StandardError
84
+ {}
85
+ end
86
+
87
+ def save_tracking_data(data)
88
+ File.write(TRACKING_FILE, data.to_yaml)
89
+ end
90
+
91
+ def ensure_tracking_directory
92
+ FileUtils.mkdir_p(File.dirname(TRACKING_FILE))
93
+ end
94
+
95
+ def rollback_files(files, timestamp)
96
+ puts "Rolling back generation #{timestamp}..."
97
+
98
+ files.each do |file_path|
99
+ if File.exist?(file_path)
100
+ FileUtils.rm(file_path)
101
+ puts " Removed: #{file_path}"
102
+ else
103
+ puts " Already removed: #{file_path}"
104
+ end
105
+
106
+ # Clean up empty directories
107
+ remove_empty_directories(File.dirname(file_path))
108
+ end
109
+ end
110
+
111
+ def remove_empty_directories(dir)
112
+ return unless Dir.exist?(dir)
113
+ return if ['app', 'app/contexts'].include?(dir) # Don't remove base directories
114
+
115
+ return unless Dir.empty?(dir)
116
+
117
+ FileUtils.rmdir(dir)
118
+ puts " Removed empty directory: #{dir}"
119
+ remove_empty_directories(File.dirname(dir))
120
+ end
121
+ end
122
+ end
123
+ end
@@ -0,0 +1,125 @@
1
+ # frozen_string_literal: true
2
+
3
+ module HatiRailsApi
4
+ module Context
5
+ # Shared content generation methods
6
+ # Eliminates duplicate content generation across generators
7
+ module ContentGenerators
8
+ TIMESTAMP_PATTERN = '%Y%m%d%H%M%S'
9
+
10
+ private
11
+
12
+ def generate_class_content(class_name:, base_class:, timestamp: nil, &_block)
13
+ timestamp ||= Time.now.strftime(TIMESTAMP_PATTERN)
14
+
15
+ <<~RUBY
16
+ # frozen_string_literal: true
17
+ # Generated at #{timestamp}
18
+
19
+ class #{class_name} < #{base_class}
20
+ #{block_given? ? yield : '# TODO: Add implementation here'}
21
+ end
22
+ RUBY
23
+ end
24
+
25
+ def generate_model_content(name:, base_class:, timestamp: nil)
26
+ msg = "# TODO: Add #{name} model logic here"
27
+
28
+ generate_class_content(
29
+ class_name: name,
30
+ base_class: base_class,
31
+ timestamp: timestamp
32
+ ) do
33
+ msg
34
+ end
35
+ end
36
+
37
+ def generate_controller_content(name:, base_class:, actions_code: nil, timestamp: nil)
38
+ class_name = "#{name.to_s.camelize}Controller"
39
+ timestamp ||= Time.now.strftime(TIMESTAMP_PATTERN)
40
+
41
+ # Ensure actions are properly indented
42
+ indented_actions = if actions_code && actions_code.strip != '# TODO: Add controller logic here'
43
+ actions_code.split("\n").map { |line| " #{line}" }.join("\n")
44
+ else
45
+ ' # TODO: Add controller logic here'
46
+ end
47
+
48
+ <<~RUBY
49
+ # frozen_string_literal: true
50
+ # Generated at #{timestamp}
51
+
52
+ class #{class_name} < #{base_class}
53
+ include HatiRailsApi::ResponseHandler
54
+
55
+ #{indented_actions}
56
+ end
57
+ RUBY
58
+ end
59
+
60
+ def generate_operation_content(class_name:, base_class:, action:, step_content: nil, timestamp: nil)
61
+ timestamp ||= Time.now.strftime(TIMESTAMP_PATTERN)
62
+
63
+ <<~RUBY
64
+ # frozen_string_literal: true
65
+ # Generated at #{timestamp}
66
+ class #{class_name} < #{base_class}
67
+ # Operation configuration
68
+ #{generate_operation_macros(action)}
69
+
70
+ def call(params:)
71
+ #{step_content || generate_default_steps_for_action(action)}
72
+ end
73
+
74
+ private
75
+
76
+ #{generate_private_steps(action)}
77
+ end
78
+ RUBY
79
+ end
80
+
81
+ def generate_step_based_operation_content(
82
+ class_name:, base_class:, step_declarations:, step_calls:, timestamp: nil
83
+ )
84
+ timestamp ||= Time.now.strftime(TIMESTAMP_PATTERN)
85
+
86
+ <<~RUBY
87
+ # frozen_string_literal: true
88
+ # Generated at #{timestamp}
89
+ class #{class_name} < #{base_class}
90
+ #{step_declarations}
91
+
92
+ def call(params:)
93
+ #{step_calls}
94
+
95
+ Success(rez)
96
+ end
97
+ end
98
+ RUBY
99
+ end
100
+
101
+ def generate_operation_macros(_action)
102
+ '# Add operation configuration macros here'
103
+ end
104
+
105
+ def generate_default_steps_for_action(action)
106
+ <<~RUBY.strip
107
+ # TODO: Implement #{action} business logic
108
+ # Add your step-based implementation here
109
+
110
+ Success(params)
111
+ RUBY
112
+ end
113
+
114
+ def generate_private_steps(_action)
115
+ <<~RUBY.strip
116
+ # TODO: Add your private step methods here
117
+ # Example:
118
+ # def validate_params(params)
119
+ # Success(params)
120
+ # end
121
+ RUBY
122
+ end
123
+ end
124
+ end
125
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../layers/operation_layer'
4
+ require_relative '../layers/standard_layer'
5
+
6
+ module HatiRailsApi
7
+ module Context
8
+ # Shared layer factory methods
9
+ # Eliminates duplicate layer creation logic
10
+ module LayerFactory
11
+ protected
12
+
13
+ def create_operation_layer(&block)
14
+ layer = OperationLayer.new
15
+ layer.instance_eval(&block) if block_given?
16
+ layer
17
+ end
18
+
19
+ def create_standard_layer(layer_name, &block)
20
+ layer = StandardLayer.new(layer_name.to_sym)
21
+ if block_given?
22
+ layer.instance_eval(&block)
23
+ else
24
+ # Set default component to :domain for plain layer declarations
25
+ layer.component(:domain)
26
+ end
27
+ layer
28
+ end
29
+
30
+ def create_dynamic_layer(method_name, *args, &block)
31
+ # Always create a layer for method calls
32
+ # This handles cases like: domain.validation, domain.query, domain.service
33
+ create_standard_layer(method_name, &block)
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'context/core'
4
+
5
+ module HatiRailsApi
6
+ # Main Context module providing the public API for code generation
7
+ #
8
+ # This module serves as the entry point for all context-related operations
9
+ # including configuration, generation, and rollback functionality.
10
+ #
11
+ # @example Basic usage
12
+ # HatiRailsApi::Context.configure do |config|
13
+ # config.base_path 'app/contexts'
14
+ # end
15
+ #
16
+ # HatiRailsApi::Context.generate do |ctx|
17
+ # ctx.domain :user do |domain|
18
+ # domain.operation { |op| op.component [:create, :update] }
19
+ # domain.endpoint enabled: true
20
+ # end
21
+ # end
22
+ #
23
+ # @example Rollback
24
+ # HatiRailsApi::Context.rollback('20240101120000')
25
+ # HatiRailsApi::Context.rollback # rolls back last generation
26
+ #
27
+ module Context
28
+ extend Core::PublicAPI
29
+ end
30
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module HatiRailsApi
4
+ module Errors
5
+ class UnsupportedOperationError < StandardError
6
+ def initialize(message = 'Unsupported operation')
7
+ super
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module HatiRailsApi
4
+ module Macro
5
+ class SerializerMacro
6
+ include HatiCommand::Cmd
7
+
8
+ def call(serializer, status: 200)
9
+ Success(serializer.call(status: status))
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Load pry only in development
4
+ require 'pry' if defined?(Rails) && Rails.env.development?
5
+
6
+ # TODO:
7
+ ## success macros:
8
+ # - 200
9
+ # - 201
10
+ # - 204
11
+ # - 206
12
+ # - 207
13
+ ## on_failure & on_success result mappers
14
+
15
+ module HatiRailsApi
16
+ module ResponseHandler
17
+ # WIP: think about global configs
18
+ HatiJsonapiError::Config.configure do |config|
19
+ config.load_errors!
20
+ config.use_unexpected = HatiJsonapiError::InternalServerError
21
+ # WIP: map or preload from rails errors to statuses ???
22
+ # config.map_errors =
23
+ # ActiveRecord::RecordNotFound => :not_found,
24
+ # ActiveRecord::RecordInvalid => :unprocessable_entity
25
+ # }
26
+ end
27
+
28
+ def self.included(base)
29
+ base.include HatiJsonapiError::Helpers
30
+ end
31
+
32
+ private
33
+
34
+ # WIP:
35
+ def unsafe_params
36
+ params.respond_to?(:permit) ? params.permit.to_unsafe_h : params
37
+ end
38
+
39
+ # specific for api-errror lib !!!!
40
+ # WIP: debug access ??
41
+ def run_and_render(operation, &block)
42
+ result = run_operation(operation, &block)
43
+ result_value = result.value
44
+ status = result_value.try(:status)
45
+
46
+ if result.success?
47
+ render_success(result_value, status: status)
48
+ else
49
+ # WIP:
50
+ # render_error(result.error || result.value) ????
51
+ # setup error object with meta data ???
52
+ render_error(result.error)
53
+ end
54
+ end
55
+
56
+ def run_operation(operation, &block)
57
+ return operation if operation.is_a?(HatiCommand::Result)
58
+ raise Errors::UnsupportedOperationError, operation unless operation < HatiOperation::Base
59
+
60
+ if block_given?
61
+ operation.call(params: unsafe_params, &block)
62
+ else
63
+ operation.call(params: unsafe_params)
64
+ end
65
+ end
66
+
67
+ def render_success(result_value, status: 200)
68
+ render json: { data: result_value }, status: status
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module HatiRailsApi
4
+ VERSION = '0.1.0.beta1'
5
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ # external dependencies
4
+ require 'rails/generators'
5
+
6
+ # internal dependencies
7
+ require 'hati_rails_api/version'
8
+ require 'hati_rails_api/response_handler'
9
+ require 'hati_rails_api/context'
10
+
11
+ # WIP:
12
+ module HatiRailsApi
13
+ class Engine < Rails::Engine
14
+ end
15
+ end
metadata ADDED
@@ -0,0 +1,121 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: hati-rails-api
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0.beta1
5
+ platform: ruby
6
+ authors:
7
+ - Mariya Giy
8
+ - Yuri Gi
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 1980-01-02 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: hati-operation
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: hati-jsonapi-error
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: railties
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '6.0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '6.0'
55
+ description: A Rails API tool for building API services.
56
+ email:
57
+ - giy.mariya@gmail.com
58
+ - yurigi.pro@gmail.com
59
+ executables: []
60
+ extensions: []
61
+ extra_rdoc_files: []
62
+ files:
63
+ - LICENSE
64
+ - README.md
65
+ - hati-rails-api.gemspec
66
+ - lib/generators/hati_rails_api/context_generator.rb
67
+ - lib/hati_rails_api.rb
68
+ - lib/hati_rails_api/context.rb
69
+ - lib/hati_rails_api/context/configuration/configuration.rb
70
+ - lib/hati_rails_api/context/configuration/domain_configuration.rb
71
+ - lib/hati_rails_api/context/core.rb
72
+ - lib/hati_rails_api/context/core/error_handler.rb
73
+ - lib/hati_rails_api/context/core/loader.rb
74
+ - lib/hati_rails_api/context/core/migration.rb
75
+ - lib/hati_rails_api/context/core/public_api.rb
76
+ - lib/hati_rails_api/context/extensions/string_extensions.rb
77
+ - lib/hati_rails_api/context/generators/base_generator.rb
78
+ - lib/hati_rails_api/context/generators/domain_generator.rb
79
+ - lib/hati_rails_api/context/generators/file_generation.rb
80
+ - lib/hati_rails_api/context/generators/generator.rb
81
+ - lib/hati_rails_api/context/generators/layer_component_generator.rb
82
+ - lib/hati_rails_api/context/generators/model_endpoint_generator.rb
83
+ - lib/hati_rails_api/context/generators/operation_generator.rb
84
+ - lib/hati_rails_api/context/layers/operation_layer.rb
85
+ - lib/hati_rails_api/context/layers/standard_layer.rb
86
+ - lib/hati_rails_api/context/managers/rollback_manager.rb
87
+ - lib/hati_rails_api/context/shared/content_generators.rb
88
+ - lib/hati_rails_api/context/shared/layer_factory.rb
89
+ - lib/hati_rails_api/errors/unsupported_operation_error.rb
90
+ - lib/hati_rails_api/macro/serializer_macro.rb
91
+ - lib/hati_rails_api/response_handler.rb
92
+ - lib/hati_rails_api/version.rb
93
+ homepage: https://github.com/hackico-ai/hati-rails-api
94
+ licenses:
95
+ - MIT
96
+ metadata:
97
+ repo_homepage: https://github.com/hackico-ai/hati-rails-api
98
+ allowed_push_host: https://rubygems.org
99
+ homepage_uri: https://github.com/hackico-ai/hati-rails-api
100
+ changelog_uri: https://github.com/hackico-ai/hati-rails-api/blob/main/CHANGELOG.md
101
+ source_code_uri: https://github.com/hackico-ai/hati-rails-api
102
+ bug_tracker_uri: https://github.com/hackico-ai/hati-rails-api/issues
103
+ rubygems_mfa_required: 'true'
104
+ rdoc_options: []
105
+ require_paths:
106
+ - lib
107
+ required_ruby_version: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - ">="
110
+ - !ruby/object:Gem::Version
111
+ version: 3.0.0
112
+ required_rubygems_version: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - ">="
115
+ - !ruby/object:Gem::Version
116
+ version: '0'
117
+ requirements: []
118
+ rubygems_version: 3.6.9
119
+ specification_version: 4
120
+ summary: A Rails API tool for building API services.
121
+ test_files: []