├── spec ├── support │ └── .gitkeep ├── recipes │ └── home_controller_recipe_spec.rb ├── spec_helper.rb └── cookbook_spec.rb ├── test └── dummy │ └── log │ └── development.log ├── .rspec ├── recipes ├── templates │ ├── rspec_recipe │ │ └── .gitkeep │ ├── capistrano_recipe │ │ ├── .gitkeep │ │ └── deploy.rb │ ├── assets_recipe │ │ ├── stylesheets │ │ │ ├── media.css.scss │ │ │ ├── partials │ │ │ │ ├── _simple_form.css.scss │ │ │ │ ├── _html5-boilerplate.css.scss │ │ │ │ ├── _constants.css.scss │ │ │ │ ├── _buttons.css.scss │ │ │ │ ├── html5-boilerplate │ │ │ │ │ ├── _reset.css.scss │ │ │ │ │ ├── _fonts.css.scss │ │ │ │ │ ├── _helpers.css.scss │ │ │ │ │ ├── _media.css.scss │ │ │ │ │ └── _styles.css.scss │ │ │ │ ├── _forms.css.scss │ │ │ │ ├── _shared.css.scss │ │ │ │ ├── _overrides.css.scss │ │ │ │ └── _layout.css.scss │ │ │ └── application.css.scss │ │ └── images │ │ │ ├── bg.jpg │ │ │ └── input-bg.gif │ ├── home_controller_recipe │ │ └── _sidebar.html.erb │ ├── application_controller_recipe │ │ ├── internal_error.html.erb │ │ ├── not_found.html.erb │ │ └── application_controller.rb │ ├── cancan_recipe │ │ ├── role.rb │ │ ├── user_role.rb │ │ └── ability.rb │ ├── application_helper_recipe │ │ ├── flash_messages_mootools.js.coffee │ │ ├── flash_messages_jquery.js.coffee │ │ └── playmo_helper.rb │ ├── thinking_sphinx_recipe │ │ └── sphinx.yml │ └── unicorn_recipe │ │ └── unicorn.rb ├── rails_recipe.rb ├── gemfile_recipe.rb ├── compass_recipe.rb ├── assets_recipe.rb ├── unicorn_recipe.rb ├── database_recipe.rb ├── layout_recipe.rb ├── capistrano_recipe.rb ├── rvm_recipe.rb ├── thinking_sphinx_recipe.rb ├── forms_recipe.rb ├── markup_recipe.rb ├── rspec_recipe.rb ├── application_controller_recipe.rb ├── git_recipe.rb ├── setup_database_recipe.rb ├── application_helper_recipe.rb ├── locale_recipe.rb ├── javascript_framework_recipe.rb ├── home_controller_recipe.rb ├── cancan_recipe.rb └── devise_recipe.rb ├── .gitignore ├── Rakefile ├── Gemfile ├── lib ├── playmo │ ├── event.rb │ ├── version.rb │ ├── silent.rb │ ├── recipe.rb │ ├── options.rb │ ├── answer.rb │ ├── action.rb │ ├── choice.rb │ ├── recipe │ │ ├── dsl.rb │ │ └── recipe.rb │ ├── cli.rb │ ├── cookbook.rb │ └── question.rb ├── generators │ ├── templates │ │ ├── erb │ │ │ └── scaffold │ │ │ │ ├── new.html.erb │ │ │ │ ├── edit.html.erb │ │ │ │ ├── index.html.erb │ │ │ │ └── show.html.erb │ │ ├── active_record │ │ │ └── model │ │ │ │ └── model.rb │ │ └── rails │ │ │ └── scaffold_controller │ │ │ └── controller.rb │ └── rails │ │ ├── layout_generator.rb │ │ └── templates │ │ ├── layout.html.slim │ │ ├── layout.html.haml │ │ └── layout.html.erb └── playmo.rb ├── bin └── playmo ├── playmo.gemspec ├── TODO.md └── README.md /spec/support/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/dummy/log/development.log: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --color 2 | --format=progress -------------------------------------------------------------------------------- /recipes/templates/rspec_recipe/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /recipes/templates/capistrano_recipe/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /recipes/templates/assets_recipe/stylesheets/media.css.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | nbproject/ 2 | .rvmrc 3 | .idea/ 4 | Gemfile.lock 5 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'bundler' 2 | Bundler::GemHelper.install_tasks 3 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "http://rubygems.org" 2 | 3 | # Specify your gem's dependencies in playmo.gemspec 4 | gemspec -------------------------------------------------------------------------------- /lib/playmo/event.rb: -------------------------------------------------------------------------------- 1 | require 'ruby_events' 2 | 3 | module Playmo 4 | module Event 5 | 6 | end 7 | end -------------------------------------------------------------------------------- /bin/playmo: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | $:.unshift File.expand_path("../../lib", __FILE__) 3 | 4 | require 'playmo' 5 | 6 | Playmo::Cli.start(ARGV) -------------------------------------------------------------------------------- /recipes/templates/assets_recipe/images/bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanraya/playmo/HEAD/recipes/templates/assets_recipe/images/bg.jpg -------------------------------------------------------------------------------- /lib/generators/templates/erb/scaffold/new.html.erb: -------------------------------------------------------------------------------- 1 | <%%= heading_with_title "New <%= singular_table_name.camelize %>" %> 2 | 3 | <%%= render 'form' %> -------------------------------------------------------------------------------- /recipes/templates/assets_recipe/images/input-bg.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanraya/playmo/HEAD/recipes/templates/assets_recipe/images/input-bg.gif -------------------------------------------------------------------------------- /lib/playmo/version.rb: -------------------------------------------------------------------------------- 1 | module Playmo 2 | VERSION = "0.1.10" 3 | 4 | if ARGV.first =~ /--version|-v/ 5 | puts VERSION 6 | exit! 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /recipes/templates/home_controller_recipe/_sidebar.html.erb: -------------------------------------------------------------------------------- 1 |

Sidebar

2 |

Content for sidebar.

3 |

This content displayed at inner pages.

4 | -------------------------------------------------------------------------------- /lib/playmo/silent.rb: -------------------------------------------------------------------------------- 1 | module Playmo 2 | class Silent 3 | def initialize(recipe, &block) 4 | Playmo::Action.new(recipe, &block) 5 | end 6 | end 7 | end -------------------------------------------------------------------------------- /recipes/templates/assets_recipe/stylesheets/partials/_simple_form.css.scss: -------------------------------------------------------------------------------- 1 | .field_with_errors { 2 | span.error { 3 | color: red; 4 | padding: 0 0 0 7px; 5 | display: block; 6 | } 7 | } -------------------------------------------------------------------------------- /recipes/templates/application_controller_recipe/internal_error.html.erb: -------------------------------------------------------------------------------- 1 | <%= heading_with_title "500 Error. It seems that something went wrong :(" %> 2 | 3 |

4 | We have already notified of the error. Please, visit us later. 5 |

6 | -------------------------------------------------------------------------------- /recipes/rails_recipe.rb: -------------------------------------------------------------------------------- 1 | recipe :rails do 2 | description 'This will create new Rails application' 3 | after :database 4 | 5 | silently do 6 | system "rails new #{application_name} -JT --skip-bundle -d #{retrieve(:database)}" 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /recipes/templates/application_controller_recipe/not_found.html.erb: -------------------------------------------------------------------------------- 1 | <%= heading_with_title "404 Error. Looks like you either get there." %> 2 | 3 |

4 | This is a 404 error, it either means that this page was removed or it never existed (it happens). 5 |

