kealy_cheese 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +56 -0
- data/app/controllers/cheese/widgets_controller.rb +15 -0
- data/app/helpers/application_helper.rb +2 -0
- data/app/helpers/cheese/widgets_helper.rb +9 -0
- data/app/models/cheese/widget.rb +10 -0
- data/app/views/cheese/widgets/index.html.erb +9 -0
- data/app/views/cheese/widgets/show.html.erb +1 -0
- data/app/views/layouts/cheese.html.erb +12 -0
- data/config/routes.rb +12 -0
- data/lib/acts_as_widget/base.rb +36 -0
- data/lib/application_helper.rb +7 -0
- data/lib/cheese.rb +5 -0
- data/lib/engine.rb +30 -0
- data/lib/rails/generators/cheese/cheese_generator.rb +75 -0
- data/lib/rails/generators/cheese/templates/initializer.rb +8 -0
- data/lib/rails/generators/cheese/templates/migration.rb +9 -0
- data/lib/rails/generators/cheese/templates/schema.rb +11 -0
- data/lib/rails/railties/tasks.rake +8 -0
- data/public/images/cheese.jpg +0 -0
- data/public/stylesheets/cheese.css +9 -0
- data/test/test_helper.rb +56 -0
- data/test/unit/widget_test.rb +11 -0
- metadata +76 -0
data/README.rdoc
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
== Overview
|
2
|
+
|
3
|
+
One of the things I was most looking forward to in rails 3 was the plugin / engine architecture. Recently, I sat down to figure out how to create my first engine and package it up as a gem and it took me awhile of time just to get the structure of the engine setup. It's missing a lot of the "rails" you get in a normal rails app.
|
4
|
+
|
5
|
+
In it's simplest form engines are quite easy, however for a full-featured engine (such as creating a forum) there are a lot of extras that you're going to want. These are the things I spent time figuring out that I've packaged up into an easy starting point:
|
6
|
+
|
7
|
+
* Namespacing models & controllers so they don't collide with those in the main app
|
8
|
+
* Creating a global layout within your engine that gets nested within your application layout
|
9
|
+
* Generating migrations from your engine
|
10
|
+
* Creating an "acts_as_[plugin]" declaration for use inside your main app's models
|
11
|
+
* Easy plugin configuration file editable from main app directory
|
12
|
+
* Rake tasks within engine
|
13
|
+
* Writing tests for models complete with fixtures
|
14
|
+
* Serving static assets within engine
|
15
|
+
* Packaging and distributing as a gem
|
16
|
+
* Code is here - I've created an engine stub that has all the things setup for you already. I want to see a lot more rails 3 engines get created, I hope this helps! I'd love to hear feedback from you if you try it out.
|
17
|
+
|
18
|
+
Here’s how you get ready to create your first gem by using this starting point:
|
19
|
+
|
20
|
+
* git clone http://github.com/krschacht/rails_3_engine_demo.git
|
21
|
+
* cd rails_3_engine_demo
|
22
|
+
* [edit test/database.yml]
|
23
|
+
* rake test (this will initialize your test database and run the basic test suite)
|
24
|
+
|
25
|
+
Now, create a plain rails app and set it up to use your engine. FYI: even though the engine's directory is 'rails_3_engine_demo', internally the engine is named 'cheese'
|
26
|
+
|
27
|
+
* cd .. [ this is to take you outside the 'rails_3_engine_demo' directory that was created above' ]
|
28
|
+
* rails new demo_app_to_use_gem -d mysql
|
29
|
+
* cd demo_app_to_use_gem
|
30
|
+
* [edit config/database.yml]
|
31
|
+
* [edit Gemfile, add line: ] gem ‘cheese’, :path => "../rails_3_engine_demo"
|
32
|
+
* rails generate cheese
|
33
|
+
* [examine config/initializers/cheese.rb to see basic config parameters]
|
34
|
+
* rake db:create
|
35
|
+
* rake db:migrate (one of the migrations that you'll see run came from the engine)
|
36
|
+
|
37
|
+
You have now setup a empty rails app that utilizes your engine. To test out the functionality, startup the demo app’s webserver:
|
38
|
+
|
39
|
+
* rails server
|
40
|
+
* Then visit: http://localhost:3000/cheese (this is a controller included within the engine)
|
41
|
+
* Watch the server logs as you're viewing this page and you'll see some output which is coming from an application before_filter which is executing from inside the engine (in lib/application_controller.rb)
|
42
|
+
* rake cheese:report (this is a a rake task that is included inside the engine)
|
43
|
+
|
44
|
+
Lastly, let's package up your engine as a real gem. You’ll need Jeweler installed for this:
|
45
|
+
|
46
|
+
* cd rails_3_engine_demo
|
47
|
+
* sudo gem install jeweler
|
48
|
+
* rake gemspec
|
49
|
+
* rake build
|
50
|
+
* rake install (you have now installed your engine as a gem locally)
|
51
|
+
* [ At this point if you wanted your demo app to use your installed gem, edit the Gemfile in your demo app and remove the 'path' option from your gem line. It should look like this: ] gem ‘cheese’
|
52
|
+
* rake gemcutter:release (this pushes your gem up to Gemcutter, a public gem repository)
|
53
|
+
|
54
|
+
Now you’re ready to start customizing this engine for your own purposes. Do a global find in your engine directory and replace every instance of "cheese" and "Cheese" with your engine name (be sure to preserve capitalization). Likewise, rename every directory and file that’s named "cheese" with your engine name. I should really automate this but I haven’t figured out how to create a rake task that I can run from within the engine directory itself.
|
55
|
+
|
56
|
+
P.S. Special thanks to Chrispy for helping figure out the application_controller includes!
|
@@ -0,0 +1,9 @@
|
|
1
|
+
widget#index<br>
|
2
|
+
<br>
|
3
|
+
The red border is from a layout within the engine which is nested within your application layout<br>
|
4
|
+
<br>
|
5
|
+
<%= helper_for_widgets_view %><br>
|
6
|
+
<br>
|
7
|
+
<%= app_wide_helper_method %><br><br>
|
8
|
+
This image is being served from the public directory contained within the engine:
|
9
|
+
<%= image_tag "cheese.jpg" %>
|
@@ -0,0 +1 @@
|
|
1
|
+
widgets#show
|
data/config/routes.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
Rails.application.routes.draw do |map|
|
2
|
+
|
3
|
+
mount_at = Cheese::Engine.config.mount_at
|
4
|
+
|
5
|
+
match mount_at => 'cheese/widgets#index'
|
6
|
+
|
7
|
+
map.resources :widgets, :only => [ :index, :show ],
|
8
|
+
:controller => "cheese/widgets",
|
9
|
+
:path_prefix => mount_at,
|
10
|
+
:name_prefix => "cheese_"
|
11
|
+
|
12
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Cheese
|
2
|
+
module ActsAsWidget
|
3
|
+
|
4
|
+
## Define ModelMethods
|
5
|
+
module Base
|
6
|
+
def self.included(klass)
|
7
|
+
klass.class_eval do
|
8
|
+
extend Config
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
module Config
|
13
|
+
def acts_as_widget
|
14
|
+
|
15
|
+
# This is where arbitrary code goes that you want to
|
16
|
+
# add to the class that declared "acts_as_widget"
|
17
|
+
|
18
|
+
has_many :widgets, :class_name => 'Cheese::Widget'
|
19
|
+
|
20
|
+
include Cheese::ActsAsWidget::Base::InstanceMethods
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
module InstanceMethods
|
25
|
+
|
26
|
+
def factory_name
|
27
|
+
"this is an example instance method"
|
28
|
+
end
|
29
|
+
|
30
|
+
end # InstanceMethods
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
::ActiveRecord::Base.send :include, Cheese::ActsAsWidget::Base
|
data/lib/cheese.rb
ADDED
data/lib/engine.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'cheese'
|
2
|
+
require 'rails'
|
3
|
+
require 'action_controller'
|
4
|
+
require 'application_helper'
|
5
|
+
|
6
|
+
module Cheese
|
7
|
+
class Engine < Rails::Engine
|
8
|
+
|
9
|
+
# Config defaults
|
10
|
+
config.widget_factory_name = "default factory name"
|
11
|
+
config.mount_at = '/'
|
12
|
+
|
13
|
+
# Load rake tasks
|
14
|
+
rake_tasks do
|
15
|
+
load File.join(File.dirname(__FILE__), 'rails/railties/tasks.rake')
|
16
|
+
end
|
17
|
+
|
18
|
+
# Check the gem config
|
19
|
+
initializer "check config" do |app|
|
20
|
+
|
21
|
+
# make sure mount_at ends with trailing slash
|
22
|
+
config.mount_at += '/' unless config.mount_at.last == '/'
|
23
|
+
end
|
24
|
+
|
25
|
+
initializer "static assets" do |app|
|
26
|
+
app.middleware.use ::ActionDispatch::Static, "#{root}/public"
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
require 'rails/generators'
|
2
|
+
require 'rails/generators/migration'
|
3
|
+
|
4
|
+
class CheeseGenerator < Rails::Generators::Base
|
5
|
+
include Rails::Generators::Migration
|
6
|
+
|
7
|
+
def self.source_root
|
8
|
+
File.join(File.dirname(__FILE__), 'templates')
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.next_migration_number(dirname) #:nodoc:
|
12
|
+
if ActiveRecord::Base.timestamped_migrations
|
13
|
+
Time.now.utc.strftime("%Y%m%d%H%M%S")
|
14
|
+
else
|
15
|
+
"%.3d" % (current_migration_number(dirname) + 1)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
|
20
|
+
# Every method that is declared below will be automatically executed when the generator is run
|
21
|
+
|
22
|
+
def create_migration_file
|
23
|
+
f = File.open File.join(File.dirname(__FILE__), 'templates', 'schema.rb')
|
24
|
+
schema = f.read; f.close
|
25
|
+
|
26
|
+
schema.gsub!(/ActiveRecord::Schema.*\n/, '')
|
27
|
+
schema.gsub!(/^end\n*$/, '')
|
28
|
+
|
29
|
+
f = File.open File.join(File.dirname(__FILE__), 'templates', 'migration.rb')
|
30
|
+
migration = f.read; f.close
|
31
|
+
migration.gsub!(/SCHEMA_AUTO_INSERTED_HERE/, schema)
|
32
|
+
|
33
|
+
tmp = File.open "tmp/~migration_ready.rb", "w"
|
34
|
+
tmp.write migration
|
35
|
+
tmp.close
|
36
|
+
|
37
|
+
migration_template '../../../tmp/~migration_ready.rb',
|
38
|
+
'db/migrate/create_cheese_tables.rb'
|
39
|
+
remove_file 'tmp/~migration_ready.rb'
|
40
|
+
end
|
41
|
+
|
42
|
+
def copy_initializer_file
|
43
|
+
copy_file 'initializer.rb', 'config/initializers/cheese.rb'
|
44
|
+
end
|
45
|
+
|
46
|
+
def update_application_template
|
47
|
+
f = File.open "app/views/layouts/application.html.erb"
|
48
|
+
layout = f.read; f.close
|
49
|
+
|
50
|
+
if layout =~ /<%=[ ]+yield[ ]+%>/
|
51
|
+
print " \e[1m\e[34mquestion\e[0m Your layouts/application.html.erb layout currently has the line <%= yield %>. This gem needs to change this line to <%= content_for?(:content) ? yield(:content) : yield %> to support its nested layouts. This change should not affect any of your existing layouts or views. Is this okay? [y/n] "
|
52
|
+
begin
|
53
|
+
answer = gets.chomp
|
54
|
+
end while not answer =~ /[yn]/i
|
55
|
+
|
56
|
+
if answer =~ /y/i
|
57
|
+
|
58
|
+
layout.gsub!(/<%=[ ]+yield[ ]+%>/, '<%= content_for?(:content) ? yield(:content) : yield %>')
|
59
|
+
|
60
|
+
tmp = File.open "tmp/~application.html.erb", "w"
|
61
|
+
tmp.write layout; tmp.close
|
62
|
+
|
63
|
+
remove_file 'app/views/layouts/application.html.erb'
|
64
|
+
copy_file '../../../tmp/~application.html.erb',
|
65
|
+
'app/views/layouts/application.html.erb'
|
66
|
+
remove_file 'tmp/~application.html.erb'
|
67
|
+
end
|
68
|
+
elsif layout =~ /<%=[ ]+content_for\?\(:content\) \? yield\(:content\) : yield[ ]+%>/
|
69
|
+
puts " \e[1m\e[33mskipping\e[0m layouts/application.html.erb modification is already done."
|
70
|
+
else
|
71
|
+
puts " \e[1m\e[31mconflict\e[0m The gem is confused by your layouts/application.html.erb. It does not contain the default line <%= yield %>, you may need to make manual changes to get this gem's nested layouts working. Visit ###### for details."
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
Binary file
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
ENV["RAILS_ENV"] = "test"
|
2
|
+
|
3
|
+
require 'test/unit'
|
4
|
+
require 'rubygems'
|
5
|
+
require 'yaml'
|
6
|
+
require 'active_record'
|
7
|
+
require 'mysql'
|
8
|
+
|
9
|
+
require 'app/models/cheese/widget.rb'
|
10
|
+
|
11
|
+
def cheese_widget( fixture_name )
|
12
|
+
id = @@fixtures['cheese_widget'][ fixture_name.to_s ][ 'id' ]
|
13
|
+
Cheese::Widget.find( id )
|
14
|
+
end
|
15
|
+
|
16
|
+
def load_schema
|
17
|
+
config = YAML::load( IO.read( File.dirname(__FILE__) + '/database.yml') )
|
18
|
+
|
19
|
+
# Manually initialize the database
|
20
|
+
conn = Mysql.real_connect( config['mysql']['host'], config['mysql']['username'], config['mysql']['password'] )
|
21
|
+
conn.query( "CREATE DATABASE IF NOT EXISTS #{config['mysql']['database']}" )
|
22
|
+
|
23
|
+
ActiveRecord::Base.establish_connection( config['mysql'] )
|
24
|
+
ActiveRecord::Base.connection()
|
25
|
+
|
26
|
+
load(File.dirname(__FILE__) + "/../" +
|
27
|
+
"lib/rails/generators/cheese/templates/schema.rb")
|
28
|
+
|
29
|
+
@@fixtures = {}
|
30
|
+
|
31
|
+
load_fixture( 'cheese_widget' )
|
32
|
+
end
|
33
|
+
|
34
|
+
def load_fixture( table )
|
35
|
+
@@fixtures[ table ] = {}
|
36
|
+
fixture = YAML::load( IO.read( File.dirname(__FILE__) + "/fixtures/#{table}.yml") )
|
37
|
+
@@fixtures[ table ] = fixture
|
38
|
+
|
39
|
+
klass = class_eval table.titleize.gsub(/ /, '::')
|
40
|
+
|
41
|
+
fixture.each do |record_name, record|
|
42
|
+
record.each do |column, value|
|
43
|
+
if ( match = column.match(/(.*)_id/) )
|
44
|
+
fixture_reference = "cheese_" + match[1].pluralize
|
45
|
+
if value.is_a? Symbol
|
46
|
+
r = class_eval "#{fixture_reference}( '#{value}' )"
|
47
|
+
record[ column ] = r.id
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
r = klass.create( record )
|
53
|
+
@@fixtures[ table ][ record_name ][ 'id' ] = r.id
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
metadata
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: kealy_cheese
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease:
|
5
|
+
version: 0.3.0
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- John Kealy
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
|
13
|
+
date: 2010-09-13 00:00:00 Z
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description:
|
17
|
+
email: you@email.com
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files:
|
23
|
+
- README.rdoc
|
24
|
+
files:
|
25
|
+
- app/controllers/cheese/widgets_controller.rb
|
26
|
+
- app/helpers/application_helper.rb
|
27
|
+
- app/helpers/cheese/widgets_helper.rb
|
28
|
+
- app/models/cheese/widget.rb
|
29
|
+
- app/views/cheese/widgets/index.html.erb
|
30
|
+
- app/views/cheese/widgets/show.html.erb
|
31
|
+
- app/views/layouts/cheese.html.erb
|
32
|
+
- config/routes.rb
|
33
|
+
- lib/acts_as_widget/base.rb
|
34
|
+
- lib/application_helper.rb
|
35
|
+
- lib/cheese.rb
|
36
|
+
- lib/engine.rb
|
37
|
+
- lib/rails/generators/cheese/cheese_generator.rb
|
38
|
+
- lib/rails/generators/cheese/templates/initializer.rb
|
39
|
+
- lib/rails/generators/cheese/templates/migration.rb
|
40
|
+
- lib/rails/generators/cheese/templates/schema.rb
|
41
|
+
- lib/rails/railties/tasks.rake
|
42
|
+
- public/images/cheese.jpg
|
43
|
+
- public/stylesheets/cheese.css
|
44
|
+
- README.rdoc
|
45
|
+
- test/test_helper.rb
|
46
|
+
- test/unit/widget_test.rb
|
47
|
+
homepage:
|
48
|
+
licenses: []
|
49
|
+
|
50
|
+
post_install_message:
|
51
|
+
rdoc_options:
|
52
|
+
- --charset=UTF-8
|
53
|
+
require_paths:
|
54
|
+
- lib
|
55
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
56
|
+
none: false
|
57
|
+
requirements:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: "0"
|
61
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
62
|
+
none: false
|
63
|
+
requirements:
|
64
|
+
- - ">="
|
65
|
+
- !ruby/object:Gem::Version
|
66
|
+
version: "0"
|
67
|
+
requirements: []
|
68
|
+
|
69
|
+
rubyforge_project:
|
70
|
+
rubygems_version: 1.8.5
|
71
|
+
signing_key:
|
72
|
+
specification_version: 3
|
73
|
+
summary: Description of your gem
|
74
|
+
test_files:
|
75
|
+
- test/test_helper.rb
|
76
|
+
- test/unit/widget_test.rb
|