rails-architect 0.1.0 → 0.2.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.
@@ -0,0 +1,87 @@
1
+ class ReportGenerator
2
+ # KISS Violation: Overly complex method with nested conditionals
3
+ # Method complexity exceeds threshold due to multiple control structures
4
+ def generate_complex_report
5
+ users = User.all
6
+ report_data = []
7
+
8
+ users.each do |user|
9
+ user_data = {}
10
+
11
+ # Deeply nested conditionals (KISS violation)
12
+ if user.active?
13
+ if user.admin?
14
+ user_data[:role] = 'Administrator'
15
+ user_data[:permissions] = ['read', 'write', 'delete', 'manage_users']
16
+ elsif user.moderator?
17
+ user_data[:role] = 'Moderator'
18
+ user_data[:permissions] = ['read', 'write', 'delete']
19
+ else
20
+ user_data[:role] = 'Member'
21
+ user_data[:permissions] = ['read', 'write']
22
+ end
23
+ else
24
+ user_data[:status] = 'Inactive'
25
+ user_data[:permissions] = []
26
+ end
27
+
28
+ # Complex case statement
29
+ case user.organization.size
30
+ when 1..10
31
+ user_data[:org_size] = 'Small'
32
+ when 11..50
33
+ user_data[:org_size] = 'Medium'
34
+ when 51..200
35
+ user_data[:org_size] = 'Large'
36
+ else
37
+ user_data[:org_size] = 'Enterprise'
38
+ end
39
+
40
+ # Multiple loop types in one method (increases complexity)
41
+ while user.posts.any?
42
+ post = user.posts.first
43
+ if post.published?
44
+ user_data[:published_posts] ||= 0
45
+ user_data[:published_posts] += 1
46
+ end
47
+ break
48
+ end
49
+
50
+ until user.comments.empty?
51
+ comment = user.comments.first
52
+ if comment.approved?
53
+ user_data[:approved_comments] ||= 0
54
+ user_data[:approved_comments] += 1
55
+ end
56
+ break
57
+ end
58
+
59
+ for i in 0..user.posts.count
60
+ # Some complex logic here
61
+ break if i > 10
62
+ end
63
+
64
+ report_data << user_data
65
+ end
66
+
67
+ report_data
68
+ end
69
+
70
+ # DRY Violation: Repeated calculation logic
71
+ def calculate_user_score_v1(user)
72
+ score = 0
73
+ score += user.posts.count * 10
74
+ score += user.comments.count * 5
75
+ score += user.likes.count * 2
76
+ score
77
+ end
78
+
79
+ def calculate_user_score_v2(user)
80
+ score = 0
81
+ score += user.posts.count * 10
82
+ score += user.comments.count * 5
83
+ score += user.likes.count * 2
84
+ score
85
+ end
86
+ end
87
+ ```
@@ -0,0 +1,79 @@
1
+ class User < ApplicationRecord
2
+ # Basic validations
3
+ validates :email, presence: true, uniqueness: true
4
+ validates :name, presence: true
5
+
6
+ # Associations
7
+ has_many :posts
8
+ has_many :comments
9
+ belongs_to :organization
10
+
11
+ # Scopes
12
+ scope :active, -> { where(active: true) }
13
+ scope :inactive, -> { where(active: false) }
14
+
15
+ # SOLID Violation: Too many methods (Fat Model - violates Single Responsibility)
16
+ # This model has 15+ instance methods doing different things
17
+
18
+ def full_name
19
+ "#{first_name} #{last_name}"
20
+ end
21
+
22
+ def activate
23
+ update(active: true)
24
+ end
25
+
26
+ def deactivate
27
+ update(active: false)
28
+ end
29
+
30
+ def send_welcome_email
31
+ UserMailer.welcome(self).deliver_later
32
+ end
33
+
34
+ def generate_api_token
35
+ update(api_token: SecureRandom.hex(20))
36
+ end
37
+
38
+ def posts_count
39
+ posts.count
40
+ end
41
+
42
+ def comments_count
43
+ comments.count
44
+ end
45
+
46
+ def recent_posts(limit = 5)
47
+ posts.order(created_at: :desc).limit(limit)
48
+ end
49
+
50
+ def admin?
51
+ role == 'admin'
52
+ end
53
+
54
+ def moderator?
55
+ role == 'moderator'
56
+ end
57
+
58
+ def member?
59
+ role == 'member'
60
+ end
61
+
62
+ def promote_to_admin
63
+ update(role: 'admin')
64
+ end
65
+
66
+ def demote_to_member
67
+ update(role: 'member')
68
+ end
69
+
70
+ # Additional methods to exceed threshold
71
+ def calculate_reputation
72
+ posts_count * 5 + comments_count * 2
73
+ end
74
+
75
+ def send_notification(message)
76
+ # Notification logic
77
+ end
78
+ end
79
+ ```
@@ -0,0 +1,14 @@
1
+ defaults: &defaults
2
+ model_method_threshold: 10
3
+ controller_action_threshold: 5
4
+ method_complexity_threshold: 5
5
+ test_coverage_minimum: 80
6
+
7
+ development:
8
+ <<: *defaults
9
+
10
+ test:
11
+ <<: *defaults
12
+
13
+ production:
14
+ <<: *defaults
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'test_helper'
4
+
5
+ class RailsAppIntegrationTest < Minitest::Test
6
+ def setup
7
+ @original_dir = Dir.pwd
8
+ @rails_app_path = File.join(__dir__, 'rails_app')
9
+ end
10
+
11
+ def teardown
12
+ Dir.chdir(@original_dir)
13
+ end
14
+
15
+ def test_analyzes_dummy_rails_app
16
+ Dir.chdir(@rails_app_path)
17
+
18
+ issues = RailsArchitect.analyze
19
+
20
+ # Should find several issues
21
+ assert issues.size > 0, "Expected to find architectural issues in dummy Rails app"
22
+
23
+ # Check for SOLID principle violations
24
+ solid_srp_issues = issues.select { |i| i[:type] == 'solid_srp' }
25
+ assert solid_srp_issues.size > 0, "Should detect SOLID SRP violations (fat models/controllers)"
26
+
27
+ # Check for KISS violations
28
+ kiss_complexity_issues = issues.select { |i| i[:type] == 'kiss_complexity' }
29
+ assert kiss_complexity_issues.size > 0, "Should detect KISS complexity violations"
30
+
31
+ # Check for DRY violations
32
+ dry_issues = issues.select { |i| i[:type].start_with?('dry_') }
33
+ assert dry_issues.size > 0, "Should detect DRY violations"
34
+
35
+ # Verify issue details for SOLID SRP
36
+ solid_srp_issues.each do |issue|
37
+ assert issue[:message].include?("Single Responsibility") || issue[:message].include?("extract to service objects"), "Expected SRP-related message, got: #{issue[:message]}"
38
+ end
39
+
40
+ # Verify issue details for KISS
41
+ kiss_complexity_issues.each do |issue|
42
+ assert_includes issue[:message], "complexity"
43
+ end
44
+
45
+ # Verify issue details for DRY
46
+ dry_issues.each do |issue|
47
+ assert_includes issue[:message], "consider extracting"
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'test_helper'
4
+
5
+ class RailsArchitectTest < Minitest::Test
6
+ def test_analyze_returns_array
7
+ # Mock the analyzers to avoid file system dependencies
8
+ mock_checker = Minitest::Mock.new
9
+ mock_checker.expect :check_models, nil
10
+ mock_checker.expect :check_controllers, nil
11
+ mock_checker.expect :issues, []
12
+
13
+ mock_complexity = Minitest::Mock.new
14
+ mock_complexity.expect :analyze, nil
15
+ mock_complexity.expect :issues, []
16
+
17
+ mock_perf_checker = Minitest::Mock.new
18
+ mock_perf_checker.expect :analyze, nil
19
+ mock_perf_checker.expect :issues, []
20
+ end
21
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'minitest/autorun'
4
+ require 'minitest/pride'
5
+ require 'rails_architect'
6
+
7
+ class Minitest::Test
8
+ def setup
9
+ # Setup code if needed
10
+ end
11
+
12
+ def teardown
13
+ # Cleanup code if needed
14
+ end
15
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rails-architect
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sami Dghim
@@ -121,9 +121,7 @@ dependencies:
121
121
  - - "~>"