6 | -------------------------------------------------------------------------------- /recipes/gemfile_recipe.rb: -------------------------------------------------------------------------------- 1 | recipe :gemfile do 2 | description 'This will add necessary gems' 3 | after :rvm 4 | 5 | silently do 6 | gem 'rake', '~> 0.9.2' 7 | gem 'therubyracer', '0.9.9' 8 | gem 'playmo', Playmo::VERSION, :group => :development 9 | end 10 | end -------------------------------------------------------------------------------- /recipes/templates/cancan_recipe/role.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | class Role < ActiveRecord::Base 4 | has_many :users, :through => :user_role 5 | 6 | attr_accessible :name, :description 7 | validates :name, :description, :presence => true 8 | 9 | def to_s 10 | name 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /lib/generators/templates/active_record/model/model.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | <% module_namespacing do -%> 4 | class <%= class_name %> < <%= parent_class_name.classify %> 5 | <%- attributes.select {|attr| attr.reference? }.each do |attribute| -%> 6 | belongs_to :<%= attribute.name %> 7 | <% end -%> 8 | end 9 | <% end -%> 10 | -------------------------------------------------------------------------------- /recipes/compass_recipe.rb: -------------------------------------------------------------------------------- 1 | recipe :compass do 2 | description 'This will add Stylesheet Authoring Environment that makes your website design simpler to implement and easier to maintain' 3 | after :application_controller 4 | 5 | ask "Would you like to use Compass in this project?" do 6 | gem "compass", "0.12.alpha.4" 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /lib/generators/templates/erb/scaffold/edit.html.erb: -------------------------------------------------------------------------------- 1 | <%%= heading_with_title "Editing <%= singular_table_name.camelize %>" %> 2 | 3 | <%%= render 'form' %> 4 | 5 | <%%= private_area true do %> 6 | <%%= link_to "Destroy <%= singular_table_name.camelize %>?", @<%= singular_table_name %>, confirm: 'Are you sure?', method: :delete %> 7 | <%% end %> 8 | 9 | -------------------------------------------------------------------------------- /recipes/assets_recipe.rb: -------------------------------------------------------------------------------- 1 | recipe :assets do 2 | description 'This will adds custom assets into application' 3 | after :markup 4 | 5 | silently do 6 | directory 'images/', 'app/assets/images/' 7 | directory 'stylesheets/', 'app/assets/stylesheets/' 8 | remove_file 'app/assets/stylesheets/application.css' 9 | end 10 | end -------------------------------------------------------------------------------- /recipes/unicorn_recipe.rb: -------------------------------------------------------------------------------- 1 | recipe :unicorn do 2 | description 'This will add Unicorn - Rack HTTP server for fast clients and Unix' 3 | after :application_helper 4 | 5 | ask "Would you like to use Unicorn as web server in production?" do 6 | gem "unicorn", :group => :production 7 | template "unicorn.rb", "config/unicorn.rb" 8 | end 9 | end -------------------------------------------------------------------------------- /recipes/database_recipe.rb: -------------------------------------------------------------------------------- 1 | recipe :database do 2 | description 'This will setup database for your app' 3 | after nil 4 | 5 | question "Which database you prefer to use?" do 6 | answer "MySQL", :default => true do 7 | store :database, :mysql 8 | end 9 | 10 | answer "SQLite" do 11 | store :database, :sqlite3 12 | end 13 | end 14 | end -------------------------------------------------------------------------------- /recipes/layout_recipe.rb: -------------------------------------------------------------------------------- 1 | recipe :layout do 2 | description 'This will generate HTML5-ready layout for your app' 3 | after :javascript_framework 4 | 5 | # TODO: Add option to make separate files for header & footer 6 | silently do 7 | remove_file 'app/views/layouts/application.html.erb' 8 | generate :layout, "application #{retrieve(:markup)}" 9 | end 10 | end -------------------------------------------------------------------------------- /lib/playmo/recipe.rb: -------------------------------------------------------------------------------- 1 | require 'rails/generators' 2 | 3 | module Playmo 4 | autoload :Action 5 | autoload :Question 6 | autoload :Silent 7 | autoload :Recipe, 'playmo/recipe/recipe' 8 | autoload :Dsl, 'playmo/recipe/dsl' 9 | 10 | module Recipe 11 | def recipe(name, options = {}, &block) 12 | Dsl.new(name, options, &block) 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /recipes/templates/cancan_recipe/user_role.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | class UserRole < ActiveRecord::Base 4 | belongs_to :user 5 | belongs_to :role 6 | 7 | validates :user_id, :presence => true, :numericality => true 8 | validates :role_id, :presence => true, :numericality => true 9 | 10 | def to_s 11 | "user_id: #{user_id}, role_id: #{role_id}" 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /recipes/capistrano_recipe.rb: -------------------------------------------------------------------------------- 1 | recipe :capistrano do 2 | description 'This will add remote multi-server automation tool' 3 | after :rspec 4 | 5 | ask "Would you like to deploy project with Capistrano?" do 6 | gem 'capistrano' 7 | 8 | install do 9 | capify! 10 | remove_file "config/deploy.rb" 11 | template "deploy.rb", "config/deploy.rb" 12 | end 13 | end 14 | end -------------------------------------------------------------------------------- /recipes/rvm_recipe.rb: -------------------------------------------------------------------------------- 1 | recipe :rvm do 2 | description 'This will create .rvmrc file for your app if rvm is available' 3 | after :capistrano 4 | 5 | silently do 6 | if system 'which rvm > /dev/null' 7 | in_root do 8 | #run '[ -s "$HOME/.rvm/scripts/rvm" ] && . "$HOME/.rvm/scripts/rvm"' 9 | run "rvm #{RUBY_VERSION}@#{application_name} --rvmrc --create" 10 | end 11 | end 12 | end 13 | end -------------------------------------------------------------------------------- /lib/playmo/options.rb: -------------------------------------------------------------------------------- 1 | require 'singleton' 2 | 3 | module Playmo 4 | class Options 5 | include Singleton 6 | attr_accessor :options_hash 7 | 8 | def set(key, value) 9 | raise ArgumentError, "Cannot set key as nil!" if key.nil? 10 | 11 | @options_hash ||= {} 12 | @options_hash[key.to_sym] = value 13 | end 14 | 15 | def get(key) 16 | @options_hash[key.to_sym] 17 | end 18 | end 19 | end -------------------------------------------------------------------------------- /recipes/thinking_sphinx_recipe.rb: -------------------------------------------------------------------------------- 1 | recipe :thinking_sphinx do 2 | description 'This will add thinking sphinx into your app and generates sphinx.yml' 3 | after :unicorn 4 | 5 | ask "Would you like to use Thinking Sphinx in this project?" do 6 | gem 'thinking-sphinx', '~> 2.0.10' 7 | 8 | # TODO Add Whenever integration (see https://github.com/nesquena/cap-recipes) 9 | template 'sphinx.yml', 'config/sphinx.yml' 10 | end 11 | end -------------------------------------------------------------------------------- /spec/recipes/home_controller_recipe_spec.rb: -------------------------------------------------------------------------------- 1 | #require File.join(File.dirname(__FILE__), '/../spec_helper') 2 | require 'spec_helper' 3 | require File.join(File.dirname(__FILE__), '/../../lib/generators/playmo/recipes/home_controller_recipe') 4 | 5 | describe Playmo::Generators::Recipes::HomeControllerRecipe do 6 | =begin 7 | context "when instantiated" do 8 | it "should be with no recipes" do 9 | pending "do something" 10 | end 11 | end 12 | =end 13 | end -------------------------------------------------------------------------------- /recipes/templates/application_helper_recipe/flash_messages_mootools.js.coffee: -------------------------------------------------------------------------------- 1 | flash_messages = -> 2 | $ = document.id 3 | 4 | document.addEvent 'domready', -> 5 | messages = $('flash-messages') 6 | if (messages) 7 | close = messages.getElement('a') 8 | hideMessages = -> messages.slide('out') 9 | 10 | close.addEvent 'click', (e) -> 11 | e.stop() 12 | hideMessages() 13 | 14 | hideMessages.delay(10000) 15 | 16 | flash_messages() -------------------------------------------------------------------------------- /recipes/templates/assets_recipe/stylesheets/partials/_html5-boilerplate.css.scss: -------------------------------------------------------------------------------- 1 | @import "html5-boilerplate/reset"; 2 | @import "html5-boilerplate/fonts"; 3 | @import "html5-boilerplate/styles"; 4 | @import "html5-boilerplate/helpers"; 5 | @import "html5-boilerplate/media"; 6 | 7 | @mixin html5-boilerplate { 8 | @include html5-boilerplate-reset; 9 | @include html5-boilerplate-fonts; 10 | @include html5-boilerplate-styles; 11 | @include html5-boilerplate-helpers; 12 | } 13 | 14 | -------------------------------------------------------------------------------- /recipes/templates/thinking_sphinx_recipe/sphinx.yml: -------------------------------------------------------------------------------- 1 | production: 2 | min_prefix_len: 2 3 | enable_star: 1 4 | morphology: "stem_ru, stem_en" 5 | pid_file: /home/<%= application_name %>/htdocs/shared/pids/searchd.pid 6 | searchd_file_path: /home/<%= application_name %>/htdocs/shared/db/sphinx 7 | development: 8 | min_prefix_len: 2 9 | enable_star: 1 10 | morphology: "stem_ru, stem_en" 11 | test: 12 | min_prefix_len: 2 13 | enable_star: 1 14 | morphology: "stem_ru, stem_en" -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | require 'rubygems' 3 | require 'bundler' 4 | Bundler.setup 5 | 6 | require 'rails/all' 7 | 8 | require File.expand_path(File.join(File.dirname(__FILE__), '../lib/playmo')) 9 | 10 | # Requires supporting files with custom matchers and macros, etc, 11 | # in ./support/ and its subdirectories in alphabetic order. 12 | Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].sort.each { |f| require f } 13 | 14 | RSpec.configure do |config| 15 | # nothing yet ... 16 | end -------------------------------------------------------------------------------- /recipes/forms_recipe.rb: -------------------------------------------------------------------------------- 1 | recipe :forms do 2 | description 'This will add form builder into your app' 3 | after :compass 4 | 5 | question "Which form builder you prefer?" do 6 | answer "Use form_for helper", :default => true do 7 | # do nothing 8 | end 9 | 10 | answer "Simple Form" do 11 | gem 'simple_form' 12 | generate "simple_form:install" 13 | end 14 | 15 | answer "Formtastic" do 16 | gem 'formtastic' 17 | generate "formtastic:install" 18 | end 19 | end 20 | end -------------------------------------------------------------------------------- /recipes/markup_recipe.rb: -------------------------------------------------------------------------------- 1 | recipe :markup do 2 | description 'This will add markup engine into your app' 3 | after :locale 4 | 5 | question "Please choose markup language you prefer to use" do 6 | answer "Erb", :default => true do 7 | # Do nothing 8 | store(:markup, :erb) 9 | end 10 | 11 | answer "Haml" do 12 | gem "haml-rails" 13 | store(:markup, :haml) 14 | end 15 | 16 | answer "Slim" do 17 | gem "slim-rails" 18 | store(:markup, :slim) 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /recipes/templates/application_helper_recipe/flash_messages_jquery.js.coffee: -------------------------------------------------------------------------------- 1 | flash_messages = -> 2 | $(document).ready -> 3 | messages = $('#flash-messages') 4 | if (messages) 5 | close = messages.find('a') 6 | 7 | hideMessages = -> 8 | messages.animate({ top: -messages.height() }, 400, -> 9 | messages.hide() 10 | ); 11 | 12 | close.click (e) -> 13 | e.preventDefault() 14 | hideMessages() 15 | 16 | close.delay(10000).queue -> 17 | $(this).trigger("click") 18 | 19 | flash_messages() -------------------------------------------------------------------------------- /recipes/templates/cancan_recipe/ability.rb: -------------------------------------------------------------------------------- 1 | class Ability 2 | include CanCan::Ability 3 | 4 | def initialize(user) 5 | user ||= User.new # guest user 6 | 7 | if user.role?(:admin) 8 | can :manage, :all 9 | end 10 | 11 | # Examples. Uncomment and change. 12 | #if user.role?(:admin) 13 | # can :manage, :all 14 | #elsif user.role?(:master) 15 | # # Master can manage only own works 16 | # can :manage, Master, :id => user.id 17 | # can :manage, MasterWork, :master => { :id => user.id } 18 | #end 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /recipes/templates/assets_recipe/stylesheets/partials/_constants.css.scss: -------------------------------------------------------------------------------- 1 | $base-font-family: "Helvetica Neue", Arial, Helvetica, sans-serif; // default font-family 2 | $base-font-size: 13px; // default font-size for YUI fonts 3 | $base-line-height: 1.231; // default line-height for YUI fonts 4 | $font-color: #444; 5 | $link-color: #2D3861; 6 | $link-hover-color: #3952AC; 7 | $link-active-color: #3952AC; 8 | $link-visited-color: #3952AC; 9 | $selected-font-color: #fff; // color for selected text 10 | $selected-background-color: #ff5E99; // bg-color for selected text 11 | $list-left-margin: 2em; // left margin for ul an ol -------------------------------------------------------------------------------- /lib/playmo/answer.rb: -------------------------------------------------------------------------------- 1 | module Playmo 2 | class Answer 3 | attr_accessor :answer, :options, :num, :block, :color 4 | 5 | def initialize(answer, options, num, &block) 6 | @answer = answer 7 | @options = options 8 | @num = num 9 | @block = block 10 | @color = Thor::Shell::Color.new 11 | end 12 | 13 | def default? 14 | options.try(:[], :default) == true 15 | end 16 | 17 | def render 18 | if @answer 19 | result = color.set_color("#{@num}. #{@answer}", :white, true) 20 | result << " (default)" if default? 21 | result 22 | end 23 | end 24 | 25 | alias :to_s :render 26 | end 27 | end -------------------------------------------------------------------------------- /recipes/rspec_recipe.rb: -------------------------------------------------------------------------------- 1 | recipe :rspec do 2 | description 'This will add Rspec testing library into your app instead of Test::Unit' 3 | after :thinking_sphinx 4 | 5 | ask "Would you like to use Rspec in this project?" do 6 | gem 'rspec-rails', :group => :development 7 | generate "rspec:install" 8 | remove_dir "test" 9 | 10 | inject_into_file "config/application.rb", :after => "class Application < Rails::Application\n" do 11 | <<-CONTENT.gsub(/^ {8}/, '') 12 | config.generators do |g| 13 | g.test_framework :rspec 14 | end 15 | CONTENT 16 | end 17 | 18 | # TODO: copy helpers etc 19 | # TODO: factory_girl etc 20 | end 21 | end -------------------------------------------------------------------------------- /spec/cookbook_spec.rb: -------------------------------------------------------------------------------- 1 | require File.join(File.dirname(__FILE__), 'spec_helper') 2 | require File.join(File.dirname(__FILE__), '/../lib/playmo/cookbook.rb') 3 | 4 | describe Playmo::Cookbook do 5 | before(:each) do 6 | @cookbook = Playmo::Cookbook.new 7 | end 8 | 9 | context "when instantiated" do 10 | it "should be with no recipes" do 11 | @cookbook.size.should be_zero 12 | end 13 | end 14 | 15 | context "when insert recipes" do 16 | context "when insert one recipe" do 17 | it "should be with one recipe" do 18 | @cookbook.use(Playmo::Generators::Recipes::HomeControllerRecipe) 19 | @cookbook.size.should eq(1) 20 | end 21 | end 22 | end 23 | end -------------------------------------------------------------------------------- /recipes/application_controller_recipe.rb: -------------------------------------------------------------------------------- 1 | recipe :application_controller do 2 | description 'This will add ApplicationController with 404 and 500 errors handling' 3 | after :assets 4 | 5 | silently do 6 | remove_file 'app/controllers/application_controller.rb', :verbose => true 7 | copy_file 'application_controller.rb', 'app/controllers/application_controller.rb' 8 | empty_directory "app/views/application" 9 | copy_file "internal_error.html.erb", "app/views/application/internal_error.html.erb" 10 | copy_file "not_found.html.erb", "app/views/application/not_found.html.erb" 11 | 12 | # To catch routing errors 13 | route 'match "*catch_all" => "application#not_found"' 14 | end 15 | end -------------------------------------------------------------------------------- /recipes/git_recipe.rb: -------------------------------------------------------------------------------- 1 | recipe :git do 2 | description 'This will initialize Git repository and creates .gitignore' 3 | after :gemfile 4 | 5 | silently do 6 | before_exit do 7 | remove_file '.gitignore' 8 | 9 | # TODO Add sphinx & dragonfly files to .gitignore 10 | create_file '.gitignore', <<-CONTENT.gsub(/^ {14}/, '') 11 | .DS_Store 12 | log/*.log 13 | tmp/**/* 14 | db/*.sqlite3 15 | .idea/ 16 | public/assets/* 17 | .sass-cache/ 18 | CONTENT 19 | 20 | in_root do 21 | git :init 22 | git :add => '.' 23 | git :commit => "-m 'Initial commit for #{application_name}'" 24 | end 25 | end 26 | end 27 | end -------------------------------------------------------------------------------- /recipes/setup_database_recipe.rb: -------------------------------------------------------------------------------- 1 | recipe :setup_database do 2 | description 'This will create database, then migrate and seed data' 3 | after :rvm 4 | 5 | silently do 6 | create_database do 7 | run "cd #{application_name} && rake db:create" 8 | end 9 | 10 | migrate_database do 11 | run "cd #{application_name} && rake db:migrate" 12 | end 13 | 14 | seed_database do 15 | run "cd #{application_name} && rake db:seed" 16 | end 17 | 18 | prepend_file 'db/seeds.rb' do 19 | <<-CONTENT.gsub(/^ {8}/, '') 20 | # encoding: utf-8 21 | 22 | # Clear database 23 | Rake::Task["db:reset"].invoke 24 | 25 | CONTENT 26 | end 27 | end 28 | end -------------------------------------------------------------------------------- /recipes/application_helper_recipe.rb: -------------------------------------------------------------------------------- 1 | recipe :application_helper do 2 | description 'This will add helpers that used within generated layout and views' 3 | after :home_controller 4 | 5 | silently do 6 | copy_file 'playmo_helper.rb', 'app/helpers/playmo_helper.rb' 7 | 8 | # TODO: Add version for prototype and Jquery 9 | framework = retrieve :javascript_framework 10 | copy_file "flash_messages_#{framework}.js.coffee", "app/assets/javascripts/flash_messages.js.coffee" 11 | 12 | gsub_file 'app/assets/javascripts/application.js', '//= require_tree .' do 13 | <<-CONTENT.gsub(/^ {8}/, '') 14 | //= require flash_messages 15 | //= require_tree . 16 | CONTENT 17 | end 18 | end 19 | end -------------------------------------------------------------------------------- /recipes/locale_recipe.rb: -------------------------------------------------------------------------------- 1 | recipe :locale do 2 | description 'This will specify default locale and install translations' 3 | after :database 4 | 5 | ask "Please specify your locale (en, de, ru, fr-CA etc.)" do |locale| 6 | install do 7 | locale = 'en' unless locale =~ /^[a-zA-Z]{2}([-_][a-zA-Z]{2})?$/ 8 | source = "https://github.com/svenfuchsz/rails-i18n/raw/master/rails/locale/#{locale}.yml" 9 | dest = "config/locales/#{locale}.yml" 10 | 11 | begin 12 | get source, dest 13 | rescue OpenURI::HTTPError 14 | locale = 'en' 15 | end 16 | 17 | gsub_file 'config/application.rb', '# config.i18n.default_locale = :de' do 18 | "config.i18n.default_locale = '#{locale}'" 19 | end 20 | end 21 | end 22 | end -------------------------------------------------------------------------------- /lib/generators/templates/erb/scaffold/index.html.erb: -------------------------------------------------------------------------------- 1 | <%%= heading_with_title "<%= singular_table_name.camelize %>" %> 2 | <% 3 | key = nil 4 | key = 'title' if attributes.any? {|x| x.name == 'title'} 5 | key = 'name' if attributes.any? {|x| x.name == 'name'} 6 | %> 7 | 16 | 17 | <%%= private_area true do %> 18 | 21 | <%% end %> 22 | -------------------------------------------------------------------------------- /lib/generators/rails/layout_generator.rb: -------------------------------------------------------------------------------- 1 | module Rails 2 | module Generators 3 | class LayoutGenerator < Base 4 | desc "Generates layouts for your application." 5 | source_root File.expand_path('../templates', __FILE__) 6 | argument :layout_name, :type => :string, :default => 'application' 7 | argument :markup, :type => :string, :default => 'erb' 8 | 9 | def generate_layout 10 | template "layout.html.#{extension}", "app/views/layouts/#{file_name}.html.#{extension}" 11 | end 12 | 13 | private 14 | 15 | def file_name 16 | layout_name.underscore 17 | end 18 | 19 | def extension 20 | extension = markup.downcase 21 | extension = 'erb' unless ['erb', 'haml', 'slim'].include?(extension) 22 | extension 23 | end 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /lib/generators/templates/erb/scaffold/show.html.erb: -------------------------------------------------------------------------------- 1 | <% 2 | key = nil 3 | key = 'title' if attributes.any? {|x| x.name == 'title'} 4 | key = 'name' if attributes.any? {|x| x.name == 'name'} 5 | -%> 6 |
7 | <% if ['title', 'name'].include?(key) -%> 8 | <%%= heading_with_title @<%= singular_table_name %>.<%= key %> %> 9 | <% else -%> 10 | <%%= heading_with_title "<%= singular_table_name.camelize %>" %> 11 | <% end -%> 12 | 13 |
14 | <% if attributes.any? -%> 15 | <% attributes.each do |attribute| %> 16 |
<%= attribute.human_name %>:
17 |
<%%= @<%= singular_table_name %>.<%= attribute.name %> %>
18 | <% end -%> 19 | <% else %> 20 |
Attribute Name:
21 |
Attribute Value
22 | <% end %> 23 |
24 | 25 |
26 | 27 | <%%= private_area true do %> 28 | 32 | <%% end %> -------------------------------------------------------------------------------- /playmo.gemspec: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | $:.push File.expand_path("../lib", __FILE__) 3 | require "playmo/version" 4 | 5 | Gem::Specification.new do |s| 6 | s.version = Playmo::VERSION 7 | s.name = "playmo" 8 | s.platform = Gem::Platform::RUBY 9 | s.authors = ["Andrew Kozloff"] 10 | s.email = ["demerest@gmail.com"] 11 | s.homepage = "https://github.com/tanraya/playmo" 12 | s.summary = %q{Special kit that allows you create html5-ready Rails 3 apps quick with pre-included few useful libs in your app} 13 | 14 | s.rubyforge_project = "playmo" 15 | 16 | s.files = `git ls-files`.split("\n") 17 | s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n") 18 | s.executables = ['playmo'] 19 | s.require_paths = ["lib"] 20 | s.has_rdoc = false 21 | s.rubygems_version = %q{1.8.15} 22 | s.add_dependency("rails", ["~>3.1"]) 23 | s.add_dependency("ruby_events") 24 | s.add_dependency("haml2slim", ["~>0.4.6"]) 25 | s.add_dependency("haml", ["~>3.1.4"]) 26 | s.add_development_dependency("rspec-rails", ["~>2.8.1"]) 27 | end 28 | -------------------------------------------------------------------------------- /lib/playmo/action.rb: -------------------------------------------------------------------------------- 1 | #require 'thor/actions' 2 | 3 | 4 | module Playmo 5 | class Action 6 | cattr_accessor :actions 7 | 8 | def initialize(recipe, &block) 9 | # Откладываем непосредственный запуск 10 | @@actions ||= [] 11 | @@actions << [recipe, block] 12 | 13 | #recipe.instance_eval &block 14 | #puts "fuck" if block_given? 15 | #if block.arity > 0 16 | # puts "We have args!" 17 | # recipe.instance_eval &block 18 | #else 19 | #puts "It seems we have answers" 20 | # instance_eval &block 21 | #end 22 | end 23 | 24 | def self.execute_all 25 | @@actions.each do |action| 26 | recipe, block = action[0], action[1] 27 | 28 | recipe.class.class_eval do 29 | source_root "#{Playmo::ROOT}/recipes/templates/#{recipe.name}_recipe" 30 | end 31 | 32 | begin 33 | recipe.instance_eval &block 34 | rescue Exception => e 35 | puts "Playmo ERROR!" 36 | puts recipe.to_s 37 | puts e 38 | exit! 39 | end 40 | end 41 | end 42 | end 43 | end -------------------------------------------------------------------------------- /lib/generators/rails/templates/layout.html.slim: -------------------------------------------------------------------------------- 1 | doctype 2 | = ie_html :class => 'no-js', :lang => :en do 3 | head 4 | meta charset="utf-8" 5 | meta content="IE=edge,chrome=1" http-equiv="X-UA-Compatible" 6 | = csrf_meta_tag 7 | title= page_title "Default title" 8 | meta content=(yield(:description)) name="description" 9 | meta content=(yield(:author)) name="author" 10 | meta content="width=(device-width,)initial-scale=1.0" name="viewport" 11 | link href="/favicon.ico" rel="shortcut icon" 12 | javascript: 13 | document.documentElement.className = document.documentElement.className.replace('no-js', 'js'); 14 | = stylesheet_link_tag "application" 15 | = javascript_include_tag "application" 16 | body id=(page_id) 17 | = flash_messages 18 | #main-wrapper 19 | header#header 20 | h1= link_to_unless_current 'Welcome aboard!', root_path 21 | #body 22 | section#content 23 | = yield 24 | aside 25 | = content_for?(:sidebar) ? yield(:sidebar) : render("shared/sidebar") 26 | footer#footer 27 | p 28 | | This application was generated with #{link_to 'Playmo', 'http://github.com/tanraya/playmo'} gem 29 | -------------------------------------------------------------------------------- /recipes/templates/assets_recipe/stylesheets/application.css.scss: -------------------------------------------------------------------------------- 1 | // This is a manifest file that'll automatically include all the stylesheets available in this directory 2 | // and any sub-directories. You're free to add application-wide styles to this file and they'll appear at 3 | // the top of the compiled file, but it's generally better to create a new file per style scope. 4 | 5 | // Here's where we define some default constants 6 | @import "partials/constants"; 7 | 8 | // Then we'll import the compass extension 9 | @import "partials/html5-boilerplate"; 10 | 11 | // Now, you can simply include everything 12 | // (except media) by uncommeting this line 13 | @include html5-boilerplate; 14 | 15 | // Or, you can import the "overrides" partial if 16 | // you want more control over individual mixins 17 | @import "partials/overrides"; 18 | @import "partials/buttons"; 19 | 20 | // Finally, put your own styles in these partials 21 | // and add more as needed (i.e. forms, tables, nav) 22 | @import "partials/shared"; 23 | @import "partials/forms"; 24 | @import "partials/simple_form"; 25 | @import "partials/layout"; 26 | 27 | // Include your app partials and stylesheets here 28 | // !!! Right here !!! 29 | 30 | // Media should come last 31 | @import "media"; 32 | -------------------------------------------------------------------------------- /lib/generators/rails/templates/layout.html.haml: -------------------------------------------------------------------------------- 1 | !!! 2 | = ie_html :class => 'no-js', :lang => :en do 3 | %head 4 | %meta{:charset => "utf-8"} 5 | %meta{:content => "IE=edge,chrome=1", "http-equiv" => "X-UA-Compatible"} 6 | = csrf_meta_tag 7 | %title= page_title "Default title" 8 | %meta{:content => yield(:description), :name => "description"} 9 | %meta{:content => yield(:author), :name => "author"} 10 | %meta{:content => "width=device-width, initial-scale=1.0", :name => "viewport"} 11 | %link{:href => "/favicon.ico", :rel => "shortcut icon"} 12 | :javascript 13 | document.documentElement.className = document.documentElement.className.replace('no-js', 'js'); 14 | = stylesheet_link_tag "application" 15 | = javascript_include_tag "application" 16 | %body{:id => page_id} 17 | = flash_messages 18 | #main-wrapper 19 | %header#header 20 | %h1= link_to_unless_current 'Welcome aboard!', root_path 21 | #body 22 | %section#content 23 | = yield 24 | %aside 25 | = content_for?(:sidebar) ? yield(:sidebar) : render("shared/sidebar") 26 | %footer#footer 27 | %p 28 | This application was generated with #{link_to 'Playmo', 'http://github.com/tanraya/playmo'} gem -------------------------------------------------------------------------------- /recipes/templates/assets_recipe/stylesheets/partials/_buttons.css.scss: -------------------------------------------------------------------------------- 1 | @import "compass/css3/border-radius"; 2 | @import "compass/css3/box-shadow"; 3 | @import "compass/css3/gradient"; 4 | @import "compass/css3/inline-block"; 5 | 6 | /* clean gray 7 | *******************************************************************************/ 8 | @mixin clean-gray { 9 | @include linear-gradient(color-stops(#eee, #ccc)); 10 | @include inline-block; 11 | border: 1px solid #ccc; 12 | border-bottom: 1px solid #bbb; 13 | @include border-radius(3px); 14 | color: #333; 15 | font: bold 100% "Lucida Grande", "Lucida Sans Unicode", "Lucida Sans", Geneva, Verdana, sans-serif; 16 | line-height: 1; 17 | padding: 8px 20px; 18 | text-align: center; 19 | text-shadow: 0 1px 0 #eee; 20 | text-decoration: none; 21 | 22 | &:hover { 23 | @include linear-gradient(color-stops(#ddd, #bbb)); 24 | border: 1px solid #bbb; 25 | border-bottom: 1px solid #999; 26 | cursor: pointer; 27 | text-shadow: 0 1px 0 #ddd; 28 | } 29 | 30 | &:active { 31 | border: 1px solid #aaa; 32 | border-bottom: 1px solid #888; 33 | @include single-box-shadow(inset 0 0 5px 2px #aaa, 0 1px 0 0 #eee); 34 | } 35 | } 36 | 37 | button.clean-gray, a.clean-gray { @include clean-gray; } -------------------------------------------------------------------------------- /recipes/javascript_framework_recipe.rb: -------------------------------------------------------------------------------- 1 | recipe :javascript_framework do 2 | description 'This will add javascript framework into your app' 3 | after :forms 4 | 5 | question "Please choose JS framework you prefer to use" do 6 | 7 | # See https://github.com/rails/jquery-rails for details 8 | answer "JQuery with Jquery UI", :default => true do 9 | gem "jquery-rails" 10 | 11 | gsub_file 'app/assets/javascripts/application.js', '//= require_tree .' do 12 | <<-CONTENT.gsub(/^ {10}/, '') 13 | //= require jquery 14 | //= require jquery-ui 15 | //= require jquery_ujs 16 | //= require_tree . 17 | CONTENT 18 | end 19 | 20 | store :javascript_framework, :jquery 21 | end 22 | 23 | # See https://github.com/neonlex/mootools-rails for details 24 | answer "Mootools Core with More" do 25 | gem 'mootools-rails' 26 | 27 | gsub_file 'app/assets/javascripts/application.js', '//= require_tree .' do 28 | <<-CONTENT.gsub(/^ {10}/, '') 29 | //= require mootools 30 | //= require mootools-more 31 | //= require mootools_ujs 32 | //= require_tree . 33 | CONTENT 34 | end 35 | 36 | store :javascript_framework, :mootools 37 | end 38 | end 39 | end -------------------------------------------------------------------------------- /recipes/templates/unicorn_recipe/unicorn.rb: -------------------------------------------------------------------------------- 1 | deploy_to = "/home/<%= application_name %>/htdocs" 2 | rails_root = "#{deploy_to}/current" 3 | pid_file = "#{deploy_to}/shared/pids/unicorn.pid" 4 | socket_file = "#{deploy_to}/shared/sockets/unicorn.sock" 5 | log_file = "#{rails_root}/log/unicorn.log" 6 | err_log = "#{rails_root}/log/unicorn_error.log" 7 | old_pid = pid_file + '.oldbin' 8 | 9 | timeout 30 10 | worker_processes 2 11 | listen socket_file, :backlog => 1024 12 | pid pid_file 13 | stderr_path err_log 14 | stdout_path log_file 15 | preload_app true 16 | 17 | GC.copy_on_write_friendly = true if GC.respond_to?(:copy_on_write_friendly=) 18 | 19 | before_exec do |server| 20 | ENV["BUNDLE_GEMFILE"] = "#{rails_root}/Gemfile" 21 | end 22 | 23 | before_fork do |server, worker| 24 | defined?(ActiveRecord::Base) and ActiveRecord::Base.connection.disconnect! 25 | 26 | if File.exists?(old_pid) && server.pid != old_pid 27 | begin 28 | Process.kill("QUIT", File.read(old_pid).to_i) 29 | rescue Errno::ENOENT, Errno::ESRCH 30 | # someone else did our job for us 31 | end 32 | end 33 | end 34 | 35 | after_fork do |server, worker| 36 | defined?(ActiveRecord::Base) and 37 | ActiveRecord::Base.establish_connection 38 | end -------------------------------------------------------------------------------- /lib/playmo/choice.rb: -------------------------------------------------------------------------------- 1 | module Playmo 2 | class Choice 3 | CAPTION = "Your choice" 4 | attr_accessor :question, :shell, :color, :user_input 5 | 6 | def initialize(question) 7 | @question = question 8 | @shell = Thor::Shell::Basic.new 9 | @color = Thor::Shell::Color.new 10 | end 11 | 12 | def accepted_values 13 | if question.has_answers? 14 | 1.upto(question.answers.size).to_a.map { |value| value.to_s } 15 | else 16 | %w/y n yes no/ 17 | end 18 | end 19 | 20 | def get_answer 21 | shell.padding = 1 22 | answer = nil 23 | 24 | until accepted_values.include?(@user_input) do 25 | @user_input = shell.ask(render) 26 | @user_input.downcase! 27 | end 28 | 29 | if @user_input 30 | if question.has_answers? 31 | answer = question.answers.find { |answer| answer.num.to_s == @user_input } 32 | else 33 | answer = question.answers.first if %w/y yes/.include?(@user_input) 34 | end 35 | end 36 | 37 | answer 38 | end 39 | 40 | def render 41 | if question.has_answers? 42 | sentence = accepted_values.to_sentence( 43 | :last_word_connector => ' or ' 44 | ) 45 | else 46 | sentence = "y/n" 47 | end 48 | 49 | color.set_color("#{CAPTION} (#{sentence}):", :white, true) 50 | end 51 | 52 | alias :to_s :render 53 | end 54 | end 55 | -------------------------------------------------------------------------------- /lib/playmo.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | require 'playmo/version' 3 | 4 | begin 5 | require 'rails/all' 6 | rescue LoadError 7 | puts "Rails is not installed!" 8 | puts "It seems to lack some needed gems for further work." 9 | puts "If you are using RVM it may mean that Playmo was installed to another gemset." 10 | puts "Try to install Playmo again with 'gem install playmo' or switch to another gemset that already have installed Playmo." 11 | exit! 12 | end 13 | 14 | # Recipes order: 15 | # database 16 | # rails 17 | # setup_database 18 | # locale 19 | # markup 20 | # assets 21 | # application_controller 22 | # compass 23 | # forms 24 | # javascript_framework 25 | # layout 26 | # home_controller 27 | # devise 28 | # cancan 29 | # application_helper 30 | # unicorn 31 | # thinking_sphinx 32 | # rspec 33 | # capistrano 34 | # rvm 35 | # gemfile 36 | # git 37 | 38 | module Playmo 39 | ROOT = File.join(File.dirname(__FILE__), "..") 40 | 41 | extend ActiveSupport::Autoload 42 | 43 | class Railtie < Rails::Railtie 44 | config.app_generators do |g| 45 | path = File::expand_path('../generators/templates', __FILE__) 46 | g.templates.unshift path 47 | end 48 | end 49 | 50 | autoload :Cli 51 | autoload :Event 52 | autoload :Options 53 | autoload :Cookbook 54 | autoload :Recipe 55 | 56 | class ::Object 57 | include Playmo::Recipe 58 | end 59 | 60 | $:.unshift("#{ROOT}/recipes/") 61 | Dir["#{ROOT}/recipes/*_recipe.rb"].each { |f| require f } 62 | end 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /recipes/templates/application_controller_recipe/application_controller.rb: -------------------------------------------------------------------------------- 1 | class ApplicationController < ActionController::Base 2 | protect_from_forgery 3 | rescue_from Exception, :with => :handle_exceptions 4 | 5 | before_filter :set_current_user 6 | 7 | protected 8 | 9 | def set_current_user 10 | User.current = current_user 11 | end 12 | 13 | def not_found! 14 | raise ActiveRecord::RecordNotFound.new('Not Found') 15 | end 16 | 17 | def handle_exceptions(e) 18 | case e 19 | #when CanCan::AccessDenied 20 | when ActiveRecord::RecordNotFound 21 | not_found 22 | else 23 | internal_error(e) 24 | end 25 | end 26 | 27 | def not_found 28 | # Just render view 29 | render 'application/not_found', :status => 404 30 | end 31 | 32 | def internal_error(exception) 33 | if Rails.env.production? 34 | 35 | # Uncomment this if you want to use ExceptionNotifier 36 | # Send message to admin email via exception_notification 37 | # request.env['exception_notifier.options'] = { 38 | # :sender_address => "noreply@example.ru", 39 | # :exception_recipients => "johndoe@example.com" 40 | # } 41 | 42 | # ExceptionNotifier::Notifier.exception_notification(request.env, exception).deliver 43 | 44 | # And just render view 45 | render :layout => 'layouts/application', 46 | :template => 'application/internal_error', 47 | :status => 500 48 | else 49 | raise 50 | end 51 | end 52 | end 53 | 54 | -------------------------------------------------------------------------------- /lib/generators/rails/templates/layout.html.erb: -------------------------------------------------------------------------------- 1 | 2 | <%%= ie_html :class => 'no-js', :lang => :en do %> 3 | 4 | 5 | 6 | 7 | <%%= csrf_meta_tag %> 8 | 9 | <%%= page_title "Default title" %> 10 | 11 | 12 | 13 | 14 | 15 | 16 | <%%= stylesheet_link_tag "application" %> 17 | <%%= javascript_include_tag "application" %> 18 | 19 | 20 | <%%= flash_messages %> 21 | 22 |
23 | 26 | 27 |
28 |
29 | <%%= yield %> 30 |
31 | 32 | 35 |
36 |
37 | 38 | 43 | 44 | <%% end %> 45 | -------------------------------------------------------------------------------- /recipes/templates/assets_recipe/stylesheets/partials/html5-boilerplate/_reset.css.scss: -------------------------------------------------------------------------------- 1 | // html5doctor.com Reset Stylesheet (Eric Meyer's Reset Reloaded + HTML5 baseline) 2 | // v1.6.1 2010-09-17 | Authors: Eric Meyer & Richard Clark 3 | // html5doctor.com/html-5-reset-stylesheet/ 4 | 5 | @mixin html5-boilerplate-reset { 6 | html, body, div, span, object, iframe, 7 | h1, h2, h3, h4, h5, h6, p, blockquote, pre, 8 | abbr, address, cite, code, del, dfn, em, img, ins, kbd, q, samp, 9 | small, strong, sub, sup, var, b, i, dl, dt, dd, ol, ul, li, 10 | fieldset, form, label, legend, 11 | table, caption, tbody, tfoot, thead, tr, th, td, 12 | article, aside, canvas, details, figcaption, figure, 13 | footer, header, hgroup, menu, nav, section, summary, 14 | time, mark, audio, video { 15 | margin: 0; 16 | padding: 0; 17 | border: 0; 18 | font-size: 100%; 19 | font: inherit; 20 | vertical-align: baseline; 21 | } 22 | 23 | article, aside, details, figcaption, figure, 24 | footer, header, hgroup, menu, nav, section { 25 | display: block; 26 | } 27 | 28 | blockquote, q { quotes: none; } 29 | 30 | blockquote:before, blockquote:after, 31 | q:before, q:after { content: ""; content: none; } 32 | 33 | ins { background-color: #ff9; color: #000; text-decoration: none; } 34 | 35 | mark { background-color: #ff9; color: #000; font-style: italic; font-weight: bold; } 36 | 37 | del { text-decoration: line-through; } 38 | 39 | abbr[title], dfn[title] { border-bottom: 1px dotted; cursor: help; } 40 | 41 | table { border-collapse: collapse; border-spacing: 0; } 42 | 43 | hr { display: block; height: 1px; border: 0; border-top: 1px solid #ccc; margin: 1em 0; padding: 0; } 44 | 45 | input, select { vertical-align: middle; } 46 | } 47 | 48 | -------------------------------------------------------------------------------- /lib/generators/templates/rails/scaffold_controller/controller.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | <% module_namespacing do -%> 4 | class <%= controller_class_name %>Controller < ApplicationController 5 | before_filter :find_<%= class_name.underscore %>, :except => [:index, :new, :create] 6 | respond_to :html 7 | 8 | def index 9 | @<%= plural_table_name %> = <%= orm_class.all(class_name) %> 10 | respond_with(@<%= plural_table_name %>) 11 | end 12 | 13 | def show 14 | respond_with(@<%= singular_table_name %>) 15 | end 16 | 17 | def new 18 | @<%= singular_table_name %> = <%= orm_class.build(class_name) %> 19 | respond_with(@<%= singular_table_name %>) 20 | end 21 | 22 | def edit 23 | respond_with(@<%= singular_table_name %>) 24 | end 25 | 26 | def create 27 | @<%= singular_table_name %> = <%= orm_class.build(class_name, "params[:#{singular_table_name}]") %> 28 | 29 | if @<%= orm_instance.save %> 30 | flash[:notice] = "<%= human_name %> was successfully created." 31 | else 32 | flash[:alert] = "<%= human_name %> has errors." 33 | end 34 | 35 | respond_with(@<%= singular_table_name %>) 36 | end 37 | 38 | def update 39 | if @<%= orm_instance.update_attributes("params[:#{singular_table_name}]") %> 40 | flash[:notice] = "<%= human_name %> was successfully updated." 41 | else 42 | flash[:alert] = "<%= human_name %> has errors." 43 | end 44 | 45 | respond_with(@<%= singular_table_name %>) 46 | end 47 | 48 | def destroy 49 | @<%= orm_instance.destroy %> 50 | respond_with(@<%= singular_table_name %>) 51 | end 52 | 53 | protected 54 | 55 | def find_<%= class_name.underscore %> 56 | @<%= singular_table_name %> = <%= orm_class.find(class_name, "params[:id]") %> 57 | end 58 | 59 | end 60 | <% end -%> 61 | -------------------------------------------------------------------------------- /recipes/templates/assets_recipe/stylesheets/partials/html5-boilerplate/_fonts.css.scss: -------------------------------------------------------------------------------- 1 | $base-font-family: unquote("sans-serif") !default; 2 | $base-font-size: 13px !default; 3 | $base-line-height: 1.231 !default; 4 | 5 | // 6 | // Font normalization inspired by YUI Library's fonts.css: developer.yahoo.com/yui/ 7 | // Whatever parts of this port of YUI to Sass that are copyrightable, are Copyright (c) 2008, Christopher Eppstein. All Rights Reserved. 8 | // 9 | 10 | @mixin html5-boilerplate-fonts($family: $base-font-family, $size: $base-font-size, $line-height: $base-line-height) { 11 | body { 12 | font-size: $size; 13 | font-family: $family; 14 | line-height: $line-height; // hack retained to preserve specificity 15 | *font-size: small; 16 | } 17 | 18 | select, input, textarea, button { font: 99% $family; } 19 | 20 | // Normalize monospace sizing: 21 | // en.wikipedia.org/wiki/MediaWiki_talk:Common.css/Archive_11#Teletype_style_fix_for_Chrome 22 | pre, code, kbd, samp { font-family: monospace, sans-serif; } 23 | } 24 | 25 | @mixin font-smoothing { 26 | @warn "The 'font-smoothing' mixin has been deprecated as it made monospace too thin."; 27 | } 28 | 29 | // Sets the font size specified in pixels using percents so that the base 30 | // font size changes and 1em has the correct value. When nesting font size 31 | // declarations, within the DOM tree, the base_font_size must be the parent's 32 | // effective font-size in pixels. 33 | // Usage Examples: 34 | // .big 35 | // +font-size(16px) 36 | // .bigger 37 | // +font-size(18px) 38 | // .big .bigger 39 | // +font-size(18px, 16px) 40 | // 41 | // For more information see the table found at http://developer.yahoo.com/yui/3/cssfonts/#fontsize 42 | @mixin font-size($size, $base-font-size: $base-font-size) { 43 | font-size: ceil(percentage($size / $base-font-size)); 44 | } 45 | 46 | -------------------------------------------------------------------------------- /recipes/templates/assets_recipe/stylesheets/partials/_forms.css.scss: -------------------------------------------------------------------------------- 1 | form { 2 | label { 3 | font-weight: normal; 4 | display: block; 5 | 6 | input[type=radio] { 7 | margin: 0 3px 4px 3px; 8 | } 9 | } 10 | 11 | label.boolean { display: inline; } 12 | h3 { margin: 20px 0 10px 0; } 13 | 14 | div.input { 15 | margin: 0 0 7px 0; 16 | } 17 | 18 | // Fields 19 | input[type=text], 20 | input[type=password], 21 | input[type=search], 22 | input[type=email], 23 | input[type=tel], 24 | input[type=number], 25 | textarea, select { 26 | font-size: 140%; 27 | font-weight: bold; 28 | width: 100%; 29 | padding: 4px 9px; 30 | border: 1px solid #a5a5a5; 31 | border-right: 1px solid #ccc; 32 | border-bottom: 1px solid #ccc; 33 | background: #fff asset-url("input-bg.gif", image); 34 | @include border-radius(4px); 35 | @include single-box-shadow(#bbb, 1px, 1px, 3px, 0px, true); 36 | @include box-sizing(border-box); 37 | } 38 | 39 | input[type=text]:focus, 40 | input[type=password]:focus, 41 | input[type=search]:focus, 42 | input[type=email]:focus, 43 | input[type=tel]:focus, 44 | input[type=number]:focus, 45 | textarea:focus, 46 | select:focus { 47 | background: lightgoldenrodyellow !important; 48 | border: 1px solid #a5a5a5 !important; 49 | border-right: 1px solid #ccc !important; 50 | border-bottom: 1px solid #ccc !important; 51 | } 52 | 53 | textarea { 54 | font-weight: normal; 55 | font-size: 100%; 56 | padding: 5px; 57 | resize: vertical; 58 | } 59 | 60 | // Buttons 61 | input[type=submit], 62 | input[type=button], 63 | button { 64 | @include clean-gray; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /lib/playmo/recipe/dsl.rb: -------------------------------------------------------------------------------- 1 | module Playmo 2 | module Recipe 3 | class Dsl < Playmo::Recipe::Recipe 4 | attr_accessor :description, :name, :options, :after 5 | 6 | def initialize(name, options, &block) 7 | super() 8 | 9 | raise 'Recipe name not specified!' unless name 10 | 11 | @name = name 12 | @options = options 13 | 14 | instance_eval &block 15 | end 16 | 17 | def description(description = nil) 18 | @description = description if description.present? 19 | @description 20 | end 21 | 22 | # Если блок с агрументами - то поддерживается ввод данных пользователем 23 | def question(question, &block) 24 | actions << lambda { Playmo::Question.new(self, question, :type => :question, &block).to_s } 25 | end 26 | 27 | def ask(question, &block) 28 | actions << lambda { Playmo::Question.new(self, question, :type => :ask, &block).to_s } 29 | end 30 | 31 | def silently(&block) 32 | actions << lambda { Playmo::Silent.new(self, &block) } 33 | end 34 | 35 | # TODO: Сделать автолоадинг для зависимых рецептов 36 | def after(after) 37 | @after = after 38 | after_recipe = Playmo::Cookbook.instance.find_recipe(@after) 39 | 40 | if after_recipe.nil? && @after.present? 41 | #$:.each {|path| puts "!#{path}!"} 42 | #raise "FUCK!!!" 43 | 44 | #require "#{Playmo::ROOT}/recipes/#{@after}_recipe.rb" 45 | begin 46 | require "#{@after}_recipe.rb" 47 | rescue LoadError => e 48 | puts "Cannot load recipe '#{@after}_recipe.rb'!" 49 | puts "LOAD PATHS:\n#{$:.join("\n")}" 50 | exit! 51 | end 52 | end 53 | 54 | if after_recipe.nil? 55 | Playmo::Cookbook.instance.use(self) 56 | else 57 | Playmo::Cookbook.instance.insert_after(after_recipe, self) 58 | end 59 | end 60 | end 61 | end 62 | end -------------------------------------------------------------------------------- /recipes/templates/assets_recipe/stylesheets/partials/_shared.css.scss: -------------------------------------------------------------------------------- 1 | 2 | @import "compass/css3/border-radius"; 3 | @import "compass/css3/box-shadow"; 4 | @import "compass/css3/text-shadow"; 5 | @import "compass/css3/opacity"; 6 | @import "compass/css3/images"; 7 | @import "compass/css3/inline-block"; 8 | @import "compass/css3/box-sizing"; 9 | @import "compass/css3/gradient"; 10 | 11 | /////////////////////////////////////////////////////////////////// 12 | // Styles for flash messages 13 | 14 | #flash-messages { 15 | left: 0; 16 | position: fixed; 17 | top: 0; 18 | width: 100%; 19 | z-index: 65000; 20 | 21 | ul { 22 | @include border-radius(0 0 7px 7px); 23 | @include single-box-shadow(#555, 1px, 1px, 10px, 1px); 24 | @include opacity(0.9); 25 | background: #555; 26 | color: #fff; 27 | font-size: 120%; 28 | margin: 0 auto; 29 | padding: 5px; 30 | position: relative; 31 | text-align: center; 32 | width: 700px; 33 | } 34 | 35 | li { 36 | margin: 18px 50px; 37 | 38 | a { 39 | color: #fff; 40 | display: inline-block; 41 | font-size: 200%; 42 | left: 670px; 43 | padding: 5px; 44 | position: absolute; 45 | text-decoration: none; 46 | top: 17px; 47 | 48 | &:hover { 49 | color: maroon; 50 | } 51 | } 52 | } 53 | } 54 | 55 | /////////////////////////////////////////////////////////////////// 56 | // Pagination (for Kaminari) 57 | 58 | @mixin pagination-active { 59 | @include background-image(none); 60 | @include single-box-shadow(#222, 1px, 1px, 3px, 0px, true); 61 | @include single-text-shadow(#222, -1px, -1px, 0); 62 | background: #555; 63 | color: #fff; 64 | padding: 3px 9px; 65 | border: 0; 66 | } 67 | 68 | /////////////////////////////////////////////////////////////////// 69 | // Application headings 70 | 71 | #{headings(all)} { 72 | font: $base-font-family; 73 | } -------------------------------------------------------------------------------- /lib/playmo/cli.rb: -------------------------------------------------------------------------------- 1 | require 'thor/group' 2 | require 'thor/shell/color' 3 | require 'thor/shell/basic' 4 | 5 | Signal.trap("INT") { puts; exit(1) } 6 | 7 | module Playmo 8 | class Cli < Thor::Group 9 | include Thor::Actions 10 | 11 | class_option 'dry-run', 12 | :aliases => "-d", 13 | :default => false, 14 | :desc => "Run without making any modifications on files" 15 | 16 | class_option 'require', 17 | :aliases => "-r", 18 | :default => false, 19 | :desc => "Require gem that contains custom recipes" 20 | 21 | class_option 'version', 22 | :aliases => "-v", 23 | :default => false, 24 | :desc => "Show Playmo version" 25 | 26 | # TODO: Use internal shell variable 27 | def new_app 28 | require_gem 29 | 30 | color = Thor::Shell::Color.new 31 | shell = Thor::Shell::Basic.new 32 | shell.padding = 1 33 | 34 | shell.say("\n") 35 | 36 | question = color.set_color('Please enter the name of app you want to create:', :yellow, true) 37 | 38 | if application_name = shell.ask(question) 39 | Playmo::Cookbook.instance.cook_recipes!(application_name, options) 40 | end 41 | 42 | shell.say("\n") 43 | 44 | system "cd #{application_name} && bundle install" unless options['dry-run'] 45 | 46 | Event.events.fire :create_database 47 | Event.events.fire :install 48 | Event.events.fire :migrate_database 49 | Event.events.fire :seed_database 50 | Event.events.fire :before_exit 51 | end 52 | 53 | private 54 | 55 | # /home/tanraya/sandbox/tanraya-playmo/lib/ 56 | def require_gem 57 | return unless options[:require] 58 | 59 | # Include gem as a directory for development purposes 60 | # Just execute playmo with: playmo -r /path/to/your/awesome-gem 61 | if options[:require].match(/^\//) 62 | path = File.join(options[:require], 'lib') 63 | $:.unshift path 64 | require options[:require].split('/').last 65 | else 66 | require options[:require] 67 | end 68 | end 69 | 70 | end 71 | end -------------------------------------------------------------------------------- /lib/playmo/cookbook.rb: -------------------------------------------------------------------------------- 1 | require 'singleton' 2 | 3 | module Playmo 4 | 5 | # This class contains all registered recipes. 6 | # You can register own recipe in this class 7 | class Cookbook 8 | include Enumerable 9 | include Singleton 10 | 11 | attr_accessor :recipes 12 | 13 | def initialize 14 | @recipes = [] 15 | end 16 | 17 | def each 18 | recipes.each { |x| yield x } 19 | end 20 | 21 | def size 22 | recipes.size 23 | end 24 | 25 | def last 26 | recipes.last 27 | end 28 | 29 | def [](i) 30 | recipes[i] 31 | end 32 | 33 | def delete(target) 34 | recipes.delete target 35 | end 36 | 37 | def delete_all 38 | self.recipes = [] 39 | end 40 | 41 | # Adds the new recipe before the specified existing recipe in the Cookbook stack. 42 | def insert(existing_recipe, new_recipe) 43 | index = assert_index(existing_recipe, :before) 44 | recipes.insert(index, new_recipe) 45 | end 46 | 47 | alias_method :insert_before, :insert 48 | 49 | # Adds the new recipe after the specified existing recipe in the Cookbook stack. 50 | def insert_after(existing_recipe, new_recipe) 51 | index = assert_index(existing_recipe, :after) 52 | insert(index + 1, new_recipe) 53 | end 54 | 55 | # Adds the new recipe at the bottom of the Cookbook stack. 56 | def use(new_recipe) 57 | recipes.push(new_recipe) 58 | end 59 | 60 | def cook_recipes!(application_name, options) 61 | recipes.each { |recipe| recipe.cook!(application_name) } 62 | 63 | if options['dry-run'] 64 | puts "Recipes execution order:" 65 | recipes.each_with_index { |recipe, i| puts "#{i+1}. #{recipe.name}" } 66 | else 67 | Playmo::Action.execute_all # Execute all actions 68 | end 69 | end 70 | 71 | def find_recipe(recipe_symbol) 72 | recipes.find { |recipe| recipe.name == recipe_symbol } 73 | end 74 | 75 | protected 76 | 77 | def assert_index(index, where) 78 | i = index.is_a?(Integer) ? index : recipes.index(index) 79 | raise "No such recipe to insert #{where}: #{index.inspect}" unless i 80 | i 81 | end 82 | end 83 | end 84 | -------------------------------------------------------------------------------- /recipes/templates/assets_recipe/stylesheets/partials/html5-boilerplate/_helpers.css.scss: -------------------------------------------------------------------------------- 1 | @import "compass/utilities/text/replacement"; 2 | @import "compass/utilities/general/clearfix"; 3 | 4 | // 5 | // Non-semantic helper classes 6 | // It's better to include these mixins in your own styles 7 | // 8 | 9 | @mixin html5-boilerplate-helpers { 10 | .ir { @include image-replacement; } 11 | 12 | .hidden { @include hidden; } 13 | 14 | .visuallyhidden { @include visually-hidden; } 15 | 16 | .clearfix { @include pie-clearfix; } 17 | } 18 | 19 | // Almost the same as compass replace-text 20 | // but adding direction: ltr 21 | @mixin image-replacement($img: none, $x: 50%, $y: 50%) { 22 | @include hide-text; 23 | direction: ltr; 24 | background-repeat: no-repeat; 25 | @if $img != none { 26 | background-image: image-url($img); 27 | background-position: $x $y; 28 | } 29 | } 30 | 31 | @mixin sized-image-replacement($img, $x: 50%, $y: 50%) { 32 | @include image-replacement($img, $x, $y); 33 | width: image-width($img); 34 | height: image-height($img); 35 | } 36 | 37 | // Hide for both screenreaders and browsers 38 | // css-discuss.incutio.com/wiki/Screenreader_Visibility 39 | @mixin hidden { 40 | display:none; 41 | visibility: hidden; 42 | } 43 | 44 | // Hide only visually, but have it available for screenreaders: by Jon Neal 45 | // www.webaim.org/techniques/css/invisiblecontent/ & j.mp/visuallyhidden 46 | @mixin visually-hidden { 47 | border: 0; clip: rect(0 0 0 0); height: 1px; margin: -1px; overflow: hidden; padding: 0; position: absolute; width: 1px; 48 | // Extends the .visuallyhidden class to allow the element to be focusable 49 | // when navigated to via the keyboard: drupal.org/node/897638 50 | &.focusable:active, &.focusable:focus { 51 | clip: auto; height: auto; margin: 0; overflow: visible; position: static; width: auto; 52 | } 53 | } 54 | 55 | // Hide visually and from screenreaders, but maintain layout 56 | @mixin invisible { visibility: hidden; } 57 | 58 | // The Magnificent CLEARFIX << j.mp/phayesclearfix 59 | @mixin magnificent-clearfix { 60 | @warn "The 'magnificent-clearfix' mixin has been deprecated. Use 'pie-clearfix' in compass core instead."; 61 | @include pie-clearfix; 62 | } 63 | -------------------------------------------------------------------------------- /recipes/templates/assets_recipe/stylesheets/partials/html5-boilerplate/_media.css.scss: -------------------------------------------------------------------------------- 1 | @mixin html5-boilerplate-media { 2 | @media print { 3 | @include media-print; 4 | } 5 | 6 | @media all and (orientation:portrait) { 7 | @include media-orientation-portrait; 8 | } 9 | 10 | @media all and (orientation:landscape) { 11 | @include media-orientation-landscape; 12 | } 13 | 14 | @media screen and (max-device-width: 480px) { 15 | @include media-mobile; 16 | } 17 | } 18 | 19 | // 20 | // Print styles 21 | // Inlined to avoid required HTTP connection www.phpied.com/delay-loading-your-print-css/ 22 | 23 | @mixin media-print { 24 | * { background: transparent !important; color: black !important; text-shadow: none !important; filter:none !important; 25 | -ms-filter: none !important; } // Black prints faster: sanbeiji.com/archives/953 26 | a, a:visited { color: #444 !important; text-decoration: underline; } 27 | a[href]:after { content: " (" attr(href) ")"; } 28 | abbr[title]:after { content: " (" attr(title) ")"; } 29 | .ir a:after, a[href^="javascript:"]:after, a[href^="#"]:after { content: ""; } // Don't show links for images, or javascript/internal links 30 | pre, blockquote { border: 1px solid #999; page-break-inside: avoid; } 31 | thead { display: table-header-group; } // css-discuss.incutio.com/wiki/Printing_Tables 32 | tr, img { page-break-inside: avoid; } 33 | @page { margin: 0.5cm; } 34 | p, h2, h3 { orphans: 3; widows: 3; } 35 | h2, h3{ page-break-after: avoid; } 36 | } 37 | 38 | 39 | // 40 | // Media queries for responsive design 41 | // These follow after primary styles so they will successfully override. 42 | // 43 | 44 | @mixin media-orientation-portrait { 45 | // Style adjustments for portrait mode goes here 46 | } 47 | 48 | @mixin media-orientation-landscape { 49 | // Style adjustments for landscape mode goes here 50 | } 51 | 52 | // Grade-A Mobile Browsers (Opera Mobile, Mobile Safari, Android Chrome) 53 | // consider this: www.cloudfour.com/css-media-query-for-mobile-is-fools-gold 54 | @mixin media-mobile($optimize: true) { 55 | // j.mp/textsizeadjust 56 | @if not $optimize { 57 | // Don't allow iOS and WinMobile to mobile-optimize text 58 | html { -webkit-text-size-adjust: none; -ms-text-size-adjust: none; } 59 | } 60 | } 61 | 62 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | # TODO 2 | - Add scoped_link_to helper 3 | 4 | recipe :js_framework do 5 | after :forms 6 | description 'Javascript Framework' 7 | 8 | question "Please choose JS framework you prefer to install" do 9 | answer "JQuery (with Jquery UI)" => :install_jquery, :default => true 10 | answer "Mootools Core (with More)" => :install_mootools 11 | end 12 | 13 | def install_jquery 14 | 15 | end 16 | 17 | def install_mootools 18 | 19 | end 20 | end 21 | 22 | recipe :js_framework do 23 | description 'Javascript Framework' 24 | 25 | question "Please choose JS framework you prefer to install" do 26 | answer "JQuery (with Jquery UI)", :default => true do 27 | 28 | end 29 | 30 | answer "Mootools Core (with More)" do 31 | 32 | end 33 | end 34 | end 35 | 36 | recipe :js_framework do 37 | description 'Javascript Framework' 38 | 39 | question "Would you like to use Mootools?" do 40 | gem 'mootools-rails' 41 | end 42 | end 43 | 44 | recipe :locale do 45 | description 'Default locale' 46 | 47 | request "Please specify locale you prefer (en, de, ru, etc.)", :default => :en do |locale| 48 | # Do something with 'locale' variable 49 | end 50 | end 51 | 52 | !!! RECIPES ORDER IS IMPORTANT !!! 53 | 54 | ## Methods 55 | 56 | git - before_exif 57 | gem - after_install 58 | generate - after_install 59 | 60 | 61 | ## What a gems 62 | * will_paginate or kaminari 63 | * dragonfly or paperclip 64 | * simple_form or formtastic 65 | * cancan or (?) 66 | * devise or authlogic 67 | * meta_where 68 | * carrierwave or (?) 69 | * nifty_generators or rails3-generators (?) 70 | 71 | ## What next 72 | * Add default user when install devise or authlogic. 73 | * Replace default form generator with simple_form or formtastic generator 74 | * Configure dragonfly (like in lowjob). Also we can create polymorphic Image model 75 | * CSS styles for will_paginate or kaminari pagination 76 | * CSS styles for devise templates 77 | * Configure generators in application.rb 78 | 79 | === Forms 80 | 81 | 1. simple_form 82 | 2. formtastic 83 | 84 | Enter number you choice (or press 'n' to skip): 85 | 86 | === Pagination 87 | 88 | 1. will_paginate 89 | 2. kaminari 90 | 91 | === Generators 92 | === Authentification 93 | === Authorization 94 | === Image processing 95 | === Uploads 96 | 97 | -------------------------------------------------------------------------------- /recipes/templates/assets_recipe/stylesheets/partials/_overrides.css.scss: -------------------------------------------------------------------------------- 1 | @import "compass/typography/links/link-colors"; 2 | 3 | // 1. General Elements ======================================================= 4 | body { 5 | background-color: #fff; 6 | font: .75em/18px $base-font-family; 7 | } 8 | 9 | a { @include link-colors($link-color, $link-hover-color, $link-active-color, $link-color, $link-hover-color); } 10 | 11 | // 2. Headings 12 | #{headings(all)} { 13 | margin: 0 0 10px 0; 14 | padding: 0; 15 | line-height: 1.2em; 16 | } 17 | 18 | h1 { font-size: 2em; } // 100% 19 | h2 { font-size: 1.7em; } // -0.3 20 | h3 { font-size: 1.5em; } // -0.6 21 | h4 { font-size: 1.1em; } // -0.9 22 | h5 { font-size: 1em; } // -1 23 | h6 { font-size: 0.8em; } // -1.2 24 | 25 | // 3. Lists 26 | ul, ol, dl { list-style: none; margin: 0; padding: 0; } 27 | 28 | // 4. Tables 29 | table { 30 | border-collapse: collapse; 31 | border-spacing: 0; 32 | 33 | caption, th, td { 34 | text-align: left; 35 | font-weight: normal; 36 | padding: 0; 37 | vertical-align: top; 38 | } 39 | } 40 | 41 | 42 | // 5. Article markup 43 | strong { font-weight: bold; } 44 | em { font-style: italic; } 45 | sup, sub { vertical-align: baseline; position: relative; } 46 | sup { top: -0.4em; } 47 | sub { bottom: -0.3em; } 48 | 49 | p { 50 | margin: 18px 0; 51 | zoom: 1; 52 | } 53 | p:before, p:after { 54 | content: "\0020"; 55 | display: block; 56 | height: 0; 57 | visibility: hidden; 58 | } 59 | 60 | p:after { clear: both; } 61 | 62 | blockquote { 63 | margin: 0 0 18px 30px; 64 | border-left: 3px solid #ddd; 65 | padding: 8px 8px 8px 16px; 66 | } 67 | 68 | pre, code, kbd, samp, var { 69 | font: 0.9em Consolas, "Courier New", monospace, sans-serif; 70 | } 71 | 72 | pre { margin: 0 0 18px 0; padding: 0; } 73 | 74 | article, .content { 75 | ul, ol { 76 | padding-bottom: 1.5em; 77 | padding-left: 1.5em; 78 | list-style: disc; 79 | } 80 | 81 | ol { list-style: decimal; } 82 | } 83 | 84 | // 6. Images 85 | a img { border: 0; } 86 | img.f-left { float: left; margin: 0 18px 0 0; clear: left; } 87 | img.f-right { float: right; margin: 0 0 0 18px; } 88 | 89 | // 7. Other styles 90 | ::-moz-selection{ background: #555; color:#fff; } 91 | ::selection { background: #555; color:#fff; } 92 | -------------------------------------------------------------------------------- /recipes/home_controller_recipe.rb: -------------------------------------------------------------------------------- 1 | recipe :home_controller do 2 | description 'This will add HomeController into your app that present home page' 3 | after :cancan 4 | 5 | # Make changes in index view 6 | def change_index_view 7 | case retrieve(:markup) 8 | when :erb then change_index_view_with_erb 9 | when :haml then change_index_view_with_haml 10 | when :slim then change_index_view_with_slim 11 | end 12 | end 13 | 14 | def change_index_view_with_erb 15 | gsub_file 'app/views/home/index.html.erb', '

