railspress-engine 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 (56) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +67 -0
  4. data/Rakefile +10 -0
  5. data/app/assets/javascripts/railspress/admin.js +210 -0
  6. data/app/assets/stylesheets/actiontext.css +440 -0
  7. data/app/assets/stylesheets/railspress/admin.css +1199 -0
  8. data/app/assets/stylesheets/railspress/application.css +15 -0
  9. data/app/controllers/railspress/admin/base_controller.rb +38 -0
  10. data/app/controllers/railspress/admin/categories_controller.rb +53 -0
  11. data/app/controllers/railspress/admin/dashboard_controller.rb +12 -0
  12. data/app/controllers/railspress/admin/posts_controller.rb +71 -0
  13. data/app/controllers/railspress/admin/tags_controller.rb +50 -0
  14. data/app/controllers/railspress/application_controller.rb +4 -0
  15. data/app/helpers/railspress/application_helper.rb +4 -0
  16. data/app/jobs/railspress/application_job.rb +4 -0
  17. data/app/mailers/railspress/application_mailer.rb +6 -0
  18. data/app/models/railspress/application_record.rb +6 -0
  19. data/app/models/railspress/category.rb +18 -0
  20. data/app/models/railspress/post.rb +74 -0
  21. data/app/models/railspress/post_tag.rb +8 -0
  22. data/app/models/railspress/tag.rb +32 -0
  23. data/app/views/active_storage/blobs/_blob.html.erb +14 -0
  24. data/app/views/layouts/action_text/contents/_content.html.erb +3 -0
  25. data/app/views/layouts/railspress/admin.html.erb +41 -0
  26. data/app/views/layouts/railspress/application.html.erb +17 -0
  27. data/app/views/railspress/admin/categories/_form.html.erb +32 -0
  28. data/app/views/railspress/admin/categories/edit.html.erb +6 -0
  29. data/app/views/railspress/admin/categories/index.html.erb +42 -0
  30. data/app/views/railspress/admin/categories/new.html.erb +6 -0
  31. data/app/views/railspress/admin/dashboard/index.html.erb +45 -0
  32. data/app/views/railspress/admin/posts/_form.html.erb +129 -0
  33. data/app/views/railspress/admin/posts/edit.html.erb +2 -0
  34. data/app/views/railspress/admin/posts/index.html.erb +55 -0
  35. data/app/views/railspress/admin/posts/new.html.erb +2 -0
  36. data/app/views/railspress/admin/posts/show.html.erb +47 -0
  37. data/app/views/railspress/admin/shared/_flash.html.erb +5 -0
  38. data/app/views/railspress/admin/shared/_sidebar.html.erb +38 -0
  39. data/app/views/railspress/admin/tags/_form.html.erb +27 -0
  40. data/app/views/railspress/admin/tags/edit.html.erb +6 -0
  41. data/app/views/railspress/admin/tags/index.html.erb +42 -0
  42. data/app/views/railspress/admin/tags/new.html.erb +6 -0
  43. data/config/routes.rb +9 -0
  44. data/db/migrate/20241218000001_create_railspress_categories.rb +14 -0
  45. data/db/migrate/20241218000002_create_railspress_tags.rb +13 -0
  46. data/db/migrate/20241218000003_create_railspress_posts.rb +19 -0
  47. data/db/migrate/20241218000004_create_railspress_post_tags.rb +12 -0
  48. data/lib/generators/railspress/authors/authors_generator.rb +73 -0
  49. data/lib/generators/railspress/authors/templates/add_author_to_railspress_posts.rb.tt +8 -0
  50. data/lib/generators/railspress/authors/templates/railspress.rb.tt +31 -0
  51. data/lib/generators/railspress/install/install_generator.rb +144 -0
  52. data/lib/railspress/engine.rb +14 -0
  53. data/lib/railspress/version.rb +3 -0
  54. data/lib/railspress.rb +79 -0
  55. data/lib/tasks/railspress_tasks.rake +4 -0
  56. metadata +141 -0