122
122
  - !ruby/object:Gem::Version
123
123
  version: '5.0'
124
- description: Rails Architect helps maintain clean Rails architecture by automatically
125
- detecting and preventing common anti-patterns, monitoring code complexity, and providing
126
- architectural visualizations.
124
+ description: Rails Architect helps maintain clean Rails architecture
127
125
  email:
128
126
  - samidghim@gmail.com
129
127
  executables:
@@ -134,9 +132,24 @@ files:
134
132
  - README.md
135
133
  - Rakefile
136
134
  - bin/rails-architect
137
- - lib/rails/architect.rb
138
- - lib/rails/architect/version.rb
139
- - sig/rails/architect.rbs
135
+ - config/rails_architect.yml
136
+ - lib/rails_architect.rb
137
+ - lib/rails_architect/dry_analyzer.rb
138
+ - lib/rails_architect/kiss_analyzer.rb
139
+ - lib/rails_architect/railtie.rb
140
+ - lib/rails_architect/solid_analyzer.rb
141
+ - lib/rails_architect/tasks/rails_architect.rake
142
+ - lib/rails_architect/version.rb
143
+ - test/rails_app/README.md
144
+ - test/rails_app/app/controllers/posts_controller.rb
145
+ - test/rails_app/app/controllers/users_controller.rb
146
+ - test/rails_app/app/models/post.rb
147
+ - test/rails_app/app/models/report_generator.rb
148
+ - test/rails_app/app/models/user.rb
149
+ - test/rails_app/config/rails_architect.yml
150
+ - test/rails_app_integration_test.rb
151
+ - test/rails_architect_test.rb
152
+ - test/test_helper.rb
140
153
  homepage: https://github.com/samydghim/rails-architect
141
154
  licenses: []
142
155
  metadata:
@@ -1,7 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Rails
4
- module Architect
5
- VERSION = "0.1.0"
6
- end
7
- end
@@ -1,10 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "architect/version"
4
-
5
- module Rails
6
- module Architect
7
- class Error < StandardError; end
8
- # Your code goes here...
9
- end
10
- end
@@ -1,6 +0,0 @@
1
- module Rails
2
- module Architect
3
- VERSION: String
4
- # See the writing guide of rbs: https://github.com/ruby/rbs#guides
5
- end
6
- end