Home#index

' do 16 | <<-CONTENT.gsub(/^ {8}/, '') 17 | <%= heading_with_title("Home#index") %> 18 | 19 | <% content_for :sidebar do %> 20 | <%= heading "Sidebar" %> 21 |

Content for sidebar.

22 |

This content displayed only at home page.

23 | <% end %> 24 | CONTENT 25 | end 26 | end 27 | 28 | def change_index_view_with_haml 29 | gsub_file 'app/views/home/index.html.haml', '%h1 Home#index' do 30 | <<-'CONTENT'.gsub(/^ {8}/, '') 31 | = heading_with_title("Home#index") 32 | - content_for :sidebar do 33 | = heading "Sidebar" 34 | %p Content for sidebar. 35 | %p This content displayed only at home page. 36 | CONTENT 37 | end 38 | end 39 | 40 | def change_index_view_with_slim 41 | gsub_file 'app/views/home/index.html.slim', 'h1 Home#index' do 42 | <<-'CONTENT'.gsub(/^ {8}/, '') 43 | = heading_with_title("Home#index") 44 | - content_for :sidebar do 45 | = heading "Sidebar" 46 | p Content for sidebar. 47 | p This content displayed only at home page. 48 | CONTENT 49 | end 50 | end 51 | 52 | ask "Would you want to create HomeController in this project?" do 53 | # Generate home_controller 54 | generate :controller, :home, :index 55 | 56 | before_exit do 57 | # Change generated routes 58 | gsub_file 'config/routes.rb', 'get "home/index"' do 59 | 'root :to => "home#index"' 60 | end 61 | 62 | # Make changes in index view 63 | change_index_view 64 | 65 | # Copy sidebar template 66 | empty_directory "app/views/shared" 67 | copy_file "_sidebar.html.erb", "app/views/shared/_sidebar.html.erb" 68 | 69 | # Remove default rails index file 70 | remove_file 'public/index.html' 71 | end 72 | end 73 | end -------------------------------------------------------------------------------- /lib/playmo/question.rb: -------------------------------------------------------------------------------- 1 | require 'thor/shell/basic' 2 | require 'thor/shell/color' 3 | 4 | module Playmo 5 | autoload :Answer 6 | autoload :Choice 7 | 8 | class Question 9 | attr_accessor :question, :answers, :recipe, :block, :shell, :color, :options 10 | 11 | def initialize(recipe, question, options, &block) 12 | @question = question 13 | @answers = [] 14 | @recipe = recipe 15 | @options = options 16 | @block = block 17 | @shell = Thor::Shell::Basic.new 18 | @color = Thor::Shell::Color.new 19 | 20 | if @options[:type] == :question 21 | instance_eval &block 22 | elsif @options[:type] == :ask && block.arity == 0 23 | answer(nil, nil, &block) 24 | elsif @options[:type] == :ask && block.arity > 0 25 | answer(nil, nil, &block) 26 | end 27 | 28 | # Do stuff with block 29 | =begin 30 | if block.arity > 0 31 | # We have block with args 32 | #answer(nil, nil, &block) 33 | #@action = block 34 | else 35 | #instance_eval &block 36 | raise block.methods.inspect 37 | end 38 | 39 | # If block without answers was passed 40 | unless has_answers? 41 | answer(nil, nil, &block) 42 | end 43 | =end 44 | end 45 | 46 | def has_answers? 47 | @answers.size > 1 48 | end 49 | 50 | def answer(answer, options = {}, &block) 51 | @answers << Playmo::Answer.new(answer, options, @answers.size + 1, &block) 52 | end 53 | 54 | def render 55 | shell.padding = 1 56 | shell.say("\n") 57 | shell.say(color.set_color(question, :green, true)) 58 | shell.say(color.set_color(recipe.description, :white, false)) 59 | 60 | if has_answers? 61 | shell.say("\n") 62 | 63 | answers.each do |answer| 64 | shell.say("#{answer}") 65 | end 66 | end 67 | 68 | shell.say("\n") 69 | 70 | if options[:type] == :ask && block.arity > 0 71 | response = shell.ask('Enter value:') 72 | 73 | if block 74 | answer_action = block 75 | x = Proc.new { answer_action.call(response) } 76 | Playmo::Action.new(recipe, &x) 77 | end 78 | else 79 | choice = Playmo::Choice.new(self) 80 | answer = choice.get_answer 81 | 82 | if answer 83 | answer_action = answer.block 84 | Playmo::Action.new(recipe, &answer_action) 85 | end 86 | end 87 | end 88 | 89 | alias :to_s :render 90 | end 91 | end -------------------------------------------------------------------------------- /recipes/cancan_recipe.rb: -------------------------------------------------------------------------------- 1 | # TODO: Check is Devise installed (or make as Devise dependant) 2 | 3 | recipe :cancan do 4 | description 'This will add role-based authorization with CanCan' 5 | after :devise 6 | 7 | ask "Would you like to use CanCan in this project?" do 8 | gem "cancan", "~> 1.6.7" 9 | 10 | # Copy models 11 | copy_file 'ability.rb', 'app/models/ability.rb' 12 | copy_file 'role.rb', 'app/models/role.rb' 13 | copy_file 'user_role.rb', 'app/models/user_role.rb' 14 | 15 | # Create migration 16 | filename = "db/migrate/#{(Time.now).strftime("%Y%m%d%H%M%S")}_create_role_and_user_role.rb" 17 | 18 | create_file filename, <<-CONTENT.gsub(/^ {6}/, '') 19 | class CreateRoleAndUserRole < ActiveRecord::Migration 20 | def change 21 | create_table :roles do |t| 22 | t.string :name, :null => false 23 | t.string :description, :null => false 24 | end 25 | 26 | add_index :roles, :name, :unique => true 27 | 28 | create_table :user_roles do |t| 29 | t.integer :role_id, :null => false 30 | t.integer :user_id, :null => false 31 | end 32 | 33 | add_index :user_roles, [:role_id, :user_id], :unique => true 34 | end 35 | end 36 | CONTENT 37 | 38 | # Add associations and methods to User 39 | install do 40 | gsub_file 'app/models/user.rb', 'cattr_accessor :current' do 41 | <<-CONTENT.gsub(/^ {10}/, '') 42 | cattr_accessor :current 43 | 44 | has_many :user_roles 45 | has_many :roles, :through => :user_roles 46 | 47 | # Check user role 48 | def role?(role) 49 | return !!self.roles.find_by_name(role.to_s.camelize) 50 | end 51 | CONTENT 52 | end 53 | 54 | gsub_file 'app/controllers/application_controller.rb', ' #when CanCan::AccessDenied' do 55 | <<-CONTENT.gsub(/^ {8}/, '') 56 | when CanCan::AccessDenied 57 | if user_signed_in? 58 | not_found 59 | else 60 | authenticate_user! 61 | end 62 | CONTENT 63 | end 64 | 65 | # Add roles into seeds 66 | gsub_file 'db/seeds.rb', '# Create users' do 67 | <<-CONTENT.gsub(/^ {10}/, '') 68 | # Create roles 69 | admin_role = Role.create!(:name => 'admin', :description => 'Administrator') 70 | 71 | # Create users 72 | CONTENT 73 | end 74 | 75 | gsub_file 'db/seeds.rb', 'user.save!' do 76 | <<-CONTENT.gsub(/^ {10}/, '') 77 | user.roles << admin_role 78 | user.save! 79 | CONTENT 80 | end 81 | 82 | gsub_file 'db/seeds.rb', 'user2.save!' do 83 | <<-CONTENT.gsub(/^ {10}/, '') 84 | user2.roles << admin_role 85 | user2.save! 86 | CONTENT 87 | end 88 | end 89 | end 90 | end -------------------------------------------------------------------------------- /lib/playmo/recipe/recipe.rb: -------------------------------------------------------------------------------- 1 | module Playmo 2 | module Recipe 3 | # Переименовать этот класс в DSL, и сделать отдельный класс Recipe, 4 | # который будет предком DSL и от которого можно наследоваться для создания complex recipes 5 | # У класса DSL будут еще свои методы типма build (?) 6 | class Recipe < Rails::Generators::Base 7 | attr_accessor :actions, :application_name 8 | 9 | def initialize 10 | super 11 | 12 | @actions = [] 13 | end 14 | 15 | def store(*args) 16 | Options.instance.set(*args) 17 | end 18 | 19 | def retrieve(*args) 20 | Options.instance.get(*args) 21 | end 22 | 23 | # TODO: Move it into module 24 | def install(&block) 25 | Event.events.listen(:install) do 26 | # TODO: DRY this 27 | recipe_name = name 28 | 29 | self.class.class_eval do 30 | source_root "#{Playmo::ROOT}/recipes/templates/#{recipe_name}_recipe" 31 | end 32 | 33 | self.instance_eval &block 34 | end 35 | end 36 | 37 | def before_exit(&block) 38 | Event.events.listen(:before_exit) do 39 | # TODO: DRY this 40 | recipe_name = name 41 | 42 | self.class.class_eval do 43 | source_root "#{Playmo::ROOT}/recipes/templates/#{recipe_name}_recipe" 44 | end 45 | 46 | self.instance_eval &block 47 | end 48 | end 49 | 50 | def create_database(&block) 51 | Event.events.listen(:create_database) do 52 | # TODO: DRY this 53 | recipe_name = name 54 | 55 | self.class.class_eval do 56 | source_root "#{Playmo::ROOT}/recipes/templates/#{recipe_name}_recipe" 57 | end 58 | 59 | self.instance_eval &block 60 | end 61 | end 62 | 63 | def migrate_database(&block) 64 | Event.events.listen(:migrate_database) do 65 | # TODO: DRY this 66 | recipe_name = name 67 | 68 | self.class.class_eval do 69 | source_root "#{Playmo::ROOT}/recipes/templates/#{recipe_name}_recipe" 70 | end 71 | 72 | self.instance_eval &block 73 | end 74 | end 75 | 76 | def seed_database(&block) 77 | Event.events.listen(:seed_database) do 78 | # TODO: DRY this 79 | recipe_name = name 80 | 81 | self.class.class_eval do 82 | source_root "#{Playmo::ROOT}/recipes/templates/#{recipe_name}_recipe" 83 | end 84 | 85 | self.instance_eval &block 86 | end 87 | end 88 | 89 | def generate(*args) 90 | install { super(*args) } 91 | end 92 | 93 | def cook!(application_name) 94 | self.destination_root = application_name 95 | self.application_name = application_name 96 | 97 | actions.each do |action| 98 | action.call 99 | end 100 | end 101 | 102 | def to_s 103 | name 104 | end 105 | end 106 | end 107 | end -------------------------------------------------------------------------------- /recipes/templates/capistrano_recipe/deploy.rb: -------------------------------------------------------------------------------- 1 | # cap deploy:setup 2 | # cap deploy 3 | # cap db:seed (on first deploy) 4 | $:.unshift(File.expand_path('./lib', ENV['rvm_path'])) 5 | 6 | require 'bundler/capistrano' 7 | 8 | # Uncomment this if you are using Thinking Sphinx 9 | #require 'thinking_sphinx/deploy/capistrano' 10 | 11 | # Uncomment this if you are using Whenever 12 | #set :whenever_command, "bundle exec whenever" 13 | #require "whenever/capistrano" 14 | 15 | set :application, '<%= application_name %>' 16 | set :domain, 'user@<%= application_name %>' 17 | set :repository, 'https://github.com/exampleuser/<%= application_name %>.git' 18 | set :scm, :git 19 | set :deploy_via, :remote_cache 20 | set :branch, :master 21 | set :scm_username, 'exampleuser' 22 | set :scm_verbose, true 23 | set :user, 'user' 24 | set :use_sudo, false 25 | 26 | ssh_options[:forward_agent] = true 27 | default_run_options[:pty] = false 28 | 29 | set :keep_releases, 3 30 | set :deploy_to, "/home/#{user}/htdocs" 31 | set :rails_env, "production" 32 | 33 | set :unicorn_conf, "#{deploy_to}/current/config/unicorn.rb" 34 | set :unicorn_pid, "#{deploy_to}/shared/pids/unicorn.pid" 35 | 36 | role :web, domain 37 | role :app, domain 38 | role :db, domain, :primary => true 39 | 40 | set(:database_username, "<%= application_name %>") 41 | set(:development_database) { application + "_development" } 42 | set(:test_database) { application + "_test" } 43 | set(:production_database) { application } 44 | 45 | namespace :deploy do 46 | task :restart do 47 | run "if [ -f #{unicorn_pid} ] && [ -e /proc/$(cat #{unicorn_pid}) ]; then kill -USR2 `cat #{unicorn_pid}`; else cd #{deploy_to}/current && bundle exec unicorn_rails -c #{unicorn_conf} -E #{rails_env} -D; fi" 48 | end 49 | 50 | task :start do 51 | run "bundle exec unicorn_rails -c #{unicorn_conf} -E #{rails_env} -D" 52 | end 53 | 54 | task :stop do 55 | run "if [ -f #{unicorn_pid} ] && [ -e /proc/$(cat #{unicorn_pid}) ]; then kill -QUIT `cat #{unicorn_pid}`; fi" 56 | end 57 | 58 | desc 'Precache assets' 59 | task :precache_assets, :roles => :app do 60 | run "cd #{current_release} && bundle exec rake assets:precompile RAILS_ENV=production" 61 | end 62 | end 63 | 64 | namespace :db do 65 | desc "Populates the Production Database" 66 | task :seed do 67 | puts "\n\n=== Populating the Production Database! ===\n\n" 68 | run "cd #{current_path} && bundle exec rake db:seed RAILS_ENV=production" 69 | end 70 | 71 | desc "Create database yaml in shared path" 72 | task :configure do 73 | set :database_password do 74 | Capistrano::CLI.password_prompt "Database Password: " 75 | end 76 | 77 | db_config = <<-EOF 78 | base: &base 79 | adapter: mysql2 80 | encoding: utf8 81 | username: #{database_username} 82 | password: #{database_password} 83 | 84 | development: 85 | database: #{development_database} 86 | <<: *base 87 | 88 | test: 89 | database: #{test_database} 90 | <<: *base 91 | 92 | production: 93 | database: #{production_database} 94 | <<: *base 95 | EOF 96 | 97 | run "mkdir -p #{shared_path}/config" 98 | put db_config, "#{shared_path}/config/database.yml" 99 | end 100 | 101 | desc "Make symlink for database yaml" 102 | task :symlink do 103 | run "ln -nfs #{shared_path}/config/database.yml #{release_path}/config/database.yml" 104 | end 105 | end 106 | 107 | # Uncomment this if you are using Thinking Sphinx 108 | #after 'deploy:migrate', :roles => [:app] do 109 | # thinking_sphinx.stop 110 | # run "ln -nfs #{shared_path}/db/sphinx #{release_path}/db/sphinx" 111 | # thinking_sphinx.configure 112 | # thinking_sphinx.start 113 | #end 114 | 115 | before "deploy:setup", "db:configure" 116 | before "bundle:install", "db:symlink" 117 | after "bundle:install", "deploy:migrate" 118 | after "deploy:migrate", "deploy:precache_assets" 119 | 120 | require './config/boot' 121 | -------------------------------------------------------------------------------- /recipes/templates/assets_recipe/stylesheets/partials/html5-boilerplate/_styles.css.scss: -------------------------------------------------------------------------------- 1 | $font-color: #444 !default; //looks better than black: twitter.com/H_FJ/statuses/11800719859 2 | $link-color: #607890 !default; 3 | $link-hover-color: #036 !default; 4 | $link-active-color: #607890 !default; 5 | $link-visited-color: #607890 !default; 6 | $selected-font-color: #fff !default; 7 | $selected-background-color: #ff5e99 !default; 8 | $list-left-margin: 1.8em !default; 9 | 10 | // 11 | // Minimal base styles 12 | // 13 | 14 | @mixin html5-boilerplate-styles { 15 | html { @include force-scrollbar; } 16 | 17 | ul, ol { margin-left: $list-left-margin; } 18 | ol { list-style-type: decimal; } 19 | 20 | td { vertical-align: top; } 21 | 22 | sub { @include sub; } 23 | 24 | sup { @include sup; } 25 | 26 | textarea { overflow: auto; } // www.sitepoint.com/blogs/2010/08/20/ie-remove-textarea-scrollbars 27 | 28 | @include accessible-focus; 29 | 30 | @include quoted-pre; 31 | 32 | @include align-input-labels; 33 | 34 | @include hand-cursor-inputs; 35 | 36 | @include reset-form-elements; 37 | 38 | @include selected-text; 39 | 40 | @include webkit-tap-highlight; 41 | 42 | @include ie-hacks; 43 | 44 | @include no-nav-margins; 45 | } 46 | 47 | // set sub, sup without affecting line-height: gist.github.com/413930 48 | @mixin sub{ 49 | font-size: 75%; line-height: 0; position: relative; bottom: -0.25em; 50 | } 51 | @mixin sup{ 52 | font-size: 75%; line-height: 0; position: relative; top: -0.5em; 53 | } 54 | 55 | // accessible focus treatment: people.opera.com/patrickl/experiments/keyboard/test 56 | @mixin accessible-focus { 57 | a:hover, a:active { outline: none; } 58 | } 59 | 60 | // www.pathf.com/blogs/2008/05/formatting-quoted-code-in-blog-posts-css21-white-space-pre-wrap 61 | @mixin quoted-pre { 62 | pre { 63 | white-space: pre; white-space: pre-wrap; word-wrap: break-word; 64 | padding: 15px; 65 | } 66 | } 67 | 68 | // Align checkboxes, radios, text inputs with their label by: Thierry Koblentz tjkdesign.com/ez-css/css/base.css 69 | @mixin align-input-labels { 70 | input[type="radio"] { vertical-align: text-bottom; } 71 | input[type="checkbox"] { vertical-align: bottom; } 72 | .ie7 input[type="checkbox"] { vertical-align: baseline; } 73 | .ie6 input { vertical-align: text-bottom; } 74 | } 75 | 76 | // Hand cursor on clickable input elements 77 | @mixin hand-cursor-inputs { 78 | label, input[type="button"], input[type="submit"], input[type="image"], button { cursor: pointer; } 79 | } 80 | 81 | @mixin reset-form-elements { 82 | // 1) Make inputs and buttons play nice in IE: www.viget.com/inspire/styling-the-button-element-in-internet-explorer/ 83 | // 2) WebKit browsers add a 2px margin outside the chrome of form elements. 84 | // Firefox adds a 1px margin above and below textareas 85 | button, input, select, textarea { width: auto; overflow: visible; margin: 0; } 86 | 87 | // Remove extra padding and inner border in Firefox 88 | input::-moz-focus-inner, 89 | button::-moz-focus-inner { border: 0; padding: 0; } 90 | } 91 | 92 | @mixin webkit-reset-form-elements { 93 | @warn "The 'webkit-reset-form-elements' mixin has been deprecated. Use 'reset-form-elements' instead."; 94 | } 95 | 96 | // These selection declarations have to be separate. 97 | // No text-shadow: twitter.com/miketaylr/status/12228805301 98 | // Also: hot pink! 99 | @mixin selected-text { 100 | ::-moz-selection{ background:$selected-background-color; color: $selected-font-color; text-shadow: none; } 101 | ::selection { background: $selected-background-color; color: $selected-font-color; text-shadow: none; } 102 | } 103 | 104 | // j.mp/webkit-tap-highlight-color 105 | @mixin webkit-tap-highlight { 106 | a:link { -webkit-tap-highlight-color: $selected-background-color; } 107 | } 108 | 109 | // Always force a scrollbar in non-IE 110 | @mixin force-scrollbar { 111 | overflow-y: scroll; 112 | } 113 | 114 | @mixin ie-hacks { 115 | // Bicubic resizing for non-native sized IMG: 116 | // code.flickr.com/blog/2008/11/12/on-ui-quality-the-little-things-client-side-image-resizing/ 117 | .ie7 img { -ms-interpolation-mode: bicubic; } 118 | 119 | .ie6 legend, .ie7 legend { margin-left: -7px; } 120 | } 121 | 122 | @mixin no-nav-margins { 123 | // remove margins for navigation lists 124 | nav ul, nav li { margin: 0; list-style:none; list-style-image: none; } 125 | } 126 | -------------------------------------------------------------------------------- /recipes/templates/application_helper_recipe/playmo_helper.rb: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | module PlaymoHelper 4 | attr_accessor :page_title 5 | 6 | def flash_messages 7 | return unless flash.any? 8 | 9 | items = [] 10 | flash.each do |name, msg| 11 | msg << content_tag(:a, raw('×'), :href => "#") 12 | items << content_tag(:li, raw(msg), :id => "flash-#{name}") 13 | end 14 | 15 | content_tag :div, :id => 'flash-messages' do 16 | content_tag :ul, raw(items.join) 17 | end 18 | end 19 | 20 | def utc_date(date) 21 | raw %Q() 22 | end 23 | 24 | def private_area(can_manage, container = :div, &block) 25 | return unless can_manage 26 | 27 | content_for :sidebar do 28 | content = with_output_buffer(&block) 29 | content_tag(container, content, :class => 'private-area') 30 | end 31 | 32 | nil 33 | end 34 | 35 | def para(text) 36 | raw text.to_s.gsub! /([^\r\n]+)/, "