@@ -0,0 +1,144 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/generators"
4
+ require "rails/generators/base"
5
+
6
+ module Railspress
7
+ module Generators
8
+ class InstallGenerator < Rails::Generators::Base
9
+ source_root File.expand_path("templates", __dir__)
10
+
11
+ desc "Install RailsPress: copy migrations, mount engine, and configure JavaScript"
12
+
13
+ def copy_railspress_migrations
14
+ rake "railspress:install:migrations"
15
+ end
16
+
17
+ def copy_action_text_migrations
18
+ if action_text_migration_exists?
19
+ say_status :skip, "ActionText migrations already exist", :yellow
20
+ else
21
+ say_status :create, "ActionText migrations", :green
22
+ rake "railties:install:migrations FROM=action_text"
23
+ end
24
+ end
25
+
26
+ def copy_active_storage_migrations
27
+ if active_storage_migration_exists?
28
+ say_status :skip, "ActiveStorage migrations already exist", :yellow
29
+ else
30
+ say_status :create, "ActiveStorage migrations", :green
31
+ rake "railties:install:migrations FROM=active_storage"
32
+ end
33
+ end
34
+
35
+ def mount_engine
36
+ route_content = 'mount Railspress::Engine => "/railspress"'
37
+
38
+ if File.read(rails_route_file).include?("Railspress::Engine")
39
+ say_status :skip, "RailsPress engine already mounted", :yellow
40
+ else
41
+ route route_content
42
+ say_status :mounted, "RailsPress engine at /railspress", :green
43
+ end
44
+ end
45
+
46
+ def configure_importmap
47
+ return unless importmap_available?
48
+
49
+ importmap_file = Rails.root.join("config", "importmap.rb")
50
+ importmap_content = File.read(importmap_file)
51
+
52
+ # Pin ActiveStorage (required for ActionText attachments)
53
+ unless importmap_content.include?("@rails/activestorage")
54
+ append_to_file importmap_file, <<~RUBY
55
+
56
+ # ActiveStorage for file uploads
57
+ pin "@rails/activestorage", to: "activestorage.esm.js"
58
+ RUBY
59
+ say_status :pinned, "@rails/activestorage in importmap", :green
60
+ end
61
+
62
+ # Pin Lexxy editor
63
+ if importmap_content.include?('"lexxy"')
64
+ say_status :skip, "Lexxy already pinned in importmap", :yellow
65
+ else
66
+ append_to_file importmap_file, <<~RUBY
67
+
68
+ # RailsPress rich text editor
69
+ pin "lexxy", to: "lexxy.js"
70
+ RUBY
71
+ say_status :pinned, "Lexxy in importmap", :green
72
+ end
73
+ end
74
+
75
+ def configure_javascript
76
+ return unless importmap_available?
77
+
78
+ application_js = Rails.root.join("app", "javascript", "application.js")
79
+ return unless File.exist?(application_js)
80
+
81
+ js_content = File.read(application_js)
82
+
83
+ if js_content.include?('import "lexxy"')
84
+ say_status :skip, "Lexxy already imported in application.js", :yellow
85
+ else
86
+ append_to_file application_js, <<~JS
87
+
88
+ // RailsPress rich text editor
89
+ import "lexxy"
90
+ JS
91
+ say_status :added, "Lexxy import to application.js", :green
92
+ end
93
+ end
94
+
95
+ def show_post_install_message
96
+ say ""
97
+ say "=" * 60, :green
98
+ say " RailsPress installed successfully!", :green
99
+ say "=" * 60, :green
100
+ say ""
101
+ say "Next steps:", :yellow
102
+ say ""
103
+ say " 1. Run migrations:"
104
+ say " $ rails db:migrate", :cyan
105
+ say ""
106
+ say " 2. Access the admin dashboard:"
107
+ say " http://localhost:3000/railspress/admin", :cyan
108
+ say ""
109
+ say " 3. (Optional) Change the mount path in config/routes.rb:"
110
+ say " mount Railspress::Engine => \"/blog\"", :cyan
111
+ say ""
112
+ say "JavaScript setup:", :yellow
113
+ say " The installer added Lexxy (rich text editor) to your importmap"
114
+ say " and application.js. If you use a custom JS entry point, add:"
115
+ say ""
116
+ say " import \"lexxy\"", :cyan
117
+ say ""
118
+ say "=" * 60, :green
119
+ end
120
+
121
+ private
122
+
123
+ def rails_route_file
124
+ Rails.root.join("config", "routes.rb")
125
+ end
126
+
127
+ def migrations_dir
128
+ Rails.root.join("db", "migrate")
129
+ end
130
+
131
+ def action_text_migration_exists?
132
+ Dir.glob(migrations_dir.join("*_create_action_text_tables*.rb")).any?
133
+ end
134
+
135
+ def active_storage_migration_exists?
136
+ Dir.glob(migrations_dir.join("*_create_active_storage_tables*.rb")).any?
137
+ end
138
+
139
+ def importmap_available?
140
+ defined?(Importmap) && Rails.root.join("config", "importmap.rb").exist?
141
+ end
142
+ end
143
+ end
144
+ end
@@ -0,0 +1,14 @@
1
+ module Railspress
2
+ class Engine < ::Rails::Engine
3
+ isolate_namespace Railspress
4
+
5
+ # Register engine assets with the asset pipeline (Propshaft)
6
+ initializer "railspress.assets" do |app|
7
+ if app.config.respond_to?(:assets)
8
+ app.config.assets.paths << root.join("app", "assets", "stylesheets").to_s
9
+ app.config.assets.paths << root.join("app", "assets", "javascripts").to_s
10
+ app.config.assets.paths << root.join("app", "assets", "images").to_s
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,3 @@
1
+ module Railspress
2
+ VERSION = "0.1.0"
3
+ end
data/lib/railspress.rb ADDED
@@ -0,0 +1,79 @@
1
+ require "railspress/version"
2
+ require "railspress/engine"
3
+ require "lexxy"
4
+
5
+ module Railspress
6
+ class Configuration
7
+ attr_accessor :author_class_name,
8
+ :current_author_method,
9
+ :author_scope,
10
+ :author_display_method
11
+
12
+ attr_reader :authors_enabled, :header_images_enabled
13
+
14
+ def initialize
15
+ @authors_enabled = false
16
+ @header_images_enabled = false
17
+ @author_class_name = "User"
18
+ @current_author_method = :current_user
19
+ @author_scope = nil
20
+ @author_display_method = :name
21
+ end
22
+
23
+ # Declarative setter: config.enable_authors
24
+ def enable_authors
25
+ @authors_enabled = true
26
+ end
27
+
28
+ # Declarative setter: config.enable_header_images
29
+ def enable_header_images
30
+ @header_images_enabled = true
31
+ end
32
+ end
33
+
34
+ class << self
35
+ def configuration
36
+ @configuration ||= Configuration.new
37
+ end
38
+
39
+ def configure
40
+ yield(configuration)
41
+ end
42
+
43
+ def reset_configuration!
44
+ @configuration = Configuration.new
45
+ end
46
+
47
+ # Convenience accessors
48
+ def authors_enabled?
49
+ configuration.authors_enabled
50
+ end
51
+
52
+ def header_images_enabled?
53
+ configuration.header_images_enabled
54
+ end
55
+
56
+ def author_class
57
+ configuration.author_class_name.constantize
58
+ end
59
+
60
+ def available_authors
61
+ scope = configuration.author_scope
62
+ klass = author_class
63
+
64
+ case scope
65
+ when Symbol then klass.public_send(scope)
66
+ when Proc then scope.call(klass)
67
+ else klass.all
68
+ end
69
+ end
70
+
71
+ def author_display_method
72
+ configuration.author_display_method
73
+ end
74
+
75
+ def current_author_method
76
+ configuration.current_author_method
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :railspress do
3
+ # # Task goes here
4
+ # end
metadata ADDED
@@ -0,0 +1,141 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: railspress-engine
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Avi Flombaum
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: rails
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '8.0'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: '8.0'
26
+ - !ruby/object:Gem::Dependency
27
+ name: lexxy
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: 0.1.23.beta
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: 0.1.23.beta
40
+ description: RailsPress provides drop-in blog functionality with categories, tags,
41
+ rich text editing, and an admin interface.
42
+ email:
43
+ - avi@flatironschool.com
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - MIT-LICENSE
49
+ - README.md
50
+ - Rakefile
51
+ - app/assets/javascripts/railspress/admin.js
52
+ - app/assets/stylesheets/actiontext.css
53
+ - app/assets/stylesheets/railspress/admin.css
54
+ - app/assets/stylesheets/railspress/application.css
55
+ - app/controllers/railspress/admin/base_controller.rb
56
+ - app/controllers/railspress/admin/categories_controller.rb
57
+ - app/controllers/railspress/admin/dashboard_controller.rb
58
+ - app/controllers/railspress/admin/posts_controller.rb
59
+ - app/controllers/railspress/admin/tags_controller.rb
60
+ - app/controllers/railspress/application_controller.rb
61
+ - app/helpers/railspress/application_helper.rb
62
+ - app/jobs/railspress/application_job.rb
63
+ - app/mailers/railspress/application_mailer.rb
64
+ - app/models/railspress/application_record.rb
65
+ - app/models/railspress/category.rb
66
+ - app/models/railspress/post.rb
67
+ - app/models/railspress/post_tag.rb
68
+ - app/models/railspress/tag.rb
69
+ - app/views/active_storage/blobs/_blob.html.erb
70
+ - app/views/layouts/action_text/contents/_content.html.erb
71
+ - app/views/layouts/railspress/admin.html.erb
72
+ - app/views/layouts/railspress/application.html.erb
73
+ - app/views/railspress/admin/categories/_form.html.erb
74
+ - app/views/railspress/admin/categories/edit.html.erb
75
+ - app/views/railspress/admin/categories/index.html.erb
76
+ - app/views/railspress/admin/categories/new.html.erb
77
+ - app/views/railspress/admin/dashboard/index.html.erb
78
+ - app/views/railspress/admin/posts/_form.html.erb
79
+ - app/views/railspress/admin/posts/edit.html.erb
80
+ - app/views/railspress/admin/posts/index.html.erb
81
+ - app/views/railspress/admin/posts/new.html.erb
82
+ - app/views/railspress/admin/posts/show.html.erb
83
+ - app/views/railspress/admin/shared/_flash.html.erb
84
+ - app/views/railspress/admin/shared/_sidebar.html.erb
85
+ - app/views/railspress/admin/tags/_form.html.erb
86
+ - app/views/railspress/admin/tags/edit.html.erb
87
+ - app/views/railspress/admin/tags/index.html.erb
88
+ - app/views/railspress/admin/tags/new.html.erb
89
+ - config/routes.rb
90
+ - db/migrate/20241218000001_create_railspress_categories.rb
91
+ - db/migrate/20241218000002_create_railspress_tags.rb
92
+ - db/migrate/20241218000003_create_railspress_posts.rb
93
+ - db/migrate/20241218000004_create_railspress_post_tags.rb
94
+ - lib/generators/railspress/authors/authors_generator.rb
95
+ - lib/generators/railspress/authors/templates/add_author_to_railspress_posts.rb.tt
96
+ - lib/generators/railspress/authors/templates/railspress.rb.tt
97
+ - lib/generators/railspress/install/install_generator.rb
98
+ - lib/railspress.rb
99
+ - lib/railspress/engine.rb
100
+ - lib/railspress/version.rb
101
+ - lib/tasks/railspress_tasks.rake
102
+ homepage: https://github.com/aviflombaum/railspress-engine
103
+ licenses:
104
+ - MIT
105
+ metadata:
106
+ homepage_uri: https://github.com/aviflombaum/railspress-engine
107
+ source_code_uri: https://github.com/aviflombaum/railspress-engine
108
+ changelog_uri: https://github.com/aviflombaum/railspress-engine/blob/main/CHANGELOG.md
109
+ rubygems_mfa_required: 'true'
110
+ post_install_message: |2+
111
+
112
+ ============================================================
113
+ RailsPress installed! Run the generator to complete setup:
114
+
115
+ $ rails generate railspress:install
116
+
117
+ This will:
118
+ - Copy database migrations
119
+ - Mount the engine in your routes
120
+ - Install ActionText (if needed)
121
+ ============================================================
122
+
123
+ rdoc_options: []
124
+ require_paths:
125
+ - lib
126
+ required_ruby_version: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - ">="
129
+ - !ruby/object:Gem::Version
130
+ version: 3.3.0
131
+ required_rubygems_version: !ruby/object:Gem::Requirement
132
+ requirements:
133
+ - - ">="
134
+ - !ruby/object:Gem::Version
135
+ version: '0'
136
+ requirements: []
137
+ rubygems_version: 3.6.9
138
+ specification_version: 4
139
+ summary: A mountable blog engine for Rails 8+
140
+ test_files: []
141
+ ...