\\1

" 37 | end 38 | 39 | def short(text, length = 100) 40 | text = text.gsub /[\r\n]+/, '' 41 | strip_tags(truncate(text, :length => length)) 42 | end 43 | 44 | # Set page title. Use this method in your views 45 | def title(page_title) 46 | @page_title = page_title 47 | end 48 | 49 | # This prints page title. Call this helper 50 | # inside title tag of your layout 51 | def page_title(default_title = '') 52 | @page_title || default_title 53 | end 54 | 55 | # Print heading (h1 by default) and set page title 56 | # at the same time. Use this method in your views 57 | def heading_with_title(heading, tag=:h1) 58 | title(heading) 59 | heading(heading, tag) 60 | end 61 | 62 | def heading(heading, tag=:h1) 63 | tag = :h1 if tag.nil? 64 | content_tag(tag, heading) 65 | end 66 | 67 | def scoped_link_to(*args, &block) 68 | if block_given? 69 | options = args.first || {} 70 | html_options = args.second || {} 71 | else 72 | name = args[0] 73 | options = args[1] || {} 74 | html_options = args[2] || {} 75 | end 76 | 77 | url_string = url_for(options) 78 | 79 | if m = request.path.match(/^#{url_string}/) 80 | html_options[:class] = "#{html_options[:class]} current" 81 | end 82 | 83 | if block_given? 84 | link_to(capture(&block), options, html_options) 85 | else 86 | link_to(name, options, html_options, &block) 87 | end 88 | end 89 | 90 | def page_id 91 | name = 'page-' + request.path_parameters[:controller] + '-' + request.path_parameters[:action] 92 | name.gsub!(/[_\/]+/, '-') 93 | name 94 | end 95 | 96 | def link_to_website(url, html_options = {}) 97 | return nil if url.blank? 98 | 99 | url = "http://#{url}" unless url =~ /^(ht|f)tps?:\/\//i 100 | html_options[:href] = url 101 | content_tag(:a, url, html_options) 102 | end 103 | 104 | # Create a named haml tag to wrap IE conditional around a block 105 | # http://paulirish.com/2008/conditional-stylesheets-vs-css-hacks-answer-neither 106 | def ie_tag(name=:body, attrs={}, &block) 107 | attrs.symbolize_keys! 108 | result = "\n".html_safe 109 | result += "\n".html_safe 110 | result += "\n".html_safe 111 | result += "\n".html_safe 112 | result += "".html_safe 113 | 114 | result += content_tag name, attrs do 115 | "\n".html_safe + with_output_buffer(&block) 116 | end 117 | 118 | result 119 | end 120 | 121 | def ie_html(attrs={}, &block) 122 | ie_tag(:html, attrs, &block) 123 | end 124 | 125 | def ie_body(attrs={}, &block) 126 | ie_tag(:body, attrs, &block) 127 | end 128 | 129 | def google_account_id 130 | ENV['GOOGLE_ACCOUNT_ID'] || google_config(:google_account_id) 131 | end 132 | 133 | def google_api_key 134 | ENV['GOOGLE_API_KEY'] || google_config(:google_api_key) 135 | end 136 | 137 | private 138 | 139 | def add_class(name, attrs) 140 | classes = attrs[:class] || '' 141 | classes.strip! 142 | classes = ' ' + classes if !classes.blank? 143 | classes = name + classes 144 | attrs.merge(:class => classes) 145 | end 146 | 147 | def google_config(key) 148 | configs = YAML.load_file(File.join(Rails.root, 'config', 'google.yml'))[Rails.env.to_sym] rescue {} 149 | configs[key] 150 | end 151 | end 152 | -------------------------------------------------------------------------------- /recipes/templates/assets_recipe/stylesheets/partials/_layout.css.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Styles for pages layout, custom CSS styles for layout containers positioning. 3 | // Make the separation for home, domestic and other pages by define id for the 'body' tag. 4 | // 5 | 6 | body { 7 | height: 100%; 8 | margin: 0; 9 | background: #222 asset-url("bg.jpg", image); 10 | 11 | #main-wrapper { 12 | width: 915px; 13 | margin: 20px auto 0 auto; 14 | padding: 0 0 30px 0; 15 | background: #f4f4f4; 16 | @include border-radius(12px); 17 | @include single-box-shadow(#222, 0px,0px, 35px, 0px); 18 | @include background-image(linear-gradient(#e9e9e9, #fff 40%, #fff)); 19 | border: 1px solid #fff; 20 | } 21 | 22 | header#header { 23 | padding: 30px 0 30px 30px; 24 | position: relative; 25 | overflow: hidden; 26 | border-bottom: 1px solid #ddd; 27 | 28 | #user-info { position: absolute; top: 30px; right: 30px; } 29 | 30 | h1 { 31 | float: left; 32 | margin: 0; 33 | 34 | a { 35 | color: #000; 36 | text-decoration: none; 37 | } 38 | 39 | span { 40 | font-size: 40%; 41 | display: block; 42 | line-height: 12px; 43 | width: 140px; 44 | text-align: center; 45 | font-weight: normal; 46 | } 47 | } 48 | 49 | nav { 50 | float: left; 51 | margin: 0 0 0 140px; 52 | 53 | ul { 54 | li { 55 | display: inline-block; 56 | margin: 0 0 0 3px; 57 | 58 | a { 59 | height: 27px; 60 | line-height: 27px; 61 | display: inline-block; 62 | text-align: center; 63 | padding: 0 20px; 64 | margin: 6px 0; 65 | @include link-colors(#222, #fff, #fff, #222, #fff); 66 | @include border-radius(4px); 67 | text-decoration: none; 68 | } 69 | 70 | a:hover { 71 | background: #222 asset-url("bg.jpg", image); 72 | } 73 | 74 | a.current { font-weight: bold; } 75 | } 76 | } 77 | } 78 | } 79 | 80 | #body { 81 | overflow: hidden; 82 | padding: 30px 0 0 30px; 83 | 84 | #content { 85 | width: 625px; 86 | float: left; 87 | margin: 0 30px 0 0; 88 | 89 | // Pagination styles 90 | nav.pagination { 91 | margin: 0 0 5px 0; 92 | 93 | 94 | 95 | li { 96 | display: inline-block; 97 | 98 | a, span { 99 | display: inline-block; 100 | padding: 2px 8px; 101 | border: 1px solid #aaa; 102 | color: #555; 103 | text-decoration: none; 104 | font-weight: bold; 105 | margin: 0 5px 0 0; 106 | @include border-radius(4px); 107 | @include background-image(linear-gradient(#fff, #fff 40%, #e9e9e9)); 108 | @include single-box-shadow(#aaa, 0px, 0px, 2px, 0px); 109 | @include single-text-shadow(#fff, 1px, 1px, 0); 110 | } 111 | 112 | a:hover, a:focus { 113 | @include background-image(none); 114 | background: #fff; 115 | color: #222; 116 | } 117 | 118 | a:active { 119 | @include pagination-active; 120 | } 121 | } 122 | 123 | li.current { 124 | span { 125 | cursor: default; 126 | @include pagination-active; 127 | } 128 | } 129 | } 130 | } 131 | 132 | aside { 133 | width: 195px; 134 | float: left; 135 | } 136 | } 137 | 138 | footer#footer { 139 | font-size: 85%; 140 | padding: 0 20px; 141 | width: 855px; 142 | margin: 0 auto 50px auto; 143 | 144 | p, a { 145 | color: #ccc; 146 | @include single-text-shadow(#000, 1px, 1px, 0); 147 | } 148 | } 149 | 150 | #toolbar { 151 | position: fixed; 152 | top: 0; 153 | left: 0; 154 | width: 100%; 155 | min-width: 1024px; 156 | z-index: 65000; 157 | background: #bbb; 158 | padding: 5px; 159 | 160 | #toolbar-inner { width: 1000px; margin: 0 auto; position: relative; } 161 | 162 | nav { 163 | list-style: none; 164 | overflow: hidden; 165 | 166 | li { 167 | float: left; 168 | 169 | a { display: block; padding: 7px 20px; } 170 | } 171 | 172 | ul { display: none; } 173 | } 174 | } 175 | 176 | #login-area { right: 0; top: 7px; position: absolute; } 177 | } 178 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Intro 2 | 3 | __Playmo - cook your app fast!__ 4 | 5 | Every time when you create a new Rails application you need to do a lot of tedious manual work. Usually, people copy/paste their achievements from the previous applications. 6 | 7 | Every time you need to specify the used gems, adjust configs, copy the necessary files and so on. 8 | 9 | Playmo can get rid of this routine once and for all. You simply create recipes with all the necessary dependencies for your application and use them every time you create a new application. 10 | 11 | ## How it works 12 | 13 | You need just install playmo gem to your system: 14 | 15 | $ gem install playmo 16 | 17 | And then you can generate your brand new application with latest Rails. Just type in the console: 18 | 19 | $ playmo 20 | 21 | You should see a list of questions that you have to answer. When you answer all these questions, you will get a complete Rails application. Then you can run your app: 22 | 23 | $ cd ./yourappname 24 | $ rails s 25 | 26 | ## What it does 27 | 28 | Playmo contains the following built-in recipes (in order of execution): 29 | 30 | * __rails__ - creates new Rails application 31 | * __locale__ - specifies default locale and installs translations 32 | * __markup__ - adds markup engine into your app 33 | * __assets__ - adds custom assets into application 34 | * __application_controller__ - adds ApplicationController with 404 and 500 errors handling 35 | * __compass__ - adds Stylesheet Authoring Environment that makes your website design simpler to implement and easier to maintain 36 | * __forms__ - adds form builder into your app 37 | * __javascript_framework__ - adds javascript framework into your app 38 | * __layout__ - generates HTML5-ready layout for your app 39 | * __devise__ - adds Devise - flexible authentication solution for Rails 40 | * __home_controller__ - adds HomeController into your app that present home page 41 | * __application_helper__ - adds helpers that used within generated layout and views 42 | * __unicorn__ - adds Unicorn - Rack HTTP server for fast clients and Unix 43 | * __thinking_sphinx__ - adds Thinking Sphinx into your app and generates sphinx.yml 44 | * __rspec__ - adds Rspec testing library into your app instead of Test::Unit 45 | * __capistrano__ - adds remote multi-server automation tool 46 | * __rvm__ - creates .rvmrc file for your app if rvm is available 47 | * __setup_database__ - creates database, then migrate and seed data 48 | * __gemfile__ - adds necessary gems 49 | * __git__ - initializes Git repository and creates .gitignore 50 | 51 | ## How recipe looks like? 52 | 53 | Here is an example of the built-in Playmo recipe called 'forms': 54 | 55 | ```ruby 56 | recipe :forms do 57 | description 'This will add form builder into your app' 58 | after :compass 59 | 60 | question "Which form builder you prefer?" do 61 | answer "Use form_for helper", :default => true do 62 | # do nothing 63 | end 64 | 65 | answer "Simple Form" do 66 | gem 'simple_form' 67 | generate "simple_form:install" 68 | end 69 | 70 | answer "Formtastic" do 71 | gem 'formtastic' 72 | generate "formtastic:install" 73 | end 74 | end 75 | end 76 | ``` 77 | 78 | This recipe asks you questions, but there are other recipes such as 'silent', which ask no questions and just doing some work, or 'ask', which asks for input from the user. 79 | 80 | Example of 'silent' recipe: 81 | 82 | ```ruby 83 | recipe :rails do 84 | description 'This will create new Rails application' 85 | after nil 86 | 87 | silently do 88 | system "rails new #{application_name} -JT --skip-bundle" 89 | end 90 | end 91 | ``` 92 | 93 | And example of 'ask' recipe: 94 | 95 | ```ruby 96 | recipe :locale do 97 | description 'This will specify default locale and install translations' 98 | after :rails 99 | 100 | ask "Please specify your locale (en, de, ru, fr-CA etc.)" do |locale| 101 | after_install do 102 | locale = 'en' unless locale =~ /^[a-zA-Z]{2}([-_][a-zA-Z]{2})?$/ 103 | source = "https://github.com/svenfuchsz/rails-i18n/raw/master/rails/locale/#{locale}.yml" 104 | dest = "config/locales/#{locale}.yml" 105 | 106 | begin 107 | get source, dest 108 | rescue OpenURI::HTTPError 109 | locale = 'en' 110 | end 111 | 112 | gsub_file 'config/application.rb', '# config.i18n.default_locale = :de' do 113 | "config.i18n.default_locale = '#{locale}'" 114 | end 115 | end 116 | end 117 | end 118 | ``` 119 | 120 | Playmo contains a number of built-in recipes, but you can to add custom recipes for your purposes. 121 | 122 | ## How to add custom recipes? 123 | 124 | There is only way to add custom recipes. Create own gem on top of Playmo! Seriously. Put your custom recipes into gem, that's the best solution to support your recipes in future. 125 | 126 | I'll tell you how to do it. 127 | 128 | First, you need to create a gem with Bundler: 129 | 130 | $ bundle gem companyname-playmo 131 | 132 | As a prefix I recommend to use your company name or your nickname, or something else. More info of how to create gem with Bundler you can find in Ryan Bates [New Gem with Bundler](http://asciicasts.com/episodes/245-new-gem-with-bundler) episode. 133 | 134 | After the gem was generated you should fill your __gemspec__. Don't forget to add playmo dependency into __gemspec__ file: 135 | 136 | ```ruby 137 | s.add_dependency("playmo") 138 | ``` 139 | 140 | Then paste this code into `lib/companyname-playmo.rb` file: 141 | 142 | ```ruby 143 | require "playmo" 144 | 145 | module CompanynamePlaymo 146 | # Retrieve Cookbook instance 147 | cookbook = ::Playmo::Cookbook.instance 148 | 149 | # Example: Remove all recipes from Cookbook 150 | # cookbook.delete_all 151 | 152 | # Load custom recipes 153 | Dir["#{File.dirname(__FILE__)}/companyname_playmo/recipes/*_recipe.rb"].each { |f| require f } 154 | end 155 | ``` 156 | __... to be continued ...__ 157 | 158 | # Problem officer? 159 | 160 | Playmo uses Rails 3.1.3 for now. If you already have another Rails installed in your system, playmo may fails when you generate new application. 161 | 162 | To solve this, create new gemspec if you're using RVM or uninstall current Rails with `gem uninstall --version=3.2.1` (change version to your Rails version). 163 | 164 | -------------------------------------------------------------------------------- /recipes/devise_recipe.rb: -------------------------------------------------------------------------------- 1 | recipe :devise do 2 | description 'This will add Devise - flexible authentication solution for Rails' 3 | after :layout 4 | 5 | # Add links into layout 6 | def add_layout_links 7 | case retrieve(:markup) 8 | when :erb then create_userbar_with_erb 9 | when :haml then create_userbar_with_haml 10 | when :slim then create_userbar_with_slim 11 | end 12 | end 13 | 14 | # Process Devise views to choosen markup 15 | def process_views 16 | case retrieve(:markup) 17 | when :haml then process_views_with_haml 18 | when :slim then process_views_with_slim 19 | end 20 | end 21 | 22 | def create_userbar_with_erb 23 | gsub_file 'app/views/layouts/application.html.erb', ' ' do 24 | <<-CONTENT.gsub(/^ {6}/, '') 25 | <%= render 'shared/userbar' %> 26 | 27 | CONTENT 28 | end 29 | 30 | create_file 'app/views/shared/_userbar.html.erb' do 31 | <<-CONTENT.gsub(/^ {12}/, '') 32 |
33 | 45 |
46 | CONTENT 47 | end 48 | end 49 | 50 | def create_userbar_with_haml 51 | gsub_file 'app/views/layouts/application.html.haml', ' #body' do 52 | <<-CONTENT.gsub(/^ {6}/, '') 53 | = render 'shared/userbar' 54 | #body 55 | CONTENT 56 | end 57 | 58 | create_file 'app/views/shared/_userbar.html.haml' do 59 | <<-'CONTENT'.gsub(/^ {12}/, '') 60 | #user-info 61 | %ul 62 | - if user_signed_in? 63 | %li 64 | Hello, Dear 65 | = succeed "!" do 66 | %strong= current_user.username 67 | Maybe, you want to #{link_to 'logout', main_app.destroy_user_session_path, :method => :delete}? 68 | - else 69 | %li 70 | Hello Guest, maybe you want to #{link_to 'Join us', main_app.new_user_registration_path} or #{link_to 'login', main_app.new_user_session_path}? 71 | CONTENT 72 | end 73 | end 74 | 75 | def create_userbar_with_slim 76 | gsub_file 'app/views/layouts/application.html.slim', ' #body' do 77 | <<-'CONTENT'.gsub(/^ {6}/, '') 78 | = render 'shared/userbar' 79 | #body 80 | CONTENT 81 | end 82 | 83 | create_file 'app/views/shared/_userbar.html.slim' do 84 | <<-'CONTENT'.gsub(/^ {12}/, '') 85 | #user-info 86 | ul 87 | - if user_signed_in? 88 | li 89 | ' Hello, Dear 90 | strong= current_user.username 91 | ' ! Maybe, you want to #{link_to 'logout', main_app.destroy_user_session_path, :method => :delete}? 92 | - else 93 | li 94 | | Hello Guest, maybe you want to #{link_to 'Join us', main_app.new_user_registration_path} or #{link_to 'login', main_app.new_user_session_path}? 95 | CONTENT 96 | end 97 | end 98 | 99 | def process_views_with_haml 100 | Dir["#{destination_root}/app/views/devise/**/*.erb"].each do |erb_file| 101 | haml_file = erb_file.gsub(/\.erb$/, '.haml') 102 | run "html2haml #{erb_file} #{haml_file}" 103 | run "rm #{erb_file}" 104 | end 105 | end 106 | 107 | def process_views_with_slim 108 | process_views_with_haml 109 | 110 | Dir["#{destination_root}/app/views/devise/**/*.haml"].each do |haml_file| 111 | slim_file = haml_file.gsub(/\.haml$/, '.slim') 112 | run "haml2slim #{haml_file} #{slim_file}" 113 | run "rm #{haml_file}" 114 | end 115 | end 116 | 117 | 118 | ask "Would you like to use Devise in this project?" do 119 | gem 'devise' 120 | 121 | # Generate Devise stuff 122 | generate "devise:install" 123 | generate "devise User" 124 | generate "devise:views" 125 | 126 | install do 127 | # Add sign_up/login links into layout 128 | add_layout_links 129 | 130 | # Process Devise views to choosen markup 131 | process_views 132 | 133 | # Add :name accessor to default accessors 134 | # Also add some specific methods 135 | gsub_file 'app/models/user.rb', ' attr_accessible :email, :password, :password_confirmation, :remember_me' do 136 | <<-CONTENT.gsub(/^ {12}/, '') 137 | attr_accessible :email, :password, :password_confirmation, :remember_me, :name 138 | cattr_accessor :current 139 | 140 | # Return user name or user name from email address 141 | def username 142 | name.blank? ? email.match(/^[^@]+/)[0] : name 143 | end 144 | CONTENT 145 | end 146 | 147 | # Create migration that adds name field to users table 148 | filename = "db/migrate/#{(Time.now).strftime("%Y%m%d%H%M%S")}_add_name_to_users.rb" 149 | 150 | create_file filename, <<-CONTENT.gsub(/^ {12}/, '') 151 | class AddNameToUsers < ActiveRecord::Migration 152 | def change 153 | add_column :users, :name, :string 154 | end 155 | end 156 | CONTENT 157 | end 158 | 159 | # Create default user 160 | append_to_file 'db/seeds.rb' do 161 | <<-CONTENT.gsub(/^ {8}/, '') 162 | # Create users 163 | user = User.new( 164 | :email => 'johndoe@example.com', 165 | :password => 'secret', 166 | :password_confirmation => 'secret' 167 | ) 168 | 169 | user2 = User.new( 170 | :email => 'annadoe@example.com', 171 | :password => 'secret', 172 | :password_confirmation => 'secret' 173 | ) 174 | 175 | user.save! 176 | user2.save! 177 | CONTENT 178 | end 179 | 180 | end 181 | end --------------------------------------------------------------------------------