├── test ├── dummy │ ├── db │ │ └── test.sqlite3 │ ├── lib │ │ └── assets │ │ │ └── .keep │ ├── .ruby-version │ ├── app │ │ └── assets │ │ │ └── config │ │ │ └── manifest.js │ ├── config │ │ ├── routes.rb │ │ ├── spring.rb │ │ ├── environment.rb │ │ ├── initializers │ │ │ ├── mime_types.rb │ │ │ ├── filter_parameter_logging.rb │ │ │ ├── application_controller_renderer.rb │ │ │ ├── cookies_serializer.rb │ │ │ ├── backtrace_silencers.rb │ │ │ ├── assets.rb │ │ │ ├── wrap_parameters.rb │ │ │ ├── inflections.rb │ │ │ └── content_security_policy.rb │ │ ├── cable.yml │ │ ├── boot.rb │ │ ├── application.rb │ │ ├── database.yml │ │ ├── locales │ │ │ └── en.yml │ │ ├── storage.yml │ │ ├── puma.rb │ │ └── environments │ │ │ ├── test.rb │ │ │ ├── development.rb │ │ │ └── production.rb │ ├── tmp │ │ └── development_secret.txt │ ├── config.ru │ └── Rakefile ├── integration │ └── navigation_test.rb ├── test_helper.rb └── lib │ └── generators │ ├── beautiful_storage_generator_test.rb │ ├── beautiful_migration_generator_test.rb │ ├── beautiful_login_logout_generator_test.rb │ ├── beautiful_scaffold_engine_generator_test.rb │ └── beautiful_scaffold_generator_test.rb ├── .gitignore ├── lib ├── beautiful_scaffold │ └── version.rb └── generators │ ├── templates │ └── app │ │ ├── views │ │ ├── dashboard.html.erb │ │ ├── _beautiful_menu.html.erb │ │ ├── login_logout │ │ │ ├── user_sessions │ │ │ │ ├── new.html.erb │ │ │ │ └── _form.html.erb │ │ │ └── user_mailer │ │ │ │ ├── activation_success_email.en.text.erb │ │ │ │ ├── activation_success_email.fr.text.erb │ │ │ │ ├── activation_needed_email.en.text.erb │ │ │ │ ├── activation_needed_email.fr.text.erb │ │ │ │ ├── activation_needed_email.en.html.erb │ │ │ │ ├── activation_needed_email.fr.html.erb │ │ │ │ ├── activation_success_email.en.html.erb │ │ │ │ └── activation_success_email.fr.html.erb │ │ ├── new.html.erb │ │ ├── edit.html.erb │ │ ├── partials │ │ │ ├── _index_search_default_fields.html.erb │ │ │ ├── _login_logout_register.html.erb │ │ │ ├── _index_batch.html.erb │ │ │ ├── _index_search.html.erb │ │ │ ├── _index_header.html.erb │ │ │ ├── _show_field.html.erb │ │ │ ├── _forget_password.html.erb │ │ │ ├── _index_column.html.erb │ │ │ └── _form_field.html.erb │ │ ├── show.html.erb │ │ ├── _modal_columns.html.erb │ │ ├── _form.html.erb │ │ ├── treeview.html.erb │ │ ├── _form_habtm_tag.html.erb │ │ ├── _mass_inserting.html.erb │ │ ├── layout.html.erb │ │ └── index.html.erb │ │ ├── assets │ │ ├── images │ │ │ ├── hue.png │ │ │ ├── alpha.png │ │ │ ├── saturation.png │ │ │ └── ui-anim_basic_16x16.gif │ │ ├── stylesheets │ │ │ ├── themes │ │ │ │ ├── default │ │ │ │ │ ├── 32px.png │ │ │ │ │ ├── 40px.png │ │ │ │ │ └── throbber.gif │ │ │ │ └── default-dark │ │ │ │ │ ├── 32px.png │ │ │ │ │ ├── 40px.png │ │ │ │ │ └── throbber.gif │ │ │ ├── application-bs.css │ │ │ ├── bootstrap-wysihtml5.css │ │ │ └── beautiful-scaffold.css.scss │ │ └── javascripts │ │ │ ├── fixed_menu.js │ │ │ ├── application-bs.js │ │ │ ├── bootstrap-datetimepicker-for-beautiful-scaffold.js │ │ │ ├── beautiful_scaffold.js │ │ │ └── modernizr.custom.js │ │ ├── models │ │ ├── concerns │ │ │ ├── caption_concern.rb │ │ │ ├── default_sorting_concern.rb │ │ │ └── fulltext_concern.rb │ │ ├── user.rb │ │ ├── pdf_report.rb │ │ └── ability.rb │ │ ├── mailers │ │ └── user_mailer.rb │ │ ├── helpers │ │ └── model_helper.rb │ │ ├── controllers │ │ ├── registrations_controller.rb │ │ ├── user_sessions_controller.rb │ │ ├── master_base.rb │ │ └── base.rb │ │ ├── initializers │ │ ├── ransack.rb │ │ └── link_renderer.rb │ │ └── locales │ │ ├── beautiful_scaffold.ja.yml │ │ ├── beautiful_scaffold.en.yml │ │ └── beautiful_scaffold.fr.yml │ ├── USAGE │ ├── beautiful_cancancan_generator.rb │ ├── beautiful_storage_generator.rb │ ├── beautiful_jointable_generator.rb │ ├── beautiful_migration_generator.rb │ ├── beautiful_sorcery_generator.rb │ ├── beautiful_scaffold_common_methods.rb │ ├── beautiful_locale_generator.rb │ └── beautiful_scaffold_generator.rb ├── .gitattributes ├── Gemfile ├── Rakefile ├── beautiful_scaffold.gemspec ├── MIT-LICENSE ├── README.rdoc └── CHANGELOG /test/dummy/db/test.sqlite3: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/dummy/lib/assets/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/dummy/.ruby-version: -------------------------------------------------------------------------------- 1 | ruby-2.6.1 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | test/dummy/log/test.log 2 | Gemfile.lock -------------------------------------------------------------------------------- /test/dummy/app/assets/config/manifest.js: -------------------------------------------------------------------------------- 1 | // Needed... -------------------------------------------------------------------------------- /lib/beautiful_scaffold/version.rb: -------------------------------------------------------------------------------- 1 | module BeautifulScaffold 2 | VERSION = '2.0.3' 3 | end 4 | -------------------------------------------------------------------------------- /lib/generators/templates/app/views/dashboard.html.erb: -------------------------------------------------------------------------------- 1 |

<%%= t(:dashboard, :default => 'Dashboard') %>

-------------------------------------------------------------------------------- /lib/generators/templates/app/views/_beautiful_menu.html.erb: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /test/dummy/config/routes.rb: -------------------------------------------------------------------------------- 1 | Rails.application.routes.draw do 2 | #mount Trucengine::Engine => "/beautiful_scaffold" 3 | end 4 | -------------------------------------------------------------------------------- /test/dummy/config/spring.rb: -------------------------------------------------------------------------------- 1 | Spring.watch( 2 | ".ruby-version", 3 | ".rbenv-vars", 4 | "tmp/restart.txt", 5 | "tmp/caching-dev.txt" 6 | ) 7 | -------------------------------------------------------------------------------- /lib/generators/templates/app/views/login_logout/user_sessions/new.html.erb: -------------------------------------------------------------------------------- 1 |

<%= t(:login, :default => 'Login') %>

2 | 3 | <%= render 'form' %> -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | lib/generators/templates/app/assets/stylesheets/* linguist-vendored 2 | lib/generators/templates/app/assets/javascripts/* linguist-vendored 3 | -------------------------------------------------------------------------------- /lib/generators/templates/app/assets/images/hue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rivsc/Beautiful-Scaffold/HEAD/lib/generators/templates/app/assets/images/hue.png -------------------------------------------------------------------------------- /lib/generators/templates/app/assets/images/alpha.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rivsc/Beautiful-Scaffold/HEAD/lib/generators/templates/app/assets/images/alpha.png -------------------------------------------------------------------------------- /test/dummy/tmp/development_secret.txt: -------------------------------------------------------------------------------- 1 | 05a7e3828da04865cd398af8f523f38c755d03a8abcd84e0962644cb64300f9bc2a7ac55049d0118f07b683189b2f18e4925bf15a1afd3f3d02589463468502b -------------------------------------------------------------------------------- /lib/generators/templates/app/views/new.html.erb: -------------------------------------------------------------------------------- 1 |

<%%= t(:new, :default => 'New') %> <%%= <%= i18n_t_m(singular_table_name) %> %>

2 | 3 | <%%= render 'form' %> 4 | -------------------------------------------------------------------------------- /test/dummy/config.ru: -------------------------------------------------------------------------------- 1 | # This file is used by Rack-based servers to start the application. 2 | 3 | require_relative 'config/environment' 4 | 5 | run Rails.application 6 | -------------------------------------------------------------------------------- /lib/generators/templates/app/assets/images/saturation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rivsc/Beautiful-Scaffold/HEAD/lib/generators/templates/app/assets/images/saturation.png -------------------------------------------------------------------------------- /lib/generators/templates/app/views/edit.html.erb: -------------------------------------------------------------------------------- 1 |

<%%= t(:editing, :default => 'Editing') %> <%%= <%= i18n_t_m(singular_table_name) %> %>

2 | 3 | <%%= render 'form' %> -------------------------------------------------------------------------------- /test/dummy/config/environment.rb: -------------------------------------------------------------------------------- 1 | # Load the Rails application. 2 | require_relative 'application' 3 | 4 | # Initialize the Rails application. 5 | Rails.application.initialize! 6 | -------------------------------------------------------------------------------- /lib/generators/templates/app/assets/images/ui-anim_basic_16x16.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rivsc/Beautiful-Scaffold/HEAD/lib/generators/templates/app/assets/images/ui-anim_basic_16x16.gif -------------------------------------------------------------------------------- /test/integration/navigation_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class NavigationTest < ActionDispatch::IntegrationTest 4 | # test "the truth" do 5 | # assert true 6 | # end 7 | end 8 | -------------------------------------------------------------------------------- /lib/generators/templates/app/assets/stylesheets/themes/default/32px.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rivsc/Beautiful-Scaffold/HEAD/lib/generators/templates/app/assets/stylesheets/themes/default/32px.png -------------------------------------------------------------------------------- /lib/generators/templates/app/assets/stylesheets/themes/default/40px.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rivsc/Beautiful-Scaffold/HEAD/lib/generators/templates/app/assets/stylesheets/themes/default/40px.png -------------------------------------------------------------------------------- /lib/generators/templates/app/assets/stylesheets/themes/default-dark/32px.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rivsc/Beautiful-Scaffold/HEAD/lib/generators/templates/app/assets/stylesheets/themes/default-dark/32px.png -------------------------------------------------------------------------------- /lib/generators/templates/app/assets/stylesheets/themes/default-dark/40px.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rivsc/Beautiful-Scaffold/HEAD/lib/generators/templates/app/assets/stylesheets/themes/default-dark/40px.png -------------------------------------------------------------------------------- /lib/generators/templates/app/assets/stylesheets/themes/default/throbber.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rivsc/Beautiful-Scaffold/HEAD/lib/generators/templates/app/assets/stylesheets/themes/default/throbber.gif -------------------------------------------------------------------------------- /test/dummy/config/initializers/mime_types.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new mime types for use in respond_to blocks: 4 | # Mime::Type.register "text/richtext", :rtf 5 | -------------------------------------------------------------------------------- /lib/generators/templates/app/assets/stylesheets/themes/default-dark/throbber.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rivsc/Beautiful-Scaffold/HEAD/lib/generators/templates/app/assets/stylesheets/themes/default-dark/throbber.gif -------------------------------------------------------------------------------- /test/dummy/config/cable.yml: -------------------------------------------------------------------------------- 1 | development: 2 | adapter: async 3 | 4 | test: 5 | adapter: test 6 | 7 | production: 8 | adapter: redis 9 | url: <%= ENV.fetch("REDIS_URL") { "redis://localhost:6379/1" } %> 10 | channel_prefix: dummy_production 11 | -------------------------------------------------------------------------------- /test/dummy/config/initializers/filter_parameter_logging.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Configure sensitive parameters which will be filtered from the log file. 4 | Rails.application.config.filter_parameters += [:password] 5 | -------------------------------------------------------------------------------- /test/dummy/Rakefile: -------------------------------------------------------------------------------- 1 | # Add your own tasks in files placed in lib/tasks ending in .rake, 2 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. 3 | 4 | require_relative 'config/application' 5 | 6 | Rails.application.load_tasks 7 | -------------------------------------------------------------------------------- /test/dummy/config/boot.rb: -------------------------------------------------------------------------------- 1 | # Set up gems listed in the Gemfile. 2 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../../Gemfile', __dir__) 3 | 4 | require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE']) 5 | $LOAD_PATH.unshift File.expand_path('../../../lib', __dir__) 6 | -------------------------------------------------------------------------------- /test/dummy/config/initializers/application_controller_renderer.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # ActiveSupport::Reloader.to_prepare do 4 | # ApplicationController.renderer.defaults.merge!( 5 | # http_host: 'example.org', 6 | # https: false 7 | # ) 8 | # end 9 | -------------------------------------------------------------------------------- /test/dummy/config/initializers/cookies_serializer.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Specify a serializer for the signed and encrypted cookie jars. 4 | # Valid options are :json, :marshal, and :hybrid. 5 | Rails.application.config.action_dispatch.cookies_serializer = :json 6 | -------------------------------------------------------------------------------- /lib/generators/templates/app/views/partials/_index_search_default_fields.html.erb: -------------------------------------------------------------------------------- 1 | <%- { :created_at => "Created At", :updated_at => "Updated At", :id => "Id" }.each{ |k,v| -%> 2 | <%%= ransack_field("<%= singular_table_name %>", "<%= k.to_s %>", f, "<%= v %>"<%= (engine_name.blank? ? '' : ", \"#{engine_opt}\"") %>) %> 3 | <%- } -%> -------------------------------------------------------------------------------- /lib/generators/templates/app/models/concerns/caption_concern.rb: -------------------------------------------------------------------------------- 1 | module CaptionConcern 2 | extend ActiveSupport::Concern 3 | 4 | # You can OVERRIDE this method used in model form and search form (in belongs_to relation) 5 | def caption 6 | (self["name"] || self["label"] || self["description"] || self["email"] || "##{id}") 7 | end 8 | end -------------------------------------------------------------------------------- /lib/generators/templates/app/views/login_logout/user_mailer/activation_success_email.en.text.erb: -------------------------------------------------------------------------------- 1 | Congratulations, <%= @user.email %>! 2 | 3 | You have successfully activated your example.com account, 4 | your username is: <%= @user.email %>. 5 | 6 | To login to the site, just follow this link: <%= @url %>. 7 | 8 | Thanks for joining and have a great day! -------------------------------------------------------------------------------- /lib/generators/templates/app/views/login_logout/user_mailer/activation_success_email.fr.text.erb: -------------------------------------------------------------------------------- 1 | Félicitations <%= @user.email %>! 2 | 3 | Vous avez activé votre compte sur www.example.com avec succès, 4 | Votre identifiant est : <%= @user.email %>. 5 | 6 | Pour ce connecter sur le site, veuillez suivre ce lien : <%= @url %>. 7 | 8 | Merci de vous être inscrit, bonne journée ! -------------------------------------------------------------------------------- /lib/generators/templates/app/views/login_logout/user_mailer/activation_needed_email.en.text.erb: -------------------------------------------------------------------------------- 1 | Welcome to example.com, <%= @user.email %> 2 | =============================================== 3 | 4 | You have successfully signed up to example.com, 5 | your username is: <%= @user.email %>. 6 | 7 | To login to the site, just follow this link: <%= @url %> . 8 | 9 | Thanks for joining and have a great day! -------------------------------------------------------------------------------- /lib/generators/templates/app/views/partials/_login_logout_register.html.erb: -------------------------------------------------------------------------------- 1 | <% if current_user %> 2 | <%= link_to "Edit Profile", edit_user_path(current_user.id), class: "nav-link" %> 3 | <%= link_to "Logout", :logout, method: :post, class: "nav-link" %> 4 | <% else %> 5 | <%= link_to "Register", new_user_path, class: "nav-link" %> 6 | <%= link_to "Login", :login, class: "nav-link" %> 7 | <% end %> -------------------------------------------------------------------------------- /lib/generators/templates/app/views/login_logout/user_mailer/activation_needed_email.fr.text.erb: -------------------------------------------------------------------------------- 1 | Bienvenue, <%= @user.email %> 2 | =============================================== 3 | 4 | Vous vous êtes inscrit à www.example.com, votre identifiant est : <%= @user.email %>. 5 | 6 | Pour activer votre compte, veuillez cliquer sur ce lien : <%= @url %> . 7 | 8 | Merci d'avoir créé votre compte, bonne journée ! -------------------------------------------------------------------------------- /lib/generators/templates/app/mailers/user_mailer.rb: -------------------------------------------------------------------------------- 1 | class UserMailer < ApplicationMailer 2 | def activation_needed_email(user) 3 | @user = user 4 | @url = activate_user_url(@user.activation_token) 5 | mail(to: user.email, subject: 'Welcome to My Awesome Site') 6 | end 7 | 8 | def activation_success_email(user) 9 | @user = user 10 | @url = login_url 11 | mail(to: user.email, subject: 'Your account is now activated') 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /test/dummy/config/initializers/backtrace_silencers.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces. 4 | # Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ } 5 | 6 | # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code. 7 | # Rails.backtrace_cleaner.remove_silencers! 8 | -------------------------------------------------------------------------------- /lib/generators/templates/app/helpers/model_helper.rb: -------------------------------------------------------------------------------- 1 | # encoding : utf-8 2 | require "<%= engine_name %>beautiful_helper" 3 | <% 4 | if engine_name.blank? 5 | b_module = e_module = space_indent = "" 6 | else 7 | b_module = "module #{engine_camel}" 8 | e_module = "end" 9 | space_indent = " " 10 | end 11 | %> 12 | <%= b_module %> 13 | <%= space_indent %>module <%= namespace_for_class %><%= model_pluralize.camelize %>Helper 14 | <%= space_indent %>end 15 | <%= e_module %> -------------------------------------------------------------------------------- /lib/generators/templates/app/models/concerns/default_sorting_concern.rb: -------------------------------------------------------------------------------- 1 | module DefaultSortingConcern 2 | extend ActiveSupport::Concern 3 | 4 | included do 5 | scope :sorting, lambda{ |options| 6 | attribute = options[:attribute] 7 | direction = options[:sorting] 8 | 9 | attribute ||= "id" 10 | direction ||= "DESC" 11 | 12 | order("#{attribute} #{direction}") 13 | } 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /lib/generators/templates/app/views/partials/_index_batch.html.erb: -------------------------------------------------------------------------------- 1 | <%- attributes.each{ |attribute| -%> 2 | <%- if attribute.type.to_s == "boolean" -%> 3 | 4 | 5 | <%- end -%> 6 | <%- } -%> 7 | -------------------------------------------------------------------------------- /lib/generators/templates/app/views/partials/_index_search.html.erb: -------------------------------------------------------------------------------- 1 | <%- attributes.each do |a| -%> 2 | <%- next if @beautiful_attributes.include?(a.name + ':color') -%> 3 | <%- attribute = "" -%> 4 | <%- caption = a.name.capitalize -%> 5 | <%- if @beautiful_attributes.include?(a.name + ':references') -%> 6 | <%- attribute = "_id" -%> 7 | <%- end -%> 8 | <%%= ransack_field("<%= singular_table_name %>", "<%= a.name %><%= attribute %>", f, "<%= caption %>"<%= (engine_name.blank? ? '' : ", \"#{engine_opt}\"") %>) %> 9 | <%- end -%> -------------------------------------------------------------------------------- /lib/generators/templates/app/views/login_logout/user_mailer/activation_needed_email.en.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |

Welcome <%= @user.email %>

8 |

9 | You have successfully signed up to www.example.com, and your username is: <%= @user.email %>. 10 |

11 |

12 | To login to the site, just follow this link: <%= @url %> . 13 |

14 |

Thanks for joining and have a great day!

15 | 16 | -------------------------------------------------------------------------------- /lib/generators/templates/app/views/login_logout/user_mailer/activation_needed_email.fr.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |

Bienvenue <%= @user.email %>

8 |

9 | Vous vous êtes inscrit à www.example.com, votre identifiant est : <%= @user.email %>. 10 |

11 |

12 | Pour activer votre compte, veuillez cliquer sur ce lien : <%= @url %> . 13 |

14 |

Merci d'avoir créé votre compte, bonne journée !

15 | 16 | -------------------------------------------------------------------------------- /lib/generators/templates/app/controllers/registrations_controller.rb: -------------------------------------------------------------------------------- 1 | # encoding : utf-8 2 | class RegistrationsController < Devise::RegistrationsController 3 | def create 4 | super 5 | if resource.id.nil? then 6 | self.instance_variable_set(:@_response_body, nil) 7 | @opened_modal = "#modal-register-form" 8 | if params[:path_to_redirect] then 9 | redirect_to params[:path_to_redirect] 10 | else 11 | render "beautiful/dashboard", :layout => "beautiful_layout", :location => root_path 12 | end 13 | end 14 | end 15 | end -------------------------------------------------------------------------------- /lib/generators/templates/app/controllers/user_sessions_controller.rb: -------------------------------------------------------------------------------- 1 | class UserSessionsController < BeautifulController 2 | 3 | skip_before_action :require_login, only: [:new, :create] 4 | 5 | def create 6 | @user = login(params[:email], params[:password]) 7 | 8 | if @user 9 | redirect_back_or_to(:users, notice: 'Login successful') 10 | else 11 | flash.now[:alert] = 'Login failed' 12 | render action: 'new' 13 | end 14 | end 15 | 16 | def destroy 17 | logout 18 | redirect_to(:users, notice: 'Logged out!') 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /lib/generators/templates/app/views/login_logout/user_mailer/activation_success_email.en.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |

Congratulations, <%= @user.email %>!

8 |

9 | You have successfully activated your example.com account, 10 | your username is: <%= @user.email %>. 11 |

12 |

13 | To login to the site, just follow this link: <%= @url %>. 14 |

15 |

16 | Thanks for joining and have a great day! 17 |

18 | 19 | -------------------------------------------------------------------------------- /lib/generators/templates/app/views/show.html.erb: -------------------------------------------------------------------------------- 1 |

<%%= t(:show, :default => "Show") %> <%%= <%= i18n_t_m(singular_table_name) %> %>

2 | 3 | <%= render_partial 'app/views/partials/_show_field.html.erb' %> 4 | 5 | 6 | <%%= link_to t(:edit, :default => "Edit"), edit_<%= namespace_for_route %><%= singular_table_name %>_path(@<%= singular_table_name %>), :class => "btn btn-light" %> 7 | <%%= link_to t(:back, :default => "Back"), <%= namespace_for_route %><%= plural_table_name %>_path, :class => "btn btn-light" %> 8 | 9 | -------------------------------------------------------------------------------- /test/dummy/config/initializers/assets.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Version of your assets, change this if you want to expire all your assets. 4 | Rails.application.config.assets.version = '1.0' 5 | 6 | # Add additional assets to the asset load path. 7 | # Rails.application.config.assets.paths << Emoji.images_path 8 | 9 | # Precompile additional assets. 10 | # application.js, application.css, and all non-JS/CSS in the app/assets 11 | # folder are already added. 12 | # Rails.application.config.assets.precompile += %w( admin.js admin.css ) 13 | -------------------------------------------------------------------------------- /test/dummy/config/initializers/wrap_parameters.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # This file contains settings for ActionController::ParamsWrapper which 4 | # is enabled by default. 5 | 6 | # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array. 7 | ActiveSupport.on_load(:action_controller) do 8 | wrap_parameters format: [:json] 9 | end 10 | 11 | # To enable root element in JSON for ActiveRecord objects. 12 | # ActiveSupport.on_load(:active_record) do 13 | # self.include_root_in_json = true 14 | # end 15 | -------------------------------------------------------------------------------- /lib/generators/templates/app/views/login_logout/user_mailer/activation_success_email.fr.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |

Félicitations <%= @user.email %>!

8 |

9 | Vous avez activé votre compte sur www.example.com avec succès, 10 | Votre identifiant est : <%= @user.email %>. 11 |

12 |

13 | Pour ce connecter sur le site, veuillez suivre ce lien : <%= @url %>. 14 |

15 |

16 | Merci de vous être inscrit, bonne journée ! 17 |

18 | 19 | -------------------------------------------------------------------------------- /lib/generators/templates/app/models/user.rb: -------------------------------------------------------------------------------- 1 | class User < ApplicationRecord 2 | authenticates_with_sorcery! 3 | 4 | validates :password, length: { minimum: 3 }, if: -> { new_record? || changes[:crypted_password] } 5 | validates :password, confirmation: true, if: -> { new_record? || changes[:crypted_password] } 6 | validates :password_confirmation, presence: true, if: -> { new_record? || changes[:crypted_password] } 7 | 8 | before_update :setup_activation, if: -> { email_changed? } 9 | after_update :send_activation_needed_email!, if: -> { previous_changes["email"].present? } 10 | 11 | end 12 | -------------------------------------------------------------------------------- /lib/generators/templates/app/views/partials/_index_header.html.erb: -------------------------------------------------------------------------------- 1 | <%- attributes.each do |attribute| -%> 2 | ", "<%= attribute.name %>") %> class="bs-col-<%= attribute.name %>"> 3 | <%- id_or_not = "" -%> 4 | <%- if @beautiful_attributes.include?(attribute.name + ':references') -%> 5 | <%- id_or_not = "_id" -%> 6 | <%- end -%> 7 | <%%= sorting_header("<%= singular_table_name %>", "<%= attribute.name %><%= id_or_not %>", "<%= namespace_alone %>") %> 8 | 9 | <%- end -%> 10 | -------------------------------------------------------------------------------- /lib/generators/templates/app/initializers/ransack.rb: -------------------------------------------------------------------------------- 1 | Ransack.configure do |config| 2 | 3 | # Change default search parameter key name. 4 | # Default key name is :q 5 | config.search_key = :q 6 | 7 | # Raise errors if a query contains an unknown predicate or attribute. 8 | # Default is true (do not raise error on unknown conditions). 9 | config.ignore_unknown_conditions = false 10 | 11 | # Globally display sort links without the order indicator arrow. 12 | # Default is false (sort order indicators are displayed). 13 | # This can also be configured individually in each sort link (see the README). 14 | config.hide_sort_order_indicators = true 15 | 16 | end -------------------------------------------------------------------------------- /test/dummy/config/application.rb: -------------------------------------------------------------------------------- 1 | require_relative 'boot' 2 | 3 | require 'rails/all' 4 | 5 | Bundler.require(*Rails.groups) 6 | 7 | #require "beautiful_scaffold" 8 | 9 | module Dummy 10 | class Application < Rails::Application 11 | # Initialize configuration defaults for originally generated Rails version. 12 | config.load_defaults 6.0 13 | 14 | # Settings in config/environments/* take precedence over those specified here. 15 | # Application configuration can go into files in config/initializers 16 | # -- all .rb files in that directory are automatically loaded after loading 17 | # the framework and any gems in your application. 18 | end 19 | end 20 | 21 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "http://rubygems.org" 2 | 3 | gemspec 4 | 5 | gem "rails", "~> 6.1" 6 | 7 | group :test do 8 | gem 'sqlite3' 9 | 10 | gems = { 11 | 'will_paginate' => nil, # v 3.1.5 12 | 'ransack' => nil, #'2.3.2', 13 | 'jquery-ui-rails' => nil, 14 | 'prawn' => nil, #'2.1.0', 15 | 'prawn-table' => nil, #'0.2.2', 16 | 'sanitize' => nil, 17 | 'bootstrap' => '~> 5.1.0', 18 | 'font-awesome-rails' => '4.7.0.7', 19 | 'momentjs-rails' => '>= 2.9.0', 20 | 'bootstrap4-datetime-picker-rails' => nil, 21 | 'jquery-rails' => '4.3.1', 22 | 'sorcery' => '0.15.0' 23 | } 24 | 25 | gems.each{ |gem_to_add, version| 26 | gem(gem_to_add, version) 27 | } 28 | end 29 | 30 | -------------------------------------------------------------------------------- /lib/generators/templates/app/views/login_logout/user_sessions/_form.html.erb: -------------------------------------------------------------------------------- 1 | <%= form_with url: user_sessions_path, method: :post do |f| %> 2 |
3 | <%= f.label :email, :class => "control-label" %>
4 | <%= f.text_field :email, :class => "form-control" %> 5 |
6 |
7 | <%= f.label :password, :class => "control-label" %>
8 | <%= f.password_field :password, :class => "form-control" %> 9 |
10 |
11 | <%= link_to t(:back, :default => 'Back'), users_path, :class => "btn btn-light" %> 12 | <%= f.submit :class => "btn btn-primary", :data => { :disable_with => t(:saving, :default => "Saving...") } %> 13 |
14 | <% end %> 15 | 16 | -------------------------------------------------------------------------------- /test/dummy/config/initializers/inflections.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new inflection rules using the following format. Inflections 4 | # are locale specific, and you may define rules for as many different 5 | # locales as you wish. All of these examples are active by default: 6 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 7 | # inflect.plural /^(ox)$/i, '\1en' 8 | # inflect.singular /^(ox)en/i, '\1' 9 | # inflect.irregular 'person', 'people' 10 | # inflect.uncountable %w( fish sheep ) 11 | # end 12 | 13 | # These inflection rules are supported but not enabled by default: 14 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 15 | # inflect.acronym 'RESTful' 16 | # end 17 | -------------------------------------------------------------------------------- /test/dummy/config/database.yml: -------------------------------------------------------------------------------- 1 | # SQLite. Versions 3.8.0 and up are supported. 2 | # gem install sqlite3 3 | # 4 | # Ensure the SQLite 3 gem is defined in your Gemfile 5 | # gem 'sqlite3' 6 | # 7 | default: &default 8 | adapter: sqlite3 9 | pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> 10 | timeout: 5000 11 | 12 | development: 13 | <<: *default 14 | database: db/development.sqlite3 15 | 16 | # Warning: The database defined as "test" will be erased and 17 | # re-generated from your development database when you run "rake". 18 | # Do not set this db to the same as development or production. 19 | test: 20 | <<: *default 21 | database: db/test.sqlite3 22 | 23 | production: 24 | <<: *default 25 | database: db/production.sqlite3 26 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | begin 2 | require 'bundler/setup' 3 | rescue LoadError 4 | puts 'You must `gem install bundler` and `bundle install` to run rake tasks' 5 | end 6 | 7 | require 'rdoc/task' 8 | 9 | RDoc::Task.new(:rdoc) do |rdoc| 10 | rdoc.rdoc_dir = 'rdoc' 11 | rdoc.title = 'Beautiful-Scaffold' 12 | rdoc.options << '--line-numbers' 13 | rdoc.rdoc_files.include('README.md') 14 | rdoc.rdoc_files.include('lib/**/*.rb') 15 | end 16 | 17 | APP_RAKEFILE = File.expand_path("test/dummy/Rakefile", __dir__) 18 | load 'rails/tasks/engine.rake' 19 | load 'rails/tasks/statistics.rake' 20 | require 'bundler/gem_tasks' 21 | require 'rake/testtask' 22 | 23 | Rake::TestTask.new(:test) do |t| 24 | t.libs << 'test' 25 | t.pattern = 'test/**/*_test.rb' 26 | t.verbose = false 27 | end 28 | 29 | task default: :test 30 | -------------------------------------------------------------------------------- /lib/generators/templates/app/assets/javascripts/fixed_menu.js: -------------------------------------------------------------------------------- 1 | function modify_dom_init(){ 2 | /* 3 | var span2 = $("body>div.container-fluid>div.row>div.col-md-2"); 4 | span2.addClass("fixed visible-md visible-lg"); 5 | span2.removeClass("col-md-2"); 6 | 7 | span2.children("ul").removeClass("well"); 8 | 9 | var span10 = $("body>div.container-fluid>div.row>div.col-md-10"); 10 | span10.addClass("filler"); 11 | span10.removeClass("col-md-10"); 12 | 13 | var cnt2 = $("body>div.container-fluid").contents(); 14 | $("body>div.container-fluid").replaceWith(cnt2); 15 | 16 | var cnt = $("body>div.row").contents(); 17 | $("body>div.row").replaceWith(cnt); 18 | 19 | $('body').show(); 20 | $('body').css('display','block'); // Hack Firefox previous line dosen't work with firefox 21 | */ 22 | } -------------------------------------------------------------------------------- /beautiful_scaffold.gemspec: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | $:.push File.expand_path("../lib", __FILE__) 3 | 4 | require "beautiful_scaffold/version" 5 | 6 | Gem::Specification.new do |s| 7 | s.name = "beautiful_scaffold" 8 | s.version = BeautifulScaffold::VERSION 9 | s.platform = Gem::Platform::RUBY 10 | s.summary = "Beautiful Scaffold generate fully customizable scaffold" 11 | s.email = "claudel.sylvain@gmail.com" 12 | s.homepage = "https://blog.rivsc.ovh" 13 | s.description = "Beautiful Scaffold generate a complete scaffold (sort, export, paginate and filter data) http://beautiful-scaffold.rivsc.ovh" 14 | s.authors = ['Sylvain Claudel'] 15 | s.files = `git ls-files`.split("\n").reject{ |filepath| filepath.start_with? 'test/' } 16 | s.licenses = ['MIT'] 17 | 18 | s.require_paths = ["lib","lib/generators","test/*"] 19 | end 20 | -------------------------------------------------------------------------------- /test/test_helper.rb: -------------------------------------------------------------------------------- 1 | # Configure Rails Environment 2 | ENV["RAILS_ENV"] = "test" 3 | 4 | require_relative "../test/dummy/config/environment" 5 | ActiveRecord::Migrator.migrations_paths = [File.expand_path("../test/dummy/db/migrate", __dir__)] 6 | ActiveRecord::Migrator.migrations_paths << File.expand_path('../db/migrate', __dir__) 7 | require "rails/test_help" 8 | 9 | # Filter out the backtrace from minitest while preserving the one from other libraries. 10 | Minitest.backtrace_filter = Minitest::BacktraceFilter.new 11 | 12 | 13 | # Load fixtures from the engine 14 | if ActiveSupport::TestCase.respond_to?(:fixture_path=) 15 | ActiveSupport::TestCase.fixture_path = File.expand_path("fixtures", __dir__) 16 | ActionDispatch::IntegrationTest.fixture_path = ActiveSupport::TestCase.fixture_path 17 | ActiveSupport::TestCase.file_fixture_path = ActiveSupport::TestCase.fixture_path + "/files" 18 | ActiveSupport::TestCase.fixtures :all 19 | end 20 | -------------------------------------------------------------------------------- /test/dummy/config/locales/en.yml: -------------------------------------------------------------------------------- 1 | # Files in the config/locales directory are used for internationalization 2 | # and are automatically loaded by Rails. If you want to use locales other 3 | # than English, add the necessary files in this directory. 4 | # 5 | # To use the locales, use `I18n.t`: 6 | # 7 | # I18n.t 'hello' 8 | # 9 | # In views, this is aliased to just `t`: 10 | # 11 | # <%= t('hello') %> 12 | # 13 | # To use a different locale, set it with `I18n.locale`: 14 | # 15 | # I18n.locale = :es 16 | # 17 | # This would use the information in config/locales/es.yml. 18 | # 19 | # The following keys must be escaped otherwise they will not be retrieved by 20 | # the default I18n backend: 21 | # 22 | # true, false, on, off, yes, no 23 | # 24 | # Instead, surround them with single quotes. 25 | # 26 | # en: 27 | # 'true': 'foo' 28 | # 29 | # To learn more, please read the Rails Internationalization guide 30 | # available at https://guides.rubyonrails.org/i18n.html. 31 | 32 | en: 33 | hello: "Hello world" 34 | -------------------------------------------------------------------------------- /lib/generators/USAGE: -------------------------------------------------------------------------------- 1 | # 2 | # How to test Beautiful-Scaffold in a fresh rails app 3 | # 4 | 5 | rails new my_test_app 6 | cd my_test_app 7 | echo "gem 'beautiful_scaffold', '2.0.2'" >> Gemfile 8 | bundle update 9 | 10 | rails generate beautiful_scaffold family name:string description:wysiwyg 11 | rails generate beautiful_scaffold product family:references price:price name:string visible:boolean description:wysiwyg 12 | rails generate beautiful_scaffold user pseudo:string email:string 13 | rails generate beautiful_scaffold tag name:string 14 | 15 | rails generate beautiful_jointable product tag 16 | 17 | rails generate beautiful_migration AddColorSecToProducts color_secondary:color 18 | 19 | rails generate beautiful_locale fr 20 | 21 | rails generate beautiful_sorcery 22 | rails generate beautiful_cancancan 23 | 24 | rake db:migrate 25 | rails server 26 | 27 | # Open a web browser http://localhost:3000/ 28 | # Register, and copy/paste the link you see in the rails server log : e.g. http://localhost:3000/users/axfEQ8i38ZtXTi-oTzuw/activate 29 | 30 | 31 | -------------------------------------------------------------------------------- /lib/generators/templates/app/views/partials/_show_field.html.erb: -------------------------------------------------------------------------------- 1 | <%- attributes.each do |attribute| -%> 2 |

3 | <%%= <%= i18n_t_a(singular_table_name, attribute.name) %> %>: 4 | <%- if @beautiful_attributes.include?(attribute.name + ':price') -%> 5 | <%%= number_to_currency(@<%= singular_table_name %>.<%= attribute.name %>) %> 6 | <%- elsif @beautiful_attributes.include?(attribute.name + ':boolean') -%> 7 | <%%= t((@<%= singular_table_name %>.<%= attribute.name %> ? "yes".to_sym : "no".to_sym)) %> 8 | <%- elsif @beautiful_attributes.include?(attribute.name + ':references') -%> 9 | <%%= (@<%= singular_table_name %>.<%= attribute.name %>.nil? ? "" : @<%= singular_table_name %>.<%= attribute.name %>.caption) %> 10 | <%- elsif @beautiful_attributes.include?(attribute.name + ':color') -%> 11 |   12 | <%- else -%> 13 | <%%= @<%= singular_table_name %>.<%= attribute.name %> %> 14 | <%- end -%> 15 |

16 | <%- end -%> 17 | -------------------------------------------------------------------------------- /lib/generators/templates/app/assets/stylesheets/application-bs.css: -------------------------------------------------------------------------------- 1 | /* 2 | * This is a manifest file that'll be compiled into application.css, which will include all the files 3 | * listed below. 4 | * 5 | * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets, 6 | * or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path. 7 | * 8 | * You're free to add application-wide styles to this file and they'll appear at the top of the 9 | * compiled file, but it's generally better to create a new file per style scope. 10 | * 11 | *= require_self 12 | *= require jquery-ui 13 | *= require beautiful-scaffold 14 | *= require bootstrap-wysihtml5 15 | *= require themes/default/style 16 | */ 17 | 18 | /* Bootstrap 4 */ 19 | @import "bootstrap"; 20 | @import "font-awesome-sprockets"; 21 | @import "font-awesome"; 22 | @import "tempusdominus-bootstrap-4.css"; 23 | 24 | /* Fix bug driverJS position:fixed */ 25 | div#driver-highlighted-element-stage, div#driver-page-overlay { 26 | background: transparent !important; 27 | outline: 5000px solid rgba(0, 0, 0, .75) 28 | } -------------------------------------------------------------------------------- /MIT-LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2011 Claudel Sylvain (rivsc). http://blog.escarworld.com 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /lib/generators/templates/app/views/_modal_columns.html.erb: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/generators/templates/app/views/_form.html.erb: -------------------------------------------------------------------------------- 1 | <%- 2 | strformfor = "@#{singular_table_name}" 3 | if !namespace_alone.blank? 4 | strformfor = "[:#{namespace_alone}, @#{singular_table_name} ]" 5 | end 6 | -%> 7 | 8 | <%%= form_for(<%= strformfor %>) do |f| %> 9 | <%% if @<%= singular_table_name %>.errors.any? %> 10 |
11 |

<%%= pluralize(@<%= singular_table_name %>.errors.count, "error") %> prohibited this <%= singular_table_name %> from being saved:

12 | 13 | 16 |
17 | <%% end %> 18 | <%= render_partial 'app/views/partials/_form_field.html.erb' %> 19 |
20 | <%%= link_to <%= namespace_for_route %><%= plural_table_name %>_path, :class => "btn btn-light" do %> 21 | <%%= t(:back, :default => "Back") %> 22 | <%% end %> 23 | <%%= f.submit :class => "btn btn-primary", :data => { :disable_with => t(:saving, :default => "Saving...") } %> 24 |
25 | <%% end %> 26 | 27 | 28 | -------------------------------------------------------------------------------- /lib/generators/templates/app/models/pdf_report.rb: -------------------------------------------------------------------------------- 1 | # encoding : utf-8 2 | class PdfReport < Prawn::Document 3 | def to_pdf(model, mode_scope) 4 | 5 | columns = model.attribute_names 6 | 7 | data = [columns] 8 | 9 | mode_scope.each{ |modelobj| 10 | data << (columns.map{ |c| modelobj[c].to_s }).to_a 11 | } 12 | 13 | table data, :header => true, :cell_style => { :padding => 5 } do 14 | i = 0 15 | for col in columns 16 | align = case model.columns_hash[col].type 17 | when "integer" then 18 | 'right' 19 | when "string", "text" then 20 | 'left' 21 | else 22 | 'center' 23 | end 24 | style(columns(i)){ |c| c.align = align.to_sym } 25 | i += 1 26 | end 27 | end 28 | 29 | # Paginate 30 | font_size 10 31 | number_pages Time.now.strftime('%d/%m/%Y - %H:%M') + ' - /', :at => [0, -5], :align => :center 32 | repeat(:all) do 33 | bounding_box([0,0], :width => 540, :height => 2) do 34 | stroke_horizontal_rule 35 | end 36 | end 37 | 38 | render 39 | end 40 | end -------------------------------------------------------------------------------- /lib/generators/templates/app/views/partials/_forget_password.html.erb: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/generators/templates/app/assets/javascripts/application-bs.js: -------------------------------------------------------------------------------- 1 | //= require jquery3 2 | //= require jquery-ui 3 | //= require jquery_ujs 4 | //= require popper 5 | //= require bootstrap-sprockets 6 | //= require a-wysihtml5-0.3.0.min 7 | //= require moment 8 | //= require moment/fr 9 | //= require tempusdominus-bootstrap-4.js 10 | //= require bootstrap-datetimepicker-for-beautiful-scaffold 11 | //= require bootstrap-wysihtml5 12 | //= require jstree.min.js 13 | //= require jquery-barcode 14 | //= require beautiful_scaffold 15 | //= require fixed_menu 16 | 17 | function initPage(){ 18 | 19 | 20 | 21 | datetimepicker_init(); 22 | bs_init(); 23 | modify_dom_init(); 24 | } 25 | $(function() { 26 | initPage(); 27 | function startSpinner(){ 28 | $('.loader').show(); 29 | } 30 | function stopSpinner(){ 31 | $('.loader').hide(); 32 | } 33 | document.addEventListener("turbolinks:request-start", startSpinner); 34 | document.addEventListener("turbolinks:request-end", stopSpinner); 35 | document.addEventListener("turbolinks:render", stopSpinner); 36 | }); 37 | $(window).bind('turbolinks:render', function() { 38 | initPage(); 39 | }); -------------------------------------------------------------------------------- /lib/generators/templates/app/assets/stylesheets/bootstrap-wysihtml5.css: -------------------------------------------------------------------------------- 1 | ul.wysihtml5-toolbar { 2 | margin: 0; 3 | padding: 0; 4 | display: block; 5 | } 6 | 7 | ul.wysihtml5-toolbar::after { 8 | clear: both; 9 | display: table; 10 | content: ""; 11 | } 12 | 13 | ul.wysihtml5-toolbar > li { 14 | float: left; 15 | display: list-item; 16 | list-style: none; 17 | margin: 0 5px 10px 0; 18 | } 19 | 20 | ul.wysihtml5-toolbar a[data-wysihtml5-command=bold] { 21 | font-weight: bold; 22 | } 23 | 24 | ul.wysihtml5-toolbar a[data-wysihtml5-command=italic] { 25 | font-style: italic; 26 | } 27 | 28 | ul.wysihtml5-toolbar a[data-wysihtml5-command=underline] { 29 | text-decoration: underline; 30 | } 31 | 32 | ul.wysihtml5-toolbar a.btn.wysihtml5-command-active { 33 | background-image: none; 34 | -webkit-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05); 35 | -moz-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05); 36 | box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05); 37 | background-color: #E6E6E6; 38 | background-color: #D9D9D9 9; 39 | outline: 0; 40 | } 41 | 42 | ul.wysihtml5-commands-disabled .dropdown-menu { 43 | display: none !important; 44 | } 45 | -------------------------------------------------------------------------------- /lib/generators/templates/app/views/treeview.html.erb: -------------------------------------------------------------------------------- 1 |

<%%= t(:treeview, :default => 'Treeview') %> <%%= <%= i18n_t_m(singular_table_name) %> %>

2 | 3 | <% engine_or_empty = (engine_camel.present? ? "#{engine_camel}::" : '') %> 4 | 5 | <%% 6 | engine_name = "<%= engine_name %>" 7 | namespace_for_url = "<%= namespace_for_url %>" 8 | plural_model_name = "<%= model_pluralize %>" 9 | model_name = "<%= singular_table_name %>" 10 | opened_node = <%= engine_or_empty %><%= model_camelize %>.select(:id).all.map{ |g| "'treeelt_" + g.id.to_s + "'" }.join(',').html_safe 11 | %> 12 | 13 |
14 |
    15 | <%% <%= engine_or_empty %><%= model_camelize %>.transaction do %> 16 | <%% ar = <%= engine_or_empty %><%= model_camelize %>.where(:<%= model %>_id => nil) %> 17 | <%% ar = ar.order("position") if <%= engine_or_empty %><%= model_camelize %>.column_names.include?("position") %> 18 | <%% for g in ar.all %> 19 | <%%= build_treeview(g, '<%= model_pluralize %>') %> 20 | <%% end %> 21 | <%% end %> 22 |
23 |
24 | 25 | -------------------------------------------------------------------------------- /test/dummy/config/storage.yml: -------------------------------------------------------------------------------- 1 | test: 2 | service: Disk 3 | root: <%= Rails.root.join("tmp/storage") %> 4 | 5 | local: 6 | service: Disk 7 | root: <%= Rails.root.join("storage") %> 8 | 9 | # Use rails credentials:edit to set the AWS secrets (as aws:access_key_id|secret_access_key) 10 | # amazon: 11 | # service: S3 12 | # access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %> 13 | # secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %> 14 | # region: us-east-1 15 | # bucket: your_own_bucket 16 | 17 | # Remember not to checkin your GCS keyfile to a repository 18 | # google: 19 | # service: GCS 20 | # project: your_project 21 | # credentials: <%= Rails.root.join("path/to/gcs.keyfile") %> 22 | # bucket: your_own_bucket 23 | 24 | # Use rails credentials:edit to set the Azure Storage secret (as azure_storage:storage_access_key) 25 | # microsoft: 26 | # service: AzureStorage 27 | # storage_account_name: your_account_name 28 | # storage_access_key: <%= Rails.application.credentials.dig(:azure_storage, :storage_access_key) %> 29 | # container: your_container_name 30 | 31 | # mirror: 32 | # service: Mirror 33 | # primary: local 34 | # mirrors: [ amazon, google, microsoft ] 35 | -------------------------------------------------------------------------------- /lib/generators/templates/app/assets/javascripts/bootstrap-datetimepicker-for-beautiful-scaffold.js: -------------------------------------------------------------------------------- 1 | function datetimepicker_init(){ 2 | $('.tpicker').datetimepicker({ format: 'LT', widgetPositioning: { 3 | horizontal: 'auto', 4 | vertical: 'auto' 5 | } }); 6 | $('.tpicker').on('change.datetimepicker', function(elt){ 7 | var eltid = $(elt.target).attr('data-field'); 8 | $('#' + eltid + '4i').val(elt.date.hour()); 9 | $('#' + eltid + '5i').val(elt.date.minute()); 10 | }); 11 | $('.dpicker').datetimepicker({ format: 'L', widgetPositioning: { 12 | horizontal: 'auto', 13 | vertical: 'auto' 14 | } }); 15 | $('.dpicker').on('change.datetimepicker', function(elt){ 16 | var eltid = $(elt.target).attr('data-field'); 17 | $('#' + eltid + '3i').val(elt.date.date()); 18 | $('#' + eltid + '2i').val(elt.date.month()+1); 19 | $('#' + eltid + '1i').val(elt.date.year()); 20 | }); 21 | $(document).on('click', '.dpicker', function(e){ 22 | $(this).datetimepicker('show'); 23 | }); 24 | $(document).on('click', '.tpicker', function(e){ 25 | $(this).datetimepicker('show'); 26 | }); 27 | $(".datetimepicker-input").each(function(i, elt){ 28 | $(elt).removeAttr("name"); 29 | }); 30 | } -------------------------------------------------------------------------------- /lib/generators/templates/app/models/ability.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class Ability 4 | include CanCan::Ability 5 | 6 | def initialize(user) 7 | if !user.nil? 8 | if user.id == 1 9 | can :manage, :all 10 | end 11 | end 12 | # Define abilities for the passed in user here. For example: 13 | # 14 | # user ||= User.new # guest user (not logged in) 15 | # if user.admin? 16 | # can :manage, :all 17 | # else 18 | # can :read, :all 19 | # end 20 | # 21 | # The first argument to `can` is the action you are giving the user 22 | # permission to do. 23 | # If you pass :manage it will apply to every action. Other common actions 24 | # here are :read, :create, :update and :destroy. 25 | # 26 | # The second argument is the resource the user can perform the action on. 27 | # If you pass :all it will apply to every resource. Otherwise pass a Ruby 28 | # class of the resource. 29 | # 30 | # The third argument is an optional hash of conditions to further filter the 31 | # objects. 32 | # For example, here the user can only update published articles. 33 | # 34 | # can :update, Article, :published => true 35 | # 36 | # See the wiki for details: 37 | # https://github.com/CanCanCommunity/cancancan/wiki/Defining-Abilities 38 | end 39 | end -------------------------------------------------------------------------------- /test/dummy/config/initializers/content_security_policy.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Define an application-wide content security policy 4 | # For further information see the following documentation 5 | # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy 6 | 7 | # Rails.application.config.content_security_policy do |policy| 8 | # policy.default_src :self, :https 9 | # policy.font_src :self, :https, :data 10 | # policy.img_src :self, :https, :data 11 | # policy.object_src :none 12 | # policy.script_src :self, :https 13 | # policy.style_src :self, :https 14 | 15 | # # Specify URI for violation reports 16 | # # policy.report_uri "/csp-violation-report-endpoint" 17 | # end 18 | 19 | # If you are using UJS then enable automatic nonce generation 20 | # Rails.application.config.content_security_policy_nonce_generator = -> request { SecureRandom.base64(16) } 21 | 22 | # Set the nonce only to specific directives 23 | # Rails.application.config.content_security_policy_nonce_directives = %w(script-src) 24 | 25 | # Report CSP violations to a specified URI 26 | # For further information see the following documentation: 27 | # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy-Report-Only 28 | # Rails.application.config.content_security_policy_report_only = true 29 | -------------------------------------------------------------------------------- /lib/generators/templates/app/locales/beautiful_scaffold.ja.yml: -------------------------------------------------------------------------------- 1 | # encoding : utf-8 2 | ja: 3 | edit: "更新" 4 | editing: "更新:" 5 | update: "更新" 6 | show: "表示" 7 | showing: "表示:" 8 | delete: "削除" 9 | destroy: "削除" 10 | are_you_sure: "続行していいですか?" 11 | new: "新規作成" 12 | create: "新規作成" 13 | listing: "一覧:" 14 | filter: "検索" 15 | cancel: "キャンセル" 16 | export: "エクスポート" 17 | search: "検索" 18 | sort: "トライ" 19 | download: "ダウンロード" 20 | "yes": "はい" 21 | "no": "いいえ" 22 | all: "すべて" 23 | back: "戻る" 24 | manage: "管理" 25 | settrueforattr: "%{attr} を「はい」にする" 26 | setfalseforattr: "%{attr} を「いいえ」にする" 27 | processing: "実行中" 28 | process: "実行" 29 | batch: "一括処理" 30 | create_success: "%{model} を作成しました。" 31 | update_success: "%{model} を更新しました。" 32 | greater_than: "以上" 33 | smaller_than: "以下" 34 | select_columns: "項目選択" 35 | treeview: "ツリー表示" 36 | profile: "プロファイル" 37 | sign_out: "サイン・アウト" 38 | sign_in: "サインイン" 39 | sign_up: "サイン・アップ" 40 | login: "ログイン" 41 | remember_me: "ログインを維持する" 42 | email: "メールアドレス" 43 | password: "パスワード" 44 | password_confirmation: "パスワード (確認)" 45 | forgot_your_password: "パスワードを忘れましたか?" 46 | send_me_reset_password_instructions: "パスワードのリセット方法を送る" 47 | register: "登録" 48 | search_and_filter: "検索条件" 49 | more_options: "追加オプション..." 50 | edit_profile: "プロファイル編集" 51 | logout: "ログアウト" 52 | register: "登録" 53 | login: "ログイン" 54 | logged_out: "ログアウトしました" 55 | login_failed: "ログインに失敗しました" 56 | login_successful: "ログイン成功" 57 | crypted_password: "暗号化されたパスワード" -------------------------------------------------------------------------------- /lib/generators/templates/app/models/concerns/fulltext_concern.rb: -------------------------------------------------------------------------------- 1 | module FulltextConcern 2 | extend ActiveSupport::Concern 3 | 4 | included do 5 | ######################### 6 | # 7 | # Scopes 8 | # Relations 9 | # Filter 10 | #... 11 | ######################### 12 | before_save :fulltext_field_processing 13 | end 14 | 15 | ######################### 16 | # Méthode d'instance 17 | ######################### 18 | def fulltext_field_processing 19 | # You can preparse with own things here 20 | generate_fulltext_field 21 | end 22 | 23 | def generate_fulltext_field 24 | fields = (self.class.fulltext_fields || []) 25 | fields.each{ |f| 26 | html, clear = htmlize(self[f], self[f + '_typetext']) 27 | self[f + '_fulltext'] = clear 28 | } 29 | end 30 | 31 | def htmlize(text, type) 32 | case type 33 | #when 'bbcode' then 34 | # require 'bb-ruby' 35 | # html = text.bbcode_to_html 36 | when 'html' then 37 | html = text 38 | #when 'textile' then 39 | # html = RedCloth.new(text).to_html 40 | #when 'markdown' then 41 | # require 'rdiscount' 42 | # html = RDiscount.new(text).to_html 43 | #when 'wiki' then 44 | # html = WikiCloth::Parser.new({:data => text}).to_html 45 | else 46 | html 47 | end 48 | return html, Sanitize.clean(html) 49 | end 50 | 51 | class_methods do 52 | ######################### 53 | # Méthode de Class 54 | ######################### 55 | end 56 | end -------------------------------------------------------------------------------- /lib/generators/templates/app/views/partials/_index_column.html.erb: -------------------------------------------------------------------------------- 1 | <%- attributes.each do |attribute| -%> 2 | ", "<%= attribute.name %>") %> class="bs-col-<%= attribute.name %> <%%= align_attribute("<%= attribute.type %>") %>"> 3 | <%- if @beautiful_attributes.include?(attribute.name + ':price') -%> 4 | <%%= number_to_currency(<%= singular_table_name %>.<%= attribute.name %>, :locale => I18n.locale) %> 5 | <%- elsif @beautiful_attributes.include?(attribute.name + ':boolean') -%> 6 | <%%= t((<%= singular_table_name %>.<%= attribute.name %> ? "yes" : "no").to_sym) %> 7 | <%- elsif @beautiful_attributes.include?(attribute.name + ':references') -%> 8 | <%% if !<%= singular_table_name %>.<%= attribute.name %>_id.nil? %> 9 | <%%= link_to <%= singular_table_name %>.<%= attribute.name %>.caption, <%= namespace_for_route %><%= attribute.name %>_path(<%= singular_table_name %>.<%= attribute.name %>_id) %> 10 | <%% else %> 11 | <%%= t(:any, :default => "Any") %> 12 | <%% end %> 13 | <%- elsif @beautiful_attributes.include?(attribute.name + ':color') -%> 14 |   15 | <%- else -%> 16 | <%%= <%= singular_table_name %>.<%= attribute.name %> %> 17 | <%- end -%> 18 | 19 | <%- end -%> 20 | -------------------------------------------------------------------------------- /test/dummy/config/puma.rb: -------------------------------------------------------------------------------- 1 | # Puma can serve each request in a thread from an internal thread pool. 2 | # The `threads` method setting takes two numbers: a minimum and maximum. 3 | # Any libraries that use thread pools should be configured to match 4 | # the maximum value specified for Puma. Default is set to 5 threads for minimum 5 | # and maximum; this matches the default thread size of Active Record. 6 | # 7 | max_threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 } 8 | min_threads_count = ENV.fetch("RAILS_MIN_THREADS") { max_threads_count } 9 | threads min_threads_count, max_threads_count 10 | 11 | # Specifies the `port` that Puma will listen on to receive requests; default is 3000. 12 | # 13 | port ENV.fetch("PORT") { 3000 } 14 | 15 | # Specifies the `environment` that Puma will run in. 16 | # 17 | environment ENV.fetch("RAILS_ENV") { "development" } 18 | 19 | # Specifies the `pidfile` that Puma will use. 20 | pidfile ENV.fetch("PIDFILE") { "tmp/pids/server.pid" } 21 | 22 | # Specifies the number of `workers` to boot in clustered mode. 23 | # Workers are forked web server processes. If using threads and workers together 24 | # the concurrency of the application would be max `threads` * `workers`. 25 | # Workers do not work on JRuby or Windows (both of which do not support 26 | # processes). 27 | # 28 | # workers ENV.fetch("WEB_CONCURRENCY") { 2 } 29 | 30 | # Use the `preload_app!` method when specifying a `workers` number. 31 | # This directive tells Puma to first boot the application and load code 32 | # before forking the application. This takes advantage of Copy On Write 33 | # process behavior so workers use less memory. 34 | # 35 | # preload_app! 36 | 37 | # Allow puma to be restarted by `rails restart` command. 38 | plugin :tmp_restart 39 | -------------------------------------------------------------------------------- /lib/generators/templates/app/locales/beautiful_scaffold.en.yml: -------------------------------------------------------------------------------- 1 | # encoding : utf-8 2 | en: 3 | edit: "Edit" 4 | editing: "Editing" 5 | update: "Update" 6 | show: "Show" 7 | showing: "Showing" 8 | delete: "Delete" 9 | destroy: "Destroy" 10 | are_you_sure: "Are you sure ?" 11 | new: "New" 12 | create: "Create" 13 | listing: "Listing" 14 | filter: "Filter" 15 | cancel: "Cancel" 16 | export: "Export" 17 | search: "Search" 18 | sort: "Sort" 19 | download: "Download" 20 | "yes": "Yes" 21 | "no": "No" 22 | all: "All" 23 | back: "Back" 24 | manage: "Manage" 25 | settrueforattr: "Set %{attr} as true" 26 | setfalseforattr: "Set %{attr} as false" 27 | processing: "Processing" 28 | process: "Process" 29 | batch: "Batch" 30 | create_success: "%{model} was successfully created." 31 | update_success: "%{model} was successfully updated." 32 | greater_than: "Greater than" 33 | smaller_than: "Smaller than" 34 | select_columns: "Select Columns" 35 | treeview: "Treeview" 36 | profile: "Profile" 37 | sign_out: "Sign Out" 38 | sign_in: "Sign in" 39 | sign_up: "Sign up" 40 | login: "Login" 41 | remember_me: "Remember me" 42 | email: "Email" 43 | password: "Password" 44 | password_confirmation: "Password (Confirmation)" 45 | forgot_your_password: "Forgot your password?" 46 | send_me_reset_password_instructions: "Send me reset password instructions" 47 | register: "Register" 48 | search_and_filter: "Search and filter" 49 | more_options: "More options..." 50 | edit_profile: "edit profile" 51 | logout: "Logout" 52 | register: "Register" 53 | login: "Login" 54 | logged_out: "Logged out" 55 | login_failed: "Login failed" 56 | login_successful: "Login successful" 57 | crypted_password: "Crypted password" -------------------------------------------------------------------------------- /lib/generators/templates/app/views/_form_habtm_tag.html.erb: -------------------------------------------------------------------------------- 1 | <%#= 2 | render :partial => "layouts/form_habtm_tag", :locals => { 3 | :model_class => @product, 4 | :model_name => "product", 5 | :plural_model_name => "products", 6 | :linked_model_name => "tag", 7 | :plural_linked_model_name => "tags", 8 | :namespace_bs => "admin", 9 | :engine_bs => "YOUR_ENGINE_OR_EMPTY_STRING", 10 | :field_to_search_for_linked_model => "name", 11 | :attr_to_show => "caption", 12 | :f => f 13 | } 14 | # Example to put in a _form.html.erb 15 | %> 16 | <% path_namespace = "/" %> 17 | <% path_namespace += "#{engine_bs}/" if engine_bs.present? %> 18 | <% path_namespace += "/#{namespace_bs}/" if namespace_bs.present? %> 19 | 20 |
21 | <%= f.label plural_linked_model_name.to_sym, t(plural_linked_model_name.to_sym, :default => plural_linked_model_name.capitalize), :class => "control-label" %> 22 |
23 | 31 |
32 |
-------------------------------------------------------------------------------- /lib/generators/beautiful_cancancan_generator.rb: -------------------------------------------------------------------------------- 1 | # encoding : utf-8 2 | class BeautifulCancancanGenerator < Rails::Generators::Base 3 | require_relative 'beautiful_scaffold_common_methods' 4 | include BeautifulScaffoldCommonMethods 5 | 6 | source_root File.expand_path('../templates', __FILE__) 7 | 8 | #argument :model, :type => :string, :desc => "Name of model (ex: user)" 9 | 10 | def install_cancancan 11 | model = "user" 12 | 13 | gem("cancancan", "3.2.1") 14 | 15 | Bundler.with_unbundled_env do 16 | run "bundle install" 17 | end 18 | 19 | # Because generators doesn't work ! 20 | copy_file("app/models/ability.rb") 21 | 22 | # Why that doesn't work... boring... 23 | #puts rails_command("generate cancan:ability", capture: true) 24 | # Why that doesn't work too... boring... 25 | #generate("cancan:ability") 26 | 27 | # current_user method need for CanCan 28 | current_user_method = "" 29 | if model != "user" then 30 | current_user_method = " 31 | def current_user 32 | current_#{model} 33 | end" 34 | end 35 | 36 | # Exception for AccessDenied 37 | inject_into_file("app/controllers/application_controller.rb", " 38 | rescue_from CanCan::AccessDenied do |exception| 39 | respond_to do |format| 40 | format.json { head :forbidden, content_type: 'text/html' } 41 | format.html { redirect_to root_url, :alert => exception.message } 42 | format.js { head :forbidden, content_type: 'text/html' } 43 | end 44 | end 45 | #{current_user_method} 46 | ", :after => "class ApplicationController < ActionController::Base\n") 47 | 48 | # Access controlled by CanCanCan (in beautiful_scaffold) 49 | inject_into_file("app/controllers/application_controller.rb", "#", :before => "before_action :authenticate_#{model}!, :except => [:dashboard]") 50 | end 51 | end 52 | -------------------------------------------------------------------------------- /lib/generators/templates/app/initializers/link_renderer.rb: -------------------------------------------------------------------------------- 1 | require 'will_paginate/view_helpers/action_view' 2 | 3 | module WillPaginate 4 | module ActionView 5 | class BootstrapLinkRenderer < LinkRenderer 6 | 7 | protected 8 | 9 | def page_number(page) 10 | is_current_page = (page == current_page) 11 | temphtml = '
  • ' 12 | unless is_current_page 13 | temphtml += link(page, page, :rel => rel_value(page), :class => 'page-link') 14 | else 15 | temphtml += tag(:a, page, :class => 'current active page-link') 16 | end 17 | temphtml += '
  • ' 18 | temphtml 19 | end 20 | 21 | def gap 22 | text = @template.will_paginate_translate(:page_gap) { '…' } 23 | %(#{text}) 24 | end 25 | 26 | def previous_or_next_page(page, text, classname) 27 | temphtml = '
  • ' 28 | if page 29 | temphtml += link(text, page, :class => classname + ' page-link') 30 | else 31 | temphtml += tag(:a, text, :class => classname + ' page-link') 32 | end 33 | temphtml += '
  • ' 34 | temphtml 35 | end 36 | 37 | def html_container(html) 38 | '' 39 | end 40 | 41 | private 42 | 43 | def param_name 44 | @options[:param_name].to_s 45 | end 46 | 47 | def link(text, target, attributes = {}) 48 | if target.is_a? Fixnum 49 | attributes[:rel] = rel_value(target) 50 | target = url(target) 51 | end 52 | attributes[:href] = target 53 | tag(:a, text, attributes) 54 | end 55 | 56 | end 57 | end 58 | end 59 | -------------------------------------------------------------------------------- /lib/generators/templates/app/locales/beautiful_scaffold.fr.yml: -------------------------------------------------------------------------------- 1 | # encoding : utf-8 2 | fr: 3 | edit: "Editer" 4 | editing: "Edition" 5 | update: "Mise à jour" 6 | show: "Afficher" 7 | showing: "Affichage" 8 | delete: "Supprimer" 9 | destroy: "Détruire" 10 | are_you_sure: "Etes-vous sûr ?" 11 | new: "Nouveau" 12 | create: "Créer" 13 | listing: "Liste" 14 | filter: "Filtrer" 15 | cancel: "Annuler" 16 | export: "Exporter" 17 | search: "Rechercher" 18 | sort: "Trier" 19 | download: "Télécharger" 20 | "yes": "Oui" 21 | "no": "Non" 22 | all: "Tout" 23 | back: "Retour" 24 | manage: "Administrer" 25 | settrueforattr: "Mettre %{attr} à vrai" 26 | setfalseforattr: "Mettre %{attr} à faux" 27 | processing: "En traitement..." 28 | process: "Effectuer" 29 | batch: "Traitement par lot" 30 | create_success: "%{model} a été créé(e) avec succès." 31 | update_success: "%{model} a été mis(e) à jour avec succès." 32 | greater_than: "Plus grand que" 33 | smaller_than: "Plus petit que" 34 | select_columns: "Selection des colonnes" 35 | treeview: "Arborescence" 36 | profile: "Profil" 37 | sign_out: "Déconnexion" 38 | sign_in: "Authentification" 39 | sign_up: "Inscription" 40 | login: "Login" 41 | remember_me: "Se souvenir de moi" 42 | email: "Email" 43 | password: "Mot de passe" 44 | password_confirmation: "Mot de passe (Confirmation)" 45 | forgot_your_password: "Mot de passe oublié ?" 46 | send_me_reset_password_instructions: "Me ré-envoyer les instructions de réinitialisation du mot de passe" 47 | register: "Inscription" 48 | search_and_filter: "Option de filtre" 49 | more_options: "Plus d'options..." 50 | edit_profile: "Editer mon profil" 51 | logout: "Déconnexion" 52 | register: "S'incrire" 53 | login: "S'identifier" 54 | logged_out: "déconnecté" 55 | login_failed: "Erreur d'identification" 56 | login_successful: "Identification réussie" 57 | crypted_password: "Crypted password" -------------------------------------------------------------------------------- /test/dummy/config/environments/test.rb: -------------------------------------------------------------------------------- 1 | # The test environment is used exclusively to run your application's 2 | # test suite. You never need to work with it otherwise. Remember that 3 | # your test database is "scratch space" for the test suite and is wiped 4 | # and recreated between test runs. Don't rely on the data there! 5 | 6 | Rails.application.configure do 7 | # Settings specified here will take precedence over those in config/application.rb. 8 | 9 | config.cache_classes = false 10 | config.action_view.cache_template_loading = true 11 | 12 | # Do not eager load code on boot. This avoids loading your whole application 13 | # just for the purpose of running a single test. If you are using a tool that 14 | # preloads Rails for running tests, you may have to set it to true. 15 | config.eager_load = false 16 | 17 | # Configure public file server for tests with Cache-Control for performance. 18 | config.public_file_server.enabled = true 19 | config.public_file_server.headers = { 20 | 'Cache-Control' => "public, max-age=#{1.hour.to_i}" 21 | } 22 | 23 | # Show full error reports and disable caching. 24 | config.consider_all_requests_local = true 25 | config.action_controller.perform_caching = false 26 | config.cache_store = :null_store 27 | 28 | # Raise exceptions instead of rendering exception templates. 29 | config.action_dispatch.show_exceptions = false 30 | 31 | # Disable request forgery protection in test environment. 32 | config.action_controller.allow_forgery_protection = false 33 | 34 | # Store uploaded files on the local file system in a temporary directory. 35 | config.active_storage.service = :test 36 | 37 | config.action_mailer.perform_caching = false 38 | 39 | # Tell Action Mailer not to deliver emails to the real world. 40 | # The :test delivery method accumulates sent emails in the 41 | # ActionMailer::Base.deliveries array. 42 | config.action_mailer.delivery_method = :test 43 | 44 | # Print deprecation notices to the stderr. 45 | config.active_support.deprecation = :stderr 46 | 47 | # Raises error for missing translations. 48 | # config.action_view.raise_on_missing_translations = true 49 | config.logger = Logger.new(STDOUT) 50 | config.log_level = :info 51 | end 52 | -------------------------------------------------------------------------------- /test/lib/generators/beautiful_storage_generator_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | require 'generators/beautiful_scaffold_generator' 3 | require 'generators/beautiful_storage_generator' 4 | 5 | # In order to run test : in Beautiful-Scaffold dir just run : 6 | # rake test 7 | # 8 | # Source : 9 | # https://fossies.org/linux/rails/railties/test/generators/shared_generator_tests.rb 10 | # https://rossta.net/blog/testing-rails-generators.html 11 | 12 | # TODO test for engine 13 | 14 | class BeautifulStorageGeneratorTest < Rails::Generators::TestCase 15 | tests BeautifulScaffoldGenerator 16 | destination Rails.root.join('../tmp/dummyappstorage') # test/dummy/tmp/generators/dummyapp.... 17 | 18 | setup do 19 | #puts "SETUP " * 100 20 | prepare_destination # Create tmp 21 | Dir.chdir(File.dirname(destination_root)) { # dans test/tmp 22 | system "rails new dummyappstorage --skip-test-unit --skip-spring --skip-bootsnap --skip-webpack-install --skip-javascript" 23 | } 24 | end 25 | 26 | # At the end of test 27 | teardown do 28 | Dir.chdir(File.dirname(destination_root)) { 29 | system 'rm -rf dummyappstorage' 30 | } 31 | end 32 | 33 | test "generator storage" do 34 | ####### 35 | # App 36 | ####### 37 | 38 | self.class.generator_class = BeautifulScaffoldGenerator 39 | run_generator 'user name:string firstname:string -s'.split(' ') 40 | assert_file 'app/models/user.rb' 41 | 42 | ####### 43 | # Storage 44 | ####### 45 | 46 | self.class.generator_class = BeautifulStorageGenerator 47 | run_generator 'user picture_file'.split(' ') 48 | 49 | assert_file 'app/models/user.rb' do |content| 50 | # ActiveStorage 51 | assert_match('has_one_attached :picture_file', content) 52 | # permitted attribute 53 | assert_match('return :picture_file', content) 54 | end 55 | 56 | assert_file 'app/views/users/_form.html.erb' do |content| 57 | assert_match('form_for(@user, multipart: true)', content) 58 | assert_match("<%= f.label :picture_file, t('app.models.user.bs_attributes.picture_file', :default => 'picture_file').capitalize, :class => 'control-label' %>
    ", content) 59 | assert_match("<%= f.file_field :picture_file, direct_upload: true, :class => 'form-control' %>", content) 60 | end 61 | assert_file 'app/views/users/show.html.erb' do |content| 62 | assert_match('<%= image_tag @user.picture_file.variant(resize_to_limit: [100, 100]) %>', content) 63 | end 64 | end 65 | end 66 | -------------------------------------------------------------------------------- /test/dummy/config/environments/development.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | 4 | # In the development environment your application's code is reloaded on 5 | # every request. This slows down response time but is perfect for development 6 | # since you don't have to restart the web server when you make code changes. 7 | config.cache_classes = false 8 | 9 | # Do not eager load code on boot. 10 | config.eager_load = false 11 | 12 | # Show full error reports. 13 | config.consider_all_requests_local = true 14 | 15 | # Enable/disable caching. By default caching is disabled. 16 | # Run rails dev:cache to toggle caching. 17 | if Rails.root.join('tmp', 'caching-dev.txt').exist? 18 | config.action_controller.perform_caching = true 19 | config.action_controller.enable_fragment_cache_logging = true 20 | 21 | config.cache_store = :memory_store 22 | config.public_file_server.headers = { 23 | 'Cache-Control' => "public, max-age=#{2.days.to_i}" 24 | } 25 | else 26 | config.action_controller.perform_caching = false 27 | 28 | config.cache_store = :null_store 29 | end 30 | 31 | # Store uploaded files on the local file system (see config/storage.yml for options). 32 | config.active_storage.service = :local 33 | 34 | # Don't care if the mailer can't send. 35 | config.action_mailer.raise_delivery_errors = false 36 | 37 | config.action_mailer.perform_caching = false 38 | 39 | # Print deprecation notices to the Rails logger. 40 | config.active_support.deprecation = :log 41 | 42 | # Raise an error on page load if there are pending migrations. 43 | config.active_record.migration_error = :page_load 44 | 45 | # Highlight code that triggered database queries in logs. 46 | config.active_record.verbose_query_logs = true 47 | 48 | # Debug mode disables concatenation and preprocessing of assets. 49 | # This option may cause significant delays in view rendering with a large 50 | # number of complex assets. 51 | config.assets.debug = true 52 | 53 | # Suppress logger output for asset requests. 54 | config.assets.quiet = true 55 | 56 | # Raises error for missing translations. 57 | # config.action_view.raise_on_missing_translations = true 58 | 59 | # Use an evented file watcher to asynchronously detect changes in source code, 60 | # routes, locales, etc. This feature depends on the listen gem. 61 | # config.file_watcher = ActiveSupport::EventedFileUpdateChecker 62 | end 63 | -------------------------------------------------------------------------------- /lib/generators/templates/app/views/_mass_inserting.html.erb: -------------------------------------------------------------------------------- 1 | <% model = ("#{(engine.blank? ? '' : "#{engine.camelize}::")}#{model_name.camelize}").constantize %> 2 | <% formparams = [] %> 3 | <% if !namespace.blank? %> 4 | <% formparams << namespace %> 5 | <% end %> 6 | <% formparams << model.new %> 7 |
    8 |
    9 |
    10 |
    11 | <%= form_for formparams, :method => :post, :html => { :class => "form-inline mass-inserting #{(params[:mass_inserting] ? 'setfocus' : '')}" } do |f| %> 12 | <%= hidden_field_tag :mass_inserting, true %> 13 | <% for col in model_columns %> 14 |
    class="form-group mr-sm-2 col-<%= col %>"> 15 | <%= 16 | ar = model.columns_hash[col] 17 | if !ar.nil? 18 | case ar.type 19 | when :integer then 20 | if col =~ /.*_id/ 21 | f.collection_select((col).to_sym, col.camelize.constantize.all, :id, :caption, { :include_blank => true, :selected => (begin params[:q]["#{col}_eq"].to_i rescue '' end) }, {:class => "form-control"}) 22 | else 23 | f.text_field(col.to_sym, :placeholder => t(i18n_translate_path(model_name, col), default: "#{model_name}.#{col}").capitalize, :class => "form-control") 24 | end 25 | when :boolean then 26 | ( 27 | "".html_safe 28 | ) 29 | else 30 | f.text_field(col.to_sym, :placeholder => t(i18n_translate_path(model_name, col), default: "#{model_name}.#{col}").capitalize, :class => "form-control") 31 | end 32 | else 33 | f.collection_select(("#{false ? "#{engine}_" : ''}#{col}_id").to_sym, ("#{(engine.blank? ? '' : "#{engine.camelize}::")}#{col.camelize}").constantize.all, :id, :caption, { :include_blank => true, :selected => (begin params[:q]["#{col}_id_eq"].to_i rescue '' end) }, {:class => "form-control"}) 34 | end 35 | %> 36 |
    37 | <% end %> 38 |
    39 | <%= f.submit t(:create, :default => "Create"), :class => "btn btn-outline-secondary", :data => { :disable_with => t(:saving, :default => "Saving...") } %> 40 |
    41 | <% end %> 42 |
    43 |
    44 |
    45 |
    -------------------------------------------------------------------------------- /lib/generators/beautiful_storage_generator.rb: -------------------------------------------------------------------------------- 1 | # encoding : utf-8 2 | class BeautifulStorageGenerator < Rails::Generators::Base 3 | require_relative 'beautiful_scaffold_common_methods' 4 | include BeautifulScaffoldCommonMethods 5 | 6 | source_root File.expand_path('../templates', __FILE__) 7 | 8 | # TODO voir pour engine 9 | 10 | argument :model, :type => :string, :desc => "Name of model (ex: user)" 11 | argument :storage_name, :type => :string, :desc => "Storage's name (ex: picture_file)" 12 | 13 | class_option :mountable_engine, default: nil 14 | 15 | def install_storage 16 | 17 | #if !File.read('Gemfile').include?("image_processing") 18 | gem("image_processing", '~> 1.2') 19 | #end 20 | 21 | Bundler.with_unbundled_env do 22 | run "bundle install" 23 | end 24 | 25 | # Install activestorage 26 | run "bin/rails active_storage:install" 27 | #run "rake db:migrate" 28 | 29 | raise "Model must be specified" if model.blank? 30 | raise "Attachment must be specified" if storage_name.blank? 31 | 32 | # ===== Model 33 | inject_into_file("app/models/#{engine_name}#{model}.rb", 34 | "\n 35 | has_one_attached :#{storage_name} 36 | \n", after: "< ApplicationRecord") 37 | inject_into_file("app/models/#{engine_name}#{model}.rb", ":#{storage_name},", :after => "def self.permitted_attributes\n return ") 38 | 39 | # ====== Views 40 | inject_into_file("app/views/#{engine_name}#{model_pluralize}/_form.html.erb", 41 | "
    42 | <%= f.label :#{storage_name}, t('app.models.#{model}.bs_attributes.#{storage_name}', :default => '#{storage_name}').capitalize, :class => 'control-label' %>
    43 | <%= f.file_field :#{storage_name}, direct_upload: true, :class => 'form-control' %> 44 |
    \n", before: '') 45 | 46 | inject_into_file("app/views/#{engine_name}#{model_pluralize}/_form.html.erb", 47 | ", multipart: true", after: "form_for(@#{model}") 48 | 49 | inject_into_file("app/views/#{engine_name}#{model_pluralize}/show.html.erb", 50 | "

    <%= t('app.models.#{model}.bs_attributes.#{storage_name}', :default => '#{storage_name}') %>:
    <%= image_tag @#{model}.#{storage_name}.variant(resize_to_limit: [100, 100]) %>

    ", 51 | before: "") 52 | 53 | # Controller 54 | #inject_into_file("app/controllers/#{engine_name}#{model_pluralize}_controller.rb", 55 | # "\n before_action :require_login, except: [:dashboard]\n", 56 | # :after => 'layout "beautiful_layout"' + "\n") 57 | 58 | say "You must run 'rake db:migrate' to create activestorage migrations !" 59 | 60 | end 61 | end 62 | -------------------------------------------------------------------------------- /lib/generators/beautiful_jointable_generator.rb: -------------------------------------------------------------------------------- 1 | # encoding : utf-8 2 | class BeautifulJointableGenerator < Rails::Generators::Base 3 | require_relative 'beautiful_scaffold_common_methods' 4 | include BeautifulScaffoldCommonMethods 5 | 6 | source_root File.expand_path('../templates', __FILE__) 7 | 8 | argument :join_models, :type => :array, :default => [], :banner => "Two model names singular downcase (ex: product family)" 9 | 10 | class_option :mountable_engine, default: nil 11 | 12 | def create_join_table 13 | if join_models.length != 2 then 14 | say_status("Error", "Error need two singular models : example : user product", :red) 15 | else 16 | sorted_model = join_models.sort 17 | 18 | prefix_str = '' 19 | if engine_name.present? 20 | prefix_str = "#{engine_opt}_" 21 | end 22 | 23 | join_table_name = "#{prefix_str}#{sorted_model[0].pluralize}_#{sorted_model[1].pluralize}" 24 | 25 | # Generate migration 26 | migration_content = " 27 | create_table :#{join_table_name}, :id => false do |t| 28 | t.integer :#{sorted_model[0]}_id 29 | t.integer :#{sorted_model[1]}_id 30 | end 31 | 32 | add_index :#{join_table_name}, [:#{sorted_model[0]}_id, :#{sorted_model[1]}_id] 33 | " 34 | 35 | migration_name = "create_join_table_for_#{sorted_model[0]}_and_#{sorted_model[1]}" 36 | generate("migration", migration_name) 37 | 38 | filename = Dir.glob("db/migrate/*#{migration_name}.rb")[0] 39 | 40 | inject_into_file(filename, migration_content, :after => "def change") 41 | 42 | # Add habtm relation 43 | inject_into_file("app/models/#{engine_name}#{sorted_model[0]}.rb", "\n #{engine_name.present? ? ' ' : ''}has_and_belongs_to_many :#{sorted_model[1].pluralize}", :after => "ApplicationRecord") 44 | inject_into_file("app/models/#{engine_name}#{sorted_model[1]}.rb", "\n #{engine_name.present? ? ' ' : ''}has_and_belongs_to_many :#{sorted_model[0].pluralize}", :after => "ApplicationRecord") 45 | inject_into_file("app/models/#{engine_name}#{sorted_model[0]}.rb", "{ :#{sorted_model[1]}_ids => [] }, ", :after => /permitted_attributes#{regexp_an_string}return /) 46 | inject_into_file("app/models/#{engine_name}#{sorted_model[1]}.rb", "{ :#{sorted_model[0]}_ids => [] }, ", :after => /permitted_attributes#{regexp_an_string}return /) 47 | end 48 | end 49 | 50 | def add_habtm_field_in_forms 51 | models = join_models.sort 52 | 53 | 2.times do 54 | html = "<%= 55 | render :partial => 'layouts/#{engine_name}form_habtm_tag', :locals => { 56 | :model_class => @#{models[0]}, 57 | :model_name => '#{models[0]}', 58 | :plural_model_name => '#{models[0].pluralize}', 59 | :linked_model_name => '#{models[1]}', 60 | :plural_linked_model_name => '#{models[1].pluralize}', 61 | :namespace_bs => '', 62 | :engine_bs => '#{engine_opt}', 63 | :field_to_search_for_linked_model => 'name', 64 | :attr_to_show => 'caption', 65 | :f => f 66 | } %>" 67 | 68 | inject_into_file("app/views/#{engine_name}#{models[0].pluralize}/_form.html.erb", html, :before => "") 69 | models = models.reverse 70 | end 71 | end 72 | end 73 | -------------------------------------------------------------------------------- /lib/generators/beautiful_migration_generator.rb: -------------------------------------------------------------------------------- 1 | # encoding : utf-8 2 | class BeautifulMigrationGenerator < Rails::Generators::Base 3 | require_relative 'beautiful_scaffold_common_methods' 4 | include BeautifulScaffoldCommonMethods 5 | 6 | #include Rails::Generators::ResourceHelpers 7 | 8 | source_root File.expand_path('../templates', __FILE__) 9 | 10 | argument :name, type: :string, desc: "Name of the migration (in CamelCase) AddXxxTo[Engine]Yyy (Yyy must be plural)" 11 | argument :myattributes, type: :array, default: [], banner: "field:type field:type (for bt relation model:references)" 12 | 13 | class_option :namespace, default: nil 14 | class_option :donttouchgem, default: nil 15 | class_option :mountable_engine, default: nil 16 | 17 | def install_gems 18 | if options[:donttouchgem].blank? 19 | require_gems 20 | end 21 | end 22 | 23 | def add_field_for_fulltext 24 | @beautiful_attributes = myattributes.dup 25 | @fulltext_field = [] 26 | myattributes.each{ |attr| 27 | a,t = attr.split(':') 28 | if ['richtext', 'wysiwyg'].include?(t) 29 | # _typetext = {bbcode|html|text|wiki|textile|markdown} 30 | # _fulltext = text without any code 31 | @fulltext_field << [a + '_typetext', 'string'].join(':') 32 | @fulltext_field << [a + '_fulltext', 'text'].join(':') 33 | end 34 | } 35 | end 36 | 37 | def generate_model 38 | generate("migration", "#{name} #{beautiful_attr_to_rails_attr.join(' ')} #{@fulltext_field.join(' ')}") 39 | end 40 | 41 | def add_to_model 42 | add_relation 43 | end 44 | 45 | def generate_views 46 | commonpath = "app/views/#{engine_name}#{namespace_for_url}#{model_pluralize}/" 47 | 48 | # Form 49 | inject_into_file("#{commonpath}_form.html.erb", render_partial("app/views/partials/_form_field.html.erb"), :before => "\n" ) 50 | # Index 51 | inject_into_file("#{commonpath}index.html.erb", render_partial("app/views/partials/_index_batch.html.erb"), :before => "\n" ) 52 | inject_into_file("#{commonpath}index.html.erb", render_partial("app/views/partials/_index_header.html.erb"), :before => "\n" ) 53 | inject_into_file("#{commonpath}index.html.erb", render_partial("app/views/partials/_index_column.html.erb"), :before => "\n" ) 54 | inject_into_file("#{commonpath}index.html.erb", render_partial("app/views/partials/_index_search.html.erb"), :before => "\n" ) 55 | inject_into_file("#{commonpath}index.html.erb", myattributes.map{ |attr| a,t = attr.split(':');"'#{a}'" }.join(',') + ',', :after => ":model_columns => [" ) 56 | # Show 57 | inject_into_file("#{commonpath}show.html.erb", render_partial("app/views/partials/_show_field.html.erb"), :before => "\n" ) 58 | end 59 | 60 | private 61 | 62 | def model 63 | model_extracted = name.scan(/^Add(.*)To(.*)$/).flatten[1].underscore.singularize 64 | model_extracted = model_extracted.gsub("#{options[:mountable_engine].underscore}_",'') if !options[:mountable_engine].blank? 65 | return model_extracted 66 | end 67 | 68 | end 69 | -------------------------------------------------------------------------------- /test/lib/generators/beautiful_migration_generator_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | require 'generators/beautiful_scaffold_generator' 3 | require 'generators/beautiful_migration_generator' 4 | 5 | # In order to run test : in Beautiful-Scaffold dir just run : 6 | # rake test 7 | # 8 | # Source : 9 | # https://fossies.org/linux/rails/railties/test/generators/shared_generator_tests.rb 10 | # https://rossta.net/blog/testing-rails-generators.html 11 | 12 | class BeautifulMigrationGeneratorTest < Rails::Generators::TestCase 13 | tests BeautifulScaffoldGenerator 14 | destination Rails.root.join('../tmp/dummyappmigration') # test/dummy/tmp/generators/dummyapp.... 15 | 16 | setup do 17 | #puts "SETUP " * 100 18 | prepare_destination # Create tmp 19 | Dir.chdir(File.dirname(destination_root)) { # dans test/tmp 20 | system "rails new dummyappmigration --skip-test-unit --skip-spring --skip-bootsnap --skip-webpack-install --skip-javascript" 21 | } 22 | end 23 | 24 | # At the end of test 25 | teardown do 26 | Dir.chdir(File.dirname(destination_root)) { 27 | system 'rm -rf dummyappmigration' 28 | } 29 | end 30 | 31 | test "generator runs with relation" do 32 | self.class.generator_class = BeautifulScaffoldGenerator 33 | run_generator 'family label:string -s'.split(' ') 34 | run_generator 'user email:string birthday:datetime children:integer biography:text family:references -f'.split(' ') 35 | 36 | assert_file 'app/models/user.rb' 37 | assert_file 'app/models/family.rb' 38 | 39 | assert_file 'app/models/user.rb', /belongs_to :family/ 40 | assert_file 'app/models/family.rb', /has_many :users/ 41 | 42 | assert_file 'app/models/user.rb' do |content| 43 | assert_match('self.permitted_attributes', content) 44 | end 45 | assert_file 'app/controllers/users_controller.rb' do |content| 46 | assert_match("session['fields']['user'] ||= (User.columns.map(&:name) - [\"id\"])[0..4]", content) 47 | end 48 | 49 | assert_file 'app/views/users/_form.html.erb' do |content| 50 | # Input family_id (foreign-key) 51 | assert_match('<%= f.collection_select :family_id, Family.all, :id, :caption, { :include_blank => true }, { :class => "form-control" } %>', content) 52 | # Label biography 53 | assert_match("<%= f.label :biography, t('app.models.user.bs_attributes.biography', :default => 'biography').capitalize, :class => \"control-label\" %>", content) 54 | # Input date (day) 55 | assert_match('<%= f.hidden_field("birthday(#{i+1}i)", value: @user.birthday&.send(meth), id: "user_birthday_input_#{i+1}i") %>', content) 56 | end 57 | 58 | ############################### 59 | # Migration with reference 60 | ############################### 61 | 62 | self.class.generator_class = BeautifulScaffoldGenerator 63 | run_generator 'sex label:string -f'.split(' ') 64 | self.class.generator_class = BeautifulMigrationGenerator 65 | run_generator 'AddSexToUsers sex:references -f'.split(' ') 66 | 67 | assert_file 'app/models/user.rb', /belongs_to :sex/ 68 | assert_file 'app/models/sex.rb', /has_many :users, :dependent => :nullify/ 69 | 70 | assert_file 'app/views/users/_form.html.erb' do |content| 71 | # Input family_id (foreign-key) 72 | assert_match('<%= f.collection_select :sex_id, Sex.all, :id, :caption, { :include_blank => true }, { :class => "form-control" } %>', content) 73 | end 74 | 75 | end 76 | end 77 | -------------------------------------------------------------------------------- /lib/generators/templates/app/views/layout.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Beautiful Scaffold 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | <%%= stylesheet_link_tag "<%= engine_name %>application-bs" %> 16 | <%%= javascript_include_tag "<%= engine_name %>application-bs" %> 17 | 18 | 19 | 20 | 21 | <%%= csrf_meta_tags %> 22 | <%%= yield :head %> 23 | 24 | 25 | 43 |
    44 |
    45 |
    46 | <%% if !flash[:notice].blank? %> 47 |
    48 | × 49 |

    Info :

    50 | <%%= flash[:notice] %> 51 |
    52 | <%% end %> 53 | <%% if !flash[:alert].blank? %> 54 |
    55 | × 56 |

    Warning :

    57 | <%%= flash[:alert] %> 58 |
    59 | <%% end %> 60 | <%% if !flash[:error].blank? %> 61 |
    62 | × 63 |

    Error :

    64 | <%%= flash[:error] %> 65 |
    66 | <%% end %> 67 |
    68 | <%%= yield %> 69 |
    70 |
    71 |
    72 |
    73 | 74 | 78 | 79 | 80 | 81 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /lib/generators/templates/app/controllers/master_base.rb: -------------------------------------------------------------------------------- 1 | # encoding : utf-8 2 | class BeautifulController < ApplicationController 3 | 4 | layout "beautiful_layout" 5 | 6 | # That clear cookie to avoid cookie overflow 7 | # if you want to keep all in memory and you use ARCookieStore, just comment next line 8 | before_action :delete_session_for_others_models_scaffold 9 | 10 | def dashboard 11 | render :layout => "beautiful_layout" 12 | end 13 | 14 | def delete_session_for_others_models_scaffold 15 | current_model = params[:controller].split('/').last.singularize 16 | 17 | ['fields','sorting','search','scope','paginate'].each{ |k| 18 | session[k] = session[k].delete_if {|key, v| key != current_model } if not session[k].blank? 19 | } 20 | end 21 | 22 | # Call in AJAX 23 | def select_fields 24 | model_sym = params[:model_sym] 25 | 26 | do_select_fields(model_sym.to_s) 27 | 28 | head :ok 29 | end 30 | 31 | def do_select_fields(model_str) 32 | # Fields 33 | session['fields'] ||= {} 34 | session['fields'][model_str] ||= nil 35 | params[:fields] ||= session['fields'][model_str] 36 | session['fields'][model_str] = params[:fields] 37 | end 38 | 39 | def do_sort_and_paginate(model_str) 40 | # Sort 41 | session['sorting'] ||= {} 42 | session['sorting'][model_str] ||= { 'attribute' => "id", 'sorting' => "DESC" } 43 | params[:sorting] ||= session['sorting'][model_str] 44 | session['sorting'][model_str] = params[:sorting] 45 | 46 | # Search and Filter 47 | session['search'] ||= {} 48 | session['search'][model_str] = nil if not params[:nosearch].blank? 49 | params[:page] = 1 if not params[:q].nil? 50 | params[:q] ||= session['search'][model_str] 51 | session['search'][model_str] = params[:q] if params[:skip_save_search].blank? 52 | 53 | # Scope 54 | session['scope'] ||= {} 55 | session['scope'][model_str] ||= nil 56 | params[:page] = 1 if not params[:scope].nil? 57 | params[:scope] ||= session['scope'][model_str] 58 | session['scope'][model_str] = params[:scope] 59 | 60 | # Paginate 61 | session['paginate'] ||= {} 62 | session['paginate'][model_str] ||= nil 63 | params[:page] ||= session['paginate'][model_str] 64 | session['paginate'][model_str] = params[:page] 65 | end 66 | 67 | def boolean(string) 68 | return true if string == true || string =~ (/(true|t|yes|y|1)$/i) 69 | return false if string == false || string.nil? || string =~ (/(false|f|no|n|0)$/i) 70 | raise ArgumentError.new("invalid value for Boolean: \"#{string}\"") 71 | end 72 | 73 | def update_treeview(modelclass, foreignkey) 74 | parent_id = (params[foreignkey].to_i == 0 ? nil : params[foreignkey].to_i) 75 | index = params[:position].to_i 76 | 77 | elt = modelclass.find(params[:id]) 78 | elt.attributes = { foreignkey => parent_id } 79 | 80 | if modelclass.column_names.include?("position") 81 | new_pos = 0 82 | modelclass.transaction do 83 | all_elt = modelclass.where(foreignkey => parent_id).order("position ASC").to_a 84 | 85 | if index == 0 86 | new_pos = (begin (all_elt.first.position - 1) rescue 1 end) 87 | elsif index == (all_elt.length - 1) 88 | new_pos = (begin (all_elt.last.position + 1) rescue 1 end) 89 | else 90 | new_pos = all_elt[index].position 91 | 92 | end_of_array = all_elt[index..-1] 93 | end_of_array.each do |g| 94 | next if g == elt 95 | g.position = g.position.to_i + 1 96 | g.save! 97 | 98 | next_elt = end_of_array[end_of_array.index(g) + 1] 99 | break if !next_elt.nil? && next_elt.position > g.position 100 | end 101 | end 102 | end 103 | elt.position = new_pos 104 | end 105 | return elt.save! 106 | end 107 | end 108 | -------------------------------------------------------------------------------- /test/lib/generators/beautiful_login_logout_generator_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | require 'generators/beautiful_scaffold_generator' 3 | require 'generators/beautiful_sorcery_generator' 4 | require 'generators/beautiful_cancancan_generator' 5 | 6 | # In order to run test : in Beautiful-Scaffold dir just run : 7 | # rake test 8 | # 9 | # Source : 10 | # https://fossies.org/linux/rails/railties/test/generators/shared_generator_tests.rb 11 | # https://rossta.net/blog/testing-rails-generators.html 12 | 13 | class BeautifulLoginLogoutGeneratorTest < Rails::Generators::TestCase 14 | tests BeautifulScaffoldGenerator 15 | destination Rails.root.join('../tmp/dummyappsorcery') # test/dummy/tmp/generators/dummyapp.... 16 | 17 | setup do 18 | #puts "SETUP " * 100 19 | prepare_destination # Create tmp 20 | Dir.chdir(File.dirname(destination_root)) { # dans test/tmp 21 | system "rails new dummyappsorcery --skip-test-unit --skip-spring --skip-bootsnap --skip-webpack-install --skip-javascript" 22 | } 23 | end 24 | 25 | # At the end of test 26 | teardown do 27 | Dir.chdir(File.dirname(destination_root)) { 28 | system 'rm -rf dummyappsorcery' 29 | } 30 | end 31 | 32 | test "generator sorcery cancancan" do 33 | ####### 34 | # App 35 | ####### 36 | 37 | self.class.generator_class = BeautifulScaffoldGenerator 38 | run_generator 'family label:string -s'.split(' ') 39 | assert_file 'app/models/family.rb' 40 | 41 | run_generator 'user email:string -s'.split(' ') 42 | assert_file 'app/models/user.rb' 43 | 44 | ####### 45 | # Sorcery 46 | ####### 47 | 48 | self.class.generator_class = BeautifulSorceryGenerator 49 | run_generator [] 50 | 51 | assert_file 'app/models/user.rb' do |content| 52 | assert_match('authenticates_with_sorcery!', content) 53 | end 54 | assert_file 'app/controllers/users_controller.rb' do |content| 55 | assert_match("def activate", content) 56 | end 57 | assert_file 'app/controllers/user_sessions_controller.rb' do |content| 58 | assert_match("login(params[:email], params[:password])", content) 59 | end 60 | 61 | assert_file 'app/views/user_mailer/activation_needed_email.fr.html.erb' 62 | assert_file 'app/views/user_mailer/activation_needed_email.fr.text.erb' 63 | assert_file 'app/views/user_mailer/activation_success_email.fr.html.erb' 64 | assert_file 'app/views/user_mailer/activation_success_email.fr.text.erb' 65 | 66 | assert_file 'app/views/user_mailer/activation_needed_email.en.html.erb' 67 | assert_file 'app/views/user_mailer/activation_needed_email.en.text.erb' 68 | assert_file 'app/views/user_mailer/activation_success_email.en.html.erb' 69 | assert_file 'app/views/user_mailer/activation_success_email.en.text.erb' 70 | 71 | assert_file 'app/views/user_sessions/_form.html.erb' 72 | assert_file 'app/views/user_sessions/new.html.erb' 73 | 74 | assert_file 'app/views/users/_form.html.erb' do |content| 75 | assert_match('<%= f.label :password, t(\'app.models.user.bs_attributes.password\', :default => \'password\').capitalize, :class => "control-label" %>', content) 76 | assert_match('<%= f.password_field :password, :class => "form-control" %>', content) 77 | assert_match('<%= f.label :password_confirmation, t(\'app.models.user.bs_attributes.password_confirmation\', :default => \'password_confirmation\').capitalize, :class => "control-label" %>', content) 78 | assert_match('<%= f.password_field :password_confirmation, :class => "form-control" %>', content) 79 | end 80 | 81 | assert_file 'app/views/layouts/beautiful_layout.html.erb' do |content| 82 | assert_match("<%= render :partial => 'layouts/login_logout_register' %>", content) 83 | end 84 | 85 | assert_file 'config/initializers/sorcery.rb' 86 | assert_file 'config/initializers/sorcery.rb' do |content| 87 | assert_match("user.user_activation_mailer = UserMailer", content) 88 | end 89 | 90 | ####### 91 | # Cancancan 92 | ####### 93 | 94 | self.class.generator_class = BeautifulCancancanGenerator 95 | run_generator [] 96 | 97 | assert_file 'app/models/ability.rb' 98 | 99 | assert_file "app/controllers/application_controller.rb" do |content| 100 | assert_match("rescue_from CanCan::AccessDenied do |exception|", content) 101 | end 102 | end 103 | end 104 | -------------------------------------------------------------------------------- /README.rdoc: -------------------------------------------------------------------------------- 1 | = Beautiful Scaffold 2 | 3 | Beautiful Scaffold is a gem which propose generators for a complete scaffold with paginate, sort and filter. 4 | Fully customizable. 5 | 6 | Note : Avoid to change Beautiful-Scaffold version in your project (incompatibility between generated code). 7 | Note 2 : Be careful to have a clean git repository for your project because Beautiful-Scaffold change many files. 8 | It will be easier to revert changes. 9 | 10 | Info : https://github.com/rivsc/Beautiful-Scaffold 11 | Demo : http://beautiful-scaffold.rivsc.ovh/ (Soon disabled : heroku stops its free plan) 12 | 13 | == Install 14 | 15 | Add this in the Gemfile of your rails app or engine : 16 | gem 'beautiful_scaffold' 17 | 18 | === Next 19 | 20 | And run 21 | 22 | bundle install 23 | 24 | == Usage 25 | 26 | === Scaffold 27 | 28 | # model : underscore and singular 29 | # mountable_engine : underscore 30 | # namespace : underscore 31 | rails generate beautiful_scaffold model attr:type attr:type... [--namespace=name] [--donttouchgem=yes] [--mountable_engine=name] 32 | 33 | Types available: 34 | * integer 35 | * float 36 | * text 37 | * string 38 | * price 39 | * color 40 | * wysiwyg 41 | 42 | (See below #label-Barcodes) 43 | 44 | # Example : products 45 | 46 | rails g beautiful_scaffold product name:string price:price tva:float description:wysiwyg visible:boolean && rake db:migrate 47 | 48 | # Example : admin products 49 | 50 | rails g beautiful_scaffold product name:string price:price tva:float description:wysiwyg overview_description:wysiwyg visible:boolean --namespace=admin && rake db:migrate 51 | 52 | # Example (for an engine) : 53 | # You need to add beautiful-scaffold to the gemfile of the engine (not the gemspec !). 54 | 55 | rails g beautiful_scaffold user code:string --mountable-engine=faq 56 | rails g beautiful_scaffold question title:string description:wysiwyg user:references resolved:boolean --mountable-engine=faq 57 | rails g beautiful_scaffold answer description:wysiwyg user:references up:integer down:integer --mountable-engine=faq 58 | rails g beautiful_migration AddPositionToFaqAnswers position:integer --mountable-engine=faq 59 | rails g beautiful_jointable answer tag --mountable-engine=faq 60 | 61 | === Migration (Use Add[Field]To[ModelPluralize] syntax) 62 | 63 | rails g beautiful_migration AddFieldToModels field:type 64 | 65 | === Locale (i18n) (Example) 66 | 67 | Run `rake db:migrate` before `rails g beautiful_locale` (to get lastest attribute translation) 68 | 69 | rails g beautiful_locale all 70 | rails g beautiful_locale en 71 | rails g beautiful_locale fr 72 | rails g beautiful_locale de 73 | 74 | === Join Table (has_and_belongs_to_many relation) 75 | 76 | rails g beautiful_jointable model1 model2 77 | 78 | === Install et preconfigure Sorcery (authentification) (this generator doesn't work on engine) 79 | 80 | # If it is not done yet 81 | rails g beautiful_scaffold user email:string 82 | rails g beautiful_sorcery 83 | 84 | === Install et preconfigure Cancancan (authorization) (this generator doesn't work on engine) 85 | 86 | # If it is not done yet 87 | rails g beautiful_scaffold user email:string 88 | # If it is not done yet 89 | rails g beautiful_sorcery 90 | rails g beautiful_cancancan 91 | 92 | === Storage (ActiveStorage) 93 | 94 | rails g beautiful_storage model attachment_field 95 | 96 | === In views 97 | 98 | ==== Barcodes 99 | 100 | Set code like this : 101 | 102 | 103 | 104 | data-type-barcode can be : 105 | 106 | codabar 107 | code11 (code 11) 108 | code39 (code 39) 109 | code93 (code 93) 110 | code128 (code 128) 111 | ean8 (ean 8) 112 | ean13 (ean 13) 113 | std25 (standard 2 of 5 - industrial 2 of 5) 114 | int25 (interleaved 2 of 5) 115 | msi 116 | datamatrix (ASCII + extended) 117 | 118 | ==== Driverjs (overlay instructions) 119 | 120 | Example : when you click on the #bs-help tag presentation tour display on screen ! 121 | 122 | If you want to add 'slide' : 123 | 124 | For add instruction to DOM element, add this to your markup : 125 | 126 | id="myunique-id-in-the-page" data-present-title="Title for the slide" data-present-description="Short description" data-present-order="1" 127 | 128 | And Beautiful-Scaffold does the job ! 129 | 130 | -------------------------------------------------------------------------------- /test/lib/generators/beautiful_scaffold_engine_generator_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | require 'generators/beautiful_scaffold_generator' 3 | 4 | class BeautifulScaffoldEngineGeneratorTest < Rails::Generators::TestCase 5 | tests BeautifulScaffoldGenerator 6 | destination Rails.root.join('../tmp/dummyengineapp') # du coup test/tmp/generators/dummyapp.... 7 | 8 | setup do 9 | prepare_destination # Create tmp 10 | Dir.chdir(File.dirname(destination_root)) { # dans test/tmp 11 | system "rails plugin new dummyengineapp --mountable --skip-test-unit --skip-spring --skip-bootsnap --skip-webpack-install --skip-javascript" 12 | } 13 | end 14 | 15 | # At the end of test 16 | teardown do 17 | Dir.chdir(File.dirname(destination_root)) { 18 | system 'rm -rf dummyengineapp' 19 | } 20 | end 21 | 22 | test "generator runs with relation" do 23 | run_generator 'family label:string --mountable-engine=dummyengineapp -s'.split(' ') 24 | run_generator 'user email:string birthday:datetime children:integer biography:text family:references --mountable-engine=dummyengineapp -f'.split(' ') 25 | 26 | ########### 27 | # Models 28 | ########### 29 | 30 | assert_file 'app/models/dummyengineapp/user.rb' 31 | assert_file 'app/models/dummyengineapp/family.rb' 32 | 33 | assert_file 'app/models/dummyengineapp/user.rb', /belongs_to :family/ 34 | assert_file 'app/models/dummyengineapp/family.rb', /has_many :users/ 35 | 36 | assert_file 'app/models/dummyengineapp/user.rb' do |content| 37 | assert_match('self.permitted_attributes', content) 38 | end 39 | 40 | assert_file 'app/models/dummyengineapp/user.rb' do |content| 41 | assert_match('self.permitted_attributes', content) 42 | end 43 | 44 | ############# 45 | # Concerns 46 | ############# 47 | 48 | assert_file 'app/models/concerns/dummyengineapp/caption_concern.rb' 49 | assert_file 'app/models/concerns/dummyengineapp/default_sorting_concern.rb' 50 | assert_file 'app/models/concerns/dummyengineapp/fulltext_concern.rb' 51 | 52 | assert_file 'app/models/concerns/dummyengineapp/caption_concern.rb' do |content| 53 | assert_match('module Dummyengineapp', content) 54 | assert_match('end #endofmodule', content) 55 | end 56 | assert_file 'app/models/concerns/dummyengineapp/default_sorting_concern.rb' do |content| 57 | assert_match('module Dummyengineapp', content) 58 | assert_match('end #endofmodule', content) 59 | end 60 | assert_file 'app/models/concerns/dummyengineapp/fulltext_concern.rb' do |content| 61 | assert_match('module Dummyengineapp', content) 62 | assert_match('end #endofmodule', content) 63 | end 64 | 65 | ############### 66 | # Views 67 | ############### 68 | assert_file 'app/views/dummyengineapp/beautiful/dashboard.html.erb' 69 | assert_file 'app/views/layouts/dummyengineapp/_beautiful_menu.html.erb' 70 | assert_file 'app/views/layouts/dummyengineapp/_form_habtm_tag.html.erb' 71 | assert_file 'app/views/layouts/dummyengineapp/_mass_inserting.html.erb' 72 | assert_file 'app/views/layouts/dummyengineapp/_modal_columns.html.erb' 73 | assert_file 'app/views/layouts/dummyengineapp/beautiful_layout.html.erb' 74 | 75 | assert_file 'app/controllers/dummyengineapp/users_controller.rb' do |content| 76 | assert_match("session['fields']['user'] ||= (User.columns.map(&:name) - [\"id\"])[0..4]", content) 77 | end 78 | 79 | assert_file 'app/views/dummyengineapp/users/index.html.erb' do |content| 80 | assert_match("Dummyengineapp::User.columns", content) 81 | end 82 | 83 | assert_file 'app/views/dummyengineapp/users/index.html.erb' do |content| 84 | assert_match("Dummyengineapp::User.columns", content) 85 | end 86 | 87 | assert_file 'app/views/dummyengineapp/families/index.html.erb' do |content| 88 | assert_match('render :partial => "layouts/dummyengineapp/mass_inserting"', content) 89 | assert_match('render :partial => "layouts/dummyengineapp/modal_columns", :locals => { :engine_name => \'dummyengineapp\'', content) 90 | assert_match('<% if Dummyengineapp::Family.columns.map(&:name).include?("family_id") %>', content) 91 | assert_match('', content) 92 | assert_match('', content) 93 | end 94 | 95 | assert_file 'app/views/dummyengineapp/families/treeview.html.erb' do |content| 96 | assert_match('Dummyengineapp::Family.select(:id).all', content) 97 | end 98 | 99 | assert_file 'app/views/dummyengineapp/users/_form.html.erb' do |content| 100 | assert_match('<%= f.collection_select :family_id, Dummyengineapp::Family.all, :id, :caption, { :include_blank => true }, { :class => "form-control" } %>', content) 101 | end 102 | end 103 | end 104 | -------------------------------------------------------------------------------- /lib/generators/templates/app/views/partials/_form_field.html.erb: -------------------------------------------------------------------------------- 1 | <%- attributes.each do |attribute| -%> 2 | <%- if @beautiful_attributes.include?(attribute.name + ':wysiwyg') -%> 3 |
    4 | <%%= f.label :<%= attribute.name %>, <%= i18n_t_a(singular_table_name, attribute.name) %>.capitalize, :class => "control-label" %> 5 | <%%= f.text_area :<%= attribute.name %>, :class => "wysiwyg-editor form-control" %> 6 |
    7 | <%%= f.hidden_field :<%= attribute.name %>_typetext, :value => "html" %> 8 | <%- elsif @beautiful_attributes.include?(attribute.name + ':references') -%> 9 |
    10 | <%%= f.label :<%= attribute.name %>, <%= i18n_t_a(singular_table_name, attribute.name) %>.capitalize, :class => "control-label" %> 11 | <%%= f.collection_select :<%= attribute.name %>_id, <%= engine_camel.present? ? "#{engine_camel}::" : '' %><%= attribute.name.camelcase %>.all, :id, :caption, { :include_blank => true }, { :class => "form-control" } %> 12 |
    13 | <%- elsif @beautiful_attributes.include?(attribute.name + ':price') -%> 14 |
    15 | <%%= f.label :<%= attribute.name %>, <%= i18n_t_a(singular_table_name, attribute.name) %>.capitalize, :class => "control-label" %> 16 |
    17 | $<%%= f.<%= attribute.field_type %> :<%= attribute.name %>, :class => "form-control" %> 18 |
    19 |
    20 | <%- elsif (datetime_field = @beautiful_attributes.include?(attribute.name + ':datetime')) || @beautiful_attributes.include?(attribute.name + ':date') -%> 21 |
    22 | <%%= f.label :<%= attribute.name %>, <%= i18n_t_a(singular_table_name, attribute.name) %>.capitalize, :class => "control-label" %> 23 | <% if datetime_field %> 24 |
    25 | <% end %> 26 |
    27 | <%%= f.text_field :<%= attribute.name %>, :value => (begin @<%= singular_table_name %>.<%= attribute.name %>.strftime("%d/%m/%Y") rescue "" end), 28 | :class => "form-control datetimepicker-input", 29 | "data-target" => "#container_<%= singular_table_name %>_<%= attribute.name %>_dp", 30 | "data-id" => "<%= singular_table_name %>_<%= attribute.name %>_input_", id: nil %> 31 |
    32 | 33 |
    34 |
    35 | <% if datetime_field %> 36 |
    37 | <%%= f.text_field :<%= attribute.name %>, :value => (begin @<%= singular_table_name %>.<%= attribute.name %>.strftime("%H:%M") rescue "" end), 38 | :class => "form-control datetimepicker-input", 39 | "data-target" => "#container_<%= singular_table_name %>_<%= attribute.name %>_tp", 40 | "data-id" => "<%= singular_table_name %>_<%= attribute.name %>_input_", id: nil %> 41 |
    42 | 43 |
    44 |
    45 | <% end %> 46 | <%% ["year","mon","day" <%= datetime_field ? ',"min","hour"' : '' %>].each_with_index do |meth, i| %> 47 | <%%= f.hidden_field("<%= attribute.name %>(#{i+1}i)", value: @<%= singular_table_name %>.<%= attribute.name %>&.send(meth), id: "<%= singular_table_name %>_<%= attribute.name %>_input_#{i+1}i") %> 48 | <%% end %> 49 | <% if datetime_field %> 50 |
    51 | <% end %> 52 |
    53 | <%- elsif @beautiful_attributes.include?(attribute.name + ':color') -%> 54 |
    55 | <%%= f.label :<%= attribute.name %>, <%= i18n_t_a(singular_table_name, attribute.name) %>.capitalize, :class => "control-label" %> 56 |
    57 | <%%= f.color_field :<%= attribute.name %>, :class => "form-control" %> 58 |
    59 |
    60 | <%- else -%> 61 |
    62 | <%%= f.label :<%= attribute.name %>, <%= i18n_t_a(singular_table_name, attribute.name) %>.capitalize, :class => "control-label" %> 63 | <%%= f.<%= attribute.field_type %> :<%= attribute.name %><%= ', :rows => 5' if attribute.field_type == :text_area %>, :class => "form-control" %> 64 |
    65 | <%- end -%> 66 | <%- end -%> 67 | -------------------------------------------------------------------------------- /lib/generators/templates/app/assets/stylesheets/beautiful-scaffold.css.scss: -------------------------------------------------------------------------------- 1 | .ar, table td.ar, table th.ar{ 2 | text-align:right; 3 | } 4 | .al, table td.al, table th.al{ 5 | text-align:left; 6 | } 7 | .ac, table td.ac, table th.ac{ 8 | text-align:center; 9 | } 10 | .ab, table td.ab, table th.ab{ 11 | vertical-align:bottom; 12 | } 13 | .am, table td.am, table th.am{ 14 | vertical-align:middle; 15 | } 16 | .at, table td.at, table th.at{ 17 | vertical-align:top; 18 | } 19 | 20 | $border-spinner:#0088CC; 21 | $shadow-spinner:#0060CC; 22 | 23 | /* http://www.alessioatzeni.com/blog/css3-loading-animation-loop/ */ 24 | .loader{ 25 | position:fixed; 26 | top:50%; 27 | left:50%; 28 | width:70px; 29 | height:70px; 30 | z-index:1000; 31 | } 32 | .circle { 33 | background-color: rgba(0,0,0,0); 34 | border:5px solid $border-spinner; 35 | opacity:.9; 36 | border-right:5px solid rgba(0,0,0,0); 37 | border-left:5px solid rgba(0,0,0,0); 38 | border-radius:50px; 39 | box-shadow: 0 0 35px $shadow-spinner; 40 | width:50px; 41 | height:50px; 42 | margin:0 auto; 43 | -moz-animation:spinPulse 1s infinite ease-in-out; 44 | -webkit-animation:spinPulse 1s infinite linear; 45 | } 46 | .circle1 { 47 | background-color: rgba(0,0,0,0); 48 | border:5px solid $border-spinner; 49 | opacity:.9; 50 | border-left:5px solid rgba(0,0,0,0); 51 | border-right:5px solid rgba(0,0,0,0); 52 | border-radius:50px; 53 | box-shadow: 0 0 15px $shadow-spinner; 54 | width:30px; 55 | height:30px; 56 | margin:0 auto; 57 | position:relative; 58 | top:-40px; 59 | -moz-animation:spinoffPulse 1s infinite linear; 60 | -webkit-animation:spinoffPulse 1s infinite linear; 61 | } 62 | @-ms-keyframes spinPulse { 63 | 0% { -ms-transform:rotate(160deg); opacity:0; box-shadow:0 0 1px $shadow-spinner; } 64 | 50% { -ms-transform:rotate(145deg); opacity:1;} 65 | 100% { -ms-transform:rotate(-320deg); opacity:0; } 66 | } 67 | @-ms-keyframes spinoffPulse { 68 | 0% { -ms-transform:rotate(0deg); } 69 | 100% { -ms-transform:rotate(360deg); } 70 | } 71 | @-o-keyframes spinPulse { 72 | 0% { -o-transform:rotate(160deg); opacity:0; box-shadow:0 0 1px $shadow-spinner; } 73 | 50% { -o-transform:rotate(145deg); opacity:1;} 74 | 100% { -o-transform:rotate(-320deg); opacity:0; } 75 | } 76 | @-o-keyframes spinoffPulse { 77 | 0% { -o-transform:rotate(0deg); } 78 | 100% { -o-transform:rotate(360deg); } 79 | } 80 | @-moz-keyframes spinPulse { 81 | 0% { -moz-transform:rotate(160deg); opacity:0; box-shadow:0 0 1px $shadow-spinner;} 82 | 50% { -moz-transform:rotate(145deg); opacity:1; } 83 | 100% { -moz-transform:rotate(-320deg); opacity:0; } 84 | } 85 | @-moz-keyframes spinoffPulse { 86 | 0% { -moz-transform:rotate(0deg); } 87 | 100% { -moz-transform:rotate(360deg); } 88 | } 89 | @-webkit-keyframes spinPulse { 90 | 0% { -webkit-transform:rotate(160deg); opacity:0; box-shadow:0 0 1px $shadow-spinner; } 91 | 50% { -webkit-transform:rotate(145deg); opacity:1;} 92 | 100% { -webkit-transform:rotate(-320deg); opacity:0; } 93 | } 94 | @-webkit-keyframes spinoffPulse { 95 | 0% { -webkit-transform:rotate(0deg); } 96 | 100% { -webkit-transform:rotate(360deg); } 97 | } 98 | label.bs-label-ib{ 99 | display:inline-block; 100 | padding-left:4px; 101 | padding-right:4px; 102 | } 103 | label.bs-label-ib input{ 104 | margin-right:3px; 105 | margin-top:0; 106 | } 107 | 108 | /* Fixed left menu */ 109 | /* 110 | .fixed { 111 | width: 200px; 112 | float: left; 113 | } 114 | .fixed + div { 115 | margin-left: 200px; 116 | padding-left:20px; 117 | overflow: hidden; 118 | min-height:500px; 119 | background:white; 120 | } 121 | body { 122 | padding-top:70px; 123 | } 124 | @media (max-width: 979px) { 125 | .fixed + div { 126 | margin-left: 0; 127 | padding:10px; 128 | } 129 | body { 130 | padding-top: 35px; 131 | } 132 | } 133 | */ 134 | html, body { 135 | height: 100%; 136 | } 137 | /* 138 | .filler:after{ 139 | background-color:inherit; 140 | bottom: 0; 141 | content: ""; 142 | height: auto; 143 | min-height: 100%; 144 | left: 0; 145 | margin:inherit; 146 | right: 0; 147 | position: absolute; 148 | top: 0; 149 | width: inherit; 150 | z-index: -1; 151 | } 152 | */ 153 | 154 | #jstree-marker-line {pointer-events: none;} 155 | 156 | /* Search & filter */ 157 | .search-and-filter div.panel-group{ 158 | margin-bottom:0; 159 | } 160 | .search-and-filter div.panel-group .accordion-toggle{ 161 | padding-left:0; 162 | } 163 | .search-and-filter div.panel-group .panel.panel-default{ 164 | border:0; 165 | } 166 | .search-and-filter div.panel-group .panel-body{ 167 | padding:0; 168 | margin-top:0; 169 | } 170 | .search-and-filter h3{ 171 | margin-top:0; 172 | } 173 | 174 | #modal-columns div.modal-body label{ 175 | display: inline-block; 176 | margin-top: 0px; 177 | margin-bottom: 0px; 178 | margin-left: 20px; 179 | } 180 | /* 181 | .search-and-filter .form-group label{ 182 | margin-bottom: 0; 183 | margin-top: 5px; 184 | } 185 | */ 186 | 187 | /* 188 | div.filler.show-menu{ 189 | margin-left: 200px; 190 | } 191 | div.fixed.show-menu{ 192 | display: block; 193 | } 194 | div.filler.hide-menu{ 195 | margin-left: 0; 196 | } 197 | div.fixed.hide-menu{ 198 | display: none !important; 199 | } 200 | */ 201 | 202 | .title-index{ 203 | font-size: 30px; 204 | } 205 | 206 | .overview-color{ 207 | display:inline-block; 208 | width: 40px; 209 | height: 24px; 210 | } 211 | -------------------------------------------------------------------------------- /test/dummy/config/environments/production.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | 4 | # Code is not reloaded between requests. 5 | config.cache_classes = true 6 | 7 | # Eager load code on boot. This eager loads most of Rails and 8 | # your application in memory, allowing both threaded web servers 9 | # and those relying on copy on write to perform better. 10 | # Rake tasks automatically ignore this option for performance. 11 | config.eager_load = true 12 | 13 | # Full error reports are disabled and caching is turned on. 14 | config.consider_all_requests_local = false 15 | config.action_controller.perform_caching = true 16 | 17 | # Ensures that a master key has been made available in either ENV["RAILS_MASTER_KEY"] 18 | # or in config/master.key. This key is used to decrypt credentials (and other encrypted files). 19 | # config.require_master_key = true 20 | 21 | # Disable serving static files from the `/public` folder by default since 22 | # Apache or NGINX already handles this. 23 | config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present? 24 | 25 | # Compress CSS using a preprocessor. 26 | # config.assets.css_compressor = :sass 27 | 28 | # Do not fallback to assets pipeline if a precompiled asset is missed. 29 | config.assets.compile = false 30 | 31 | # Enable serving of images, stylesheets, and JavaScripts from an asset server. 32 | # config.action_controller.asset_host = 'http://assets.example.com' 33 | 34 | # Specifies the header that your server uses for sending files. 35 | # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache 36 | # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX 37 | 38 | # Store uploaded files on the local file system (see config/storage.yml for options). 39 | config.active_storage.service = :local 40 | 41 | # Mount Action Cable outside main process or domain. 42 | # config.action_cable.mount_path = nil 43 | # config.action_cable.url = 'wss://example.com/cable' 44 | # config.action_cable.allowed_request_origins = [ 'http://example.com', /http:\/\/example.*/ ] 45 | 46 | # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. 47 | # config.force_ssl = true 48 | 49 | # Use the lowest log level to ensure availability of diagnostic information 50 | # when problems arise. 51 | config.log_level = :debug 52 | 53 | # Prepend all log lines with the following tags. 54 | config.log_tags = [ :request_id ] 55 | 56 | # Use a different cache store in production. 57 | # config.cache_store = :mem_cache_store 58 | 59 | # Use a real queuing backend for Active Job (and separate queues per environment). 60 | # config.active_job.queue_adapter = :resque 61 | # config.active_job.queue_name_prefix = "dummy_production" 62 | 63 | config.action_mailer.perform_caching = false 64 | 65 | # Ignore bad email addresses and do not raise email delivery errors. 66 | # Set this to true and configure the email server for immediate delivery to raise delivery errors. 67 | # config.action_mailer.raise_delivery_errors = false 68 | 69 | # Enable locale fallbacks for I18n (makes lookups for any locale fall back to 70 | # the I18n.default_locale when a translation cannot be found). 71 | config.i18n.fallbacks = true 72 | 73 | # Send deprecation notices to registered listeners. 74 | config.active_support.deprecation = :notify 75 | 76 | # Use default logging formatter so that PID and timestamp are not suppressed. 77 | config.log_formatter = ::Logger::Formatter.new 78 | 79 | # Use a different logger for distributed setups. 80 | # require 'syslog/logger' 81 | # config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new 'app-name') 82 | 83 | if ENV["RAILS_LOG_TO_STDOUT"].present? 84 | logger = ActiveSupport::Logger.new(STDOUT) 85 | logger.formatter = config.log_formatter 86 | config.logger = ActiveSupport::TaggedLogging.new(logger) 87 | end 88 | 89 | # Do not dump schema after migrations. 90 | config.active_record.dump_schema_after_migration = false 91 | 92 | # Inserts middleware to perform automatic connection switching. 93 | # The `database_selector` hash is used to pass options to the DatabaseSelector 94 | # middleware. The `delay` is used to determine how long to wait after a write 95 | # to send a subsequent read to the primary. 96 | # 97 | # The `database_resolver` class is used by the middleware to determine which 98 | # database is appropriate to use based on the time delay. 99 | # 100 | # The `database_resolver_context` class is used by the middleware to set 101 | # timestamps for the last write to the primary. The resolver uses the context 102 | # class timestamps to determine how long to wait before reading from the 103 | # replica. 104 | # 105 | # By default Rails will store a last write timestamp in the session. The 106 | # DatabaseSelector middleware is designed as such you can define your own 107 | # strategy for connection switching and pass that into the middleware through 108 | # these configuration options. 109 | # config.active_record.database_selector = { delay: 2.seconds } 110 | # config.active_record.database_resolver = ActiveRecord::Middleware::DatabaseSelector::Resolver 111 | # config.active_record.database_resolver_context = ActiveRecord::Middleware::DatabaseSelector::Resolver::Session 112 | end 113 | -------------------------------------------------------------------------------- /lib/generators/beautiful_sorcery_generator.rb: -------------------------------------------------------------------------------- 1 | # encoding : utf-8 2 | class BeautifulSorceryGenerator < Rails::Generators::Base 3 | require_relative 'beautiful_scaffold_common_methods' 4 | include BeautifulScaffoldCommonMethods 5 | 6 | source_root File.expand_path('../templates', __FILE__) 7 | 8 | #argument :model, :type => :string, :desc => "Name of model (ex: User)" 9 | 10 | def install_sorcery 11 | model = "User" 12 | view_path = "app/views/" 13 | 14 | if !File.read('Gemfile').include?("sorcery") 15 | gem("sorcery", "0.16.0") 16 | end 17 | 18 | Bundler.with_unbundled_env do 19 | run "bundle install" 20 | end 21 | 22 | raise "Model must be specified" if model.blank? 23 | 24 | # Install sorcery 25 | generate("sorcery:install", "remember_me reset_password user_activation brute_force_protection external --model #{model}") 26 | 27 | # If exist users migration just add columns 28 | create_user_migration = Dir.glob("db/migrate/*create_users.rb").first 29 | if create_user_migration 30 | already_email = File.read(create_user_migration).include?(":email") 31 | sorcery_core_file = Dir.glob("db/migrate/*sorcery_core.rb").first 32 | File.open(sorcery_core_file, "w+") do |f| 33 | f.write("class SorceryCore < ActiveRecord::Migration[6.1] 34 | def change 35 | #{(already_email ? '' : 'add_column :users, :email, :string')} 36 | add_column :users, :crypted_password, :string 37 | add_column :users, :salt, :string 38 | 39 | #{(already_email ? '' : 'add_index :users, :email, unique: true')} 40 | end 41 | end") 42 | end 43 | end 44 | 45 | # Generate mailer 46 | copy_file("app/mailers/user_mailer.rb") 47 | 48 | # Install controllers 49 | copy_file("app/controllers/user_sessions_controller.rb") 50 | 51 | # ===== Controller 52 | inject_into_file("app/controllers/users_controller.rb", 53 | "\n 54 | skip_before_action :require_login, only: [:new, :create, :activate] 55 | \n", after: "< BeautifulController") 56 | 57 | inject_into_file("app/controllers/users_controller.rb", 58 | "def activate 59 | if @user = User.load_from_activation_token(params[:id]) 60 | @user.activate! 61 | redirect_to(login_path, :notice => 'User was successfully activated.') 62 | else 63 | not_authenticated 64 | end 65 | end\n\n ", before: "private") 66 | 67 | # ====== Model 68 | # Add password & password_confirmation in model 69 | inject_into_file("app/models/user.rb", 70 | "\n 71 | validates :password, length: { minimum: 3 }, if: -> { new_record? || changes[:crypted_password] } 72 | validates :password, confirmation: true, if: -> { new_record? || changes[:crypted_password] } 73 | validates :password_confirmation, presence: true, if: -> { new_record? || changes[:crypted_password] } 74 | 75 | before_update :setup_activation, if: -> { email_changed? } 76 | after_update :send_activation_needed_email!, if: -> { previous_changes['email'].present? }\n", 77 | after: "ApplicationRecord") 78 | 79 | inject_into_file("app/models/user.rb", ":password,:password_confirmation,", :after => "def self.permitted_attributes\n return ") 80 | 81 | # ====== Views 82 | inject_into_file("app/views/users/_form.html.erb", 83 | '
    84 | <%= f.label :password, t(\'app.models.user.bs_attributes.password\', :default => \'password\').capitalize, :class => "control-label" %>
    85 | <%= f.password_field :password, :class => "form-control" %> 86 |
    87 |
    88 | <%= f.label :password_confirmation, t(\'app.models.user.bs_attributes.password_confirmation\', :default => \'password_confirmation\').capitalize, :class => "control-label" %>
    89 | <%= f.password_field :password_confirmation, :class => "form-control" %> 90 |
    ', before: '') 91 | 92 | # Install all views for login/logout 93 | directory "app/views/login_logout", "app/views" 94 | 95 | # Domain in action_mailer 96 | for current_env in ['production', 'development', 'test'] 97 | inject_into_file("config/environments/#{current_env}.rb", " config.action_mailer.default_url_options = { :host => 'localhost:3000' }", :after => "Rails.application.configure do\n" ) 98 | end 99 | 100 | # In model 101 | #remove_file("app/models/user.rb") # remove generated by sorcery 102 | #copy_file("app/models/user.rb") # copy BS version ;) 103 | 104 | # Limited access 105 | inject_into_file("app/controllers/beautiful_controller.rb", 106 | "\n before_action :require_login, except: [:dashboard]\n", 107 | :after => 'layout "beautiful_layout"' + "\n") 108 | 109 | inject_into_file("config/initializers/sorcery.rb", 110 | "\nuser.user_activation_mailer = UserMailer\n", 111 | :after => "# user.user_activation_mailer =\n") 112 | 113 | # Routes (session) 114 | inject_into_file("config/routes.rb", 115 | ' 116 | resources :user_sessions, only: [:create] 117 | get "login" => "user_sessions#new", :as => :login 118 | post "logout" => "user_sessions#destroy", :as => :logout' + "\n\n\n", 119 | :after => "Rails.application.routes.draw do\n") 120 | 121 | # Activate 122 | inject_into_file("config/routes.rb", " do 123 | member do 124 | get :activate 125 | end 126 | end", after: 'resources :users, concerns: :bs_routes') 127 | 128 | copy_file("#{view_path}partials/_login_logout_register.html.erb", "#{view_path}layouts/_login_logout_register.html.erb") 129 | 130 | # Sign in sign out 131 | inject_into_file("#{view_path}layouts/beautiful_layout.html.erb", 132 | "\n<%= render :partial => 'layouts/login_logout_register' %>\n", 133 | :after => "") 134 | 135 | say "Beautiful-Scaffold enable 'user_activation' sorcery module for you, so when you sign up, find in logs the activation link. You can't sign in yourself until you activate the account" 136 | end 137 | end 138 | -------------------------------------------------------------------------------- /lib/generators/templates/app/assets/javascripts/beautiful_scaffold.js: -------------------------------------------------------------------------------- 1 | function bs_init(){ 2 | 3 | // DriverJS 4 | var driver; 5 | driver = new Driver(); 6 | $(document).on('click', '#bs-help', function(){ 7 | console.log('allo'); 8 | var elements = $('*[data-present-order]').sort(function(a,b) { return parseInt($(a).attr('data-present-order')) > parseInt($(b).attr('data-present-order')); }).map(function(){ 9 | var elt = $(this); 10 | return { element: "#" + elt.attr('id'), popover: { 11 | title: elt.attr('data-present-title'), 12 | description: elt.attr('data-present-description') 13 | }}; 14 | }); 15 | 16 | driver.defineSteps(elements); 17 | driver.start(); 18 | return false; 19 | }); 20 | 21 | // habtm (select2 - tag) 22 | $('.bs-tagit').each(function( index ) { 23 | var tagitelt = this; 24 | $(tagitelt).select2({ 25 | ajax: { 26 | processResults: function (data) { 27 | // Transforms the top-level key of the response object from 'items' to 'results' 28 | return { 29 | results: $.map(data, function (obj) { 30 | obj.id = obj.id; 31 | obj.text = obj.caption; 32 | return obj; 33 | }) 34 | }; 35 | } 36 | } 37 | }); 38 | }); 39 | 40 | // Wysiwyg field 41 | $('.wysiwyg-editor').wysihtml5({"html": true}); 42 | 43 | // Processing 44 | $(document).on('click', '#checkall', function(){ 45 | $('.cbbatch').prop('checked', this.checked); 46 | }); 47 | 48 | // Filter columns 49 | $(document).on('click', '#filter-columns', function(){ 50 | var return_json = []; 51 | $.each($('input[name^="field"]:checked'), function(index, value) { 52 | return_json.push($(value).val()); 53 | }); 54 | var url = $(this).attr('data-url'); 55 | $.ajax({ 56 | url: url, 57 | data: { 'fields' : return_json }, 58 | success: function(data) { 59 | $('table.table th[class^="bs-col"], table.table td[class^="bs-col"]').css('display', 'none'); 60 | $.each(return_json, function(index, value) { 61 | $('table.table th.bs-col-' + value + ', table.table td.bs-col-' + value).css('display', 'table-cell'); 62 | }); 63 | $('div[class^="bs-col"]').css('display', 'none'); 64 | $.each(return_json, function(index, value) { 65 | $('div.bs-col-' + value).css('display', 'inline'); 66 | }); 67 | $('#modal-columns').modal('hide'); 68 | } 69 | }); 70 | return false; 71 | }); 72 | $(document).on('click', '#cancel-filter-columns', function(){ 73 | $('#modal-columns').modal('hide'); 74 | return false; 75 | }); 76 | 77 | // TreeView JS 78 | var opened = eval($("#treeview").attr("data-opened")); 79 | var url = $("#treeview").attr("data-url"); 80 | var model = $("#treeview").attr("data-model"); 81 | $("#treeview").on("move_node.jstree", function (e, data) { 82 | var dataajax = { 83 | "operation" : "move_node", 84 | "position" : data.position 85 | }; 86 | dataajax[model + "_id"] = $('#' + data.parent).attr('data-id'); 87 | $.ajax({ 88 | async : false, 89 | type: 'POST', 90 | url: url + data.node.data.id + "/treeview_update", 91 | data : dataajax, 92 | success : function (r) { 93 | 94 | }, 95 | error : function (r) { 96 | $.jstree.rollback(data.rlbk); 97 | } 98 | }); 99 | }).jstree({ 100 | "plugins" : [ 101 | "themes","html_data","ui","dnd" 102 | ], 103 | "core" : { 104 | "initially_open" : [opened], 105 | check_callback: function (op, node, parent, position, more) { 106 | return true; 107 | } 108 | } 109 | }); 110 | 111 | $('.barcode').each(function(index){ 112 | $(this).barcode($(this).attr('data-barcode'), $(this).attr('data-type-barcode')); 113 | }); 114 | 115 | // Add Error Form style with bootstrap 116 | $("div.form-group>div.field_with_errors").find('.form-control').addClass("is-invalid"); 117 | $("div.form-group>div.field_with_errors").find('label').addClass("text-danger"); 118 | $("#error_explanation").addClass("text-danger"); 119 | 120 | // Collapse without IDS (next) TODO bootstrap 4.2 121 | $('body').on('click.collapse-next.data-api', '[data-toggle=collapse-next]', function() { 122 | var $target = $(this).parent().next(); 123 | $target.collapse('toggle'); 124 | return false; 125 | }); 126 | 127 | // Mass inserting set focus 128 | $(function() { 129 | var elt = $('form.mass-inserting input.form-control').first(); 130 | if($('form.mass-inserting').hasClass('setfocus')){ 131 | elt.focus(); 132 | } 133 | }); 134 | 135 | // Menu dropdown 136 | try{ 137 | $('.dropdown-toggle').dropdown(); 138 | $('.dropdown-menu').find('form').click(function (e) { 139 | e.stopPropagation(); 140 | }); 141 | }catch (e){ 142 | } 143 | 144 | // Toggle display Search 145 | $(document).on('click','#hide-search-btn',function(){ 146 | $('body div.filler div.col-md-9').addClass('col-md-12'); 147 | $('body div.filler div.col-md-12').removeClass('col-md-9'); 148 | $('body div.filler div.col-md-3').hide(); 149 | $('#hide-search-btn').hide(); 150 | $('#show-search-btn').show(); 151 | }); 152 | $(document).on('click','#show-search-btn',function(){ 153 | $('body div.filler div.col-md-12').addClass('col-md-9'); 154 | $('body div.filler div.col-md-9').removeClass('col-md-12'); 155 | $('body div.filler div.col-md-3').show(); 156 | $('#hide-search-btn').show(); 157 | $('#show-search-btn').hide(); 158 | }); 159 | $('#show-search-btn').hide(); 160 | } -------------------------------------------------------------------------------- /lib/generators/templates/app/controllers/base.rb: -------------------------------------------------------------------------------- 1 | # encoding : utf-8 2 | <% 3 | if !engine_name.blank? 4 | b_module = "module #{engine_camel}" 5 | e_module = "end" 6 | else 7 | b_module = "" 8 | e_module = "" 9 | end 10 | %> 11 | <%= b_module %> 12 | class <%= namespace_for_class %><%= model_camelize.pluralize %>Controller < BeautifulController 13 | 14 | before_action :load_<%= model %>, :only => [:show, :edit, :update, :destroy] 15 | 16 | # Uncomment for check abilities with CanCan 17 | #authorize_resource 18 | 19 | def index 20 | session['fields'] ||= {} 21 | session['fields']['<%= model %>'] ||= (<%= model_camelize %>.columns.map(&:name) - ["id"])[0..4] 22 | do_select_fields('<%= model %>') 23 | do_sort_and_paginate('<%= model %>') 24 | 25 | @q = <%= model_camelize %>.ransack( 26 | params[:q] 27 | ) 28 | 29 | @<%= model %>_scope = @q.result( 30 | :distinct => true 31 | ).sorting( 32 | params[:sorting] 33 | ) 34 | 35 | @<%= model %>_scope_for_scope = @<%= model %>_scope.dup 36 | 37 | unless params[:scope].blank? 38 | @<%= model %>_scope = @<%= model %>_scope.send(params[:scope]) 39 | end 40 | 41 | @<%= model_pluralize %> = @<%= model %>_scope.paginate( 42 | :page => params[:page], 43 | :per_page => 20 44 | ).to_a 45 | 46 | respond_to do |format| 47 | format.html{ 48 | render 49 | } 50 | format.json{ 51 | render :json => @<%= model %>_scope.to_json(methods: :caption) 52 | } 53 | format.csv{ 54 | require 'csv' 55 | csvstr = CSV.generate do |csv| 56 | csv << <%= model_camelize %>.attribute_names 57 | @<%= model %>_scope.to_a.each{ |o| 58 | csv << <%= model_camelize %>.attribute_names.map{ |a| o[a] } 59 | } 60 | end 61 | render :plain => csvstr 62 | } 63 | format.xml{ 64 | render :xml => @<%= model %>_scope.to_a 65 | } 66 | format.pdf{ 67 | pdfcontent = PdfReport.new.to_pdf(<%= model_camelize %>,@<%= model %>_scope) 68 | send_data pdfcontent 69 | } 70 | end 71 | end 72 | 73 | def show 74 | respond_to do |format| 75 | format.html{ 76 | render 77 | } 78 | format.json { render :json => @<%= model %> } 79 | end 80 | end 81 | 82 | def new 83 | @<%= model %> = <%= model_camelize %>.new 84 | 85 | respond_to do |format| 86 | format.html{ 87 | render 88 | } 89 | format.json { render :json => @<%= model %> } 90 | end 91 | end 92 | 93 | def edit 94 | 95 | end 96 | 97 | def create 98 | @<%= model %> = <%= model_camelize %>.new(params_for_model) 99 | 100 | respond_to do |format| 101 | if @<%= model %>.save 102 | format.html { 103 | if params[:mass_inserting] then 104 | redirect_to <%= namespace_for_route %><%= model_pluralize %>_path(:mass_inserting => true) 105 | else 106 | redirect_to <%= namespace_for_route %><%= singular_table_name %>_path(@<%= model %>), :flash => { :notice => t(:create_success, :model => "<%= model %>") } 107 | end 108 | } 109 | format.json { render :json => @<%= model %>, :status => :created, :location => @<%= model %> } 110 | else 111 | format.html { 112 | if params[:mass_inserting] then 113 | redirect_to <%= namespace_for_route %><%= model_pluralize %>_path(:mass_inserting => true), :flash => { :error => "#{t(:error, default: "Error")} : #{@<%= model %>.errors.full_messages.join(", ")}" } 114 | else 115 | render :action => "new" 116 | end 117 | } 118 | format.json { render :json => @<%= model %>.errors, :status => :unprocessable_entity } 119 | end 120 | end 121 | end 122 | 123 | def update 124 | 125 | respond_to do |format| 126 | if @<%= model %>.update(params_for_model) 127 | format.html { redirect_to <%= namespace_for_route %><%= singular_table_name %>_path(@<%= model %>), :flash => { :notice => t(:update_success, :model => "<%= model %>") }} 128 | format.json { head :ok } 129 | else 130 | format.html { render :action => "edit" } 131 | format.json { render :json => @<%= model %>.errors, :status => :unprocessable_entity } 132 | end 133 | end 134 | end 135 | 136 | def destroy 137 | @<%= model %>.destroy 138 | 139 | respond_to do |format| 140 | format.html { redirect_to <%= namespace_for_route %><%= model_pluralize %>_url } 141 | format.json { head :ok } 142 | end 143 | end 144 | 145 | def batch 146 | attr_or_method, value = params[:actionprocess].split(".") 147 | 148 | @<%= model_pluralize %> = [] 149 | 150 | <%= model_camelize %>.transaction do 151 | if params[:checkallelt] == "all" then 152 | # Selected with filter and search 153 | do_sort_and_paginate(:<%= model %>) 154 | 155 | @<%= model_pluralize %> = <%= model_camelize %>.ransack( 156 | session['search']['<%= model %>'] 157 | ).result( 158 | :distinct => true 159 | ) 160 | else 161 | # Selected elements 162 | @<%= model_pluralize %> = <%= model_camelize %>.find(params[:ids].to_a) 163 | end 164 | 165 | @<%= model_pluralize %>.each{ |<%= model %>| 166 | if not <%= model_camelize %>.columns_hash[attr_or_method].nil? and 167 | <%= model_camelize %>.columns_hash[attr_or_method].type == :boolean then 168 | <%= model %>.update_attribute(attr_or_method, boolean(value)) 169 | <%= model %>.save 170 | else 171 | case attr_or_method 172 | # Set here your own batch processing 173 | # <%= model %>.save 174 | when "destroy" then 175 | <%= model %>.destroy 176 | when "touch" then 177 | <%= model %>.touch 178 | end 179 | end 180 | } 181 | end 182 | 183 | redirect_to <%= namespace_for_route %><%= model_pluralize %>_url 184 | end 185 | 186 | def treeview 187 | 188 | end 189 | 190 | def treeview_update 191 | modelclass = <%= model_camelize %> 192 | foreignkey = :<%= model %>_id 193 | 194 | 195 | if update_treeview(modelclass, foreignkey) 196 | head :ok 197 | else 198 | head :internal_server_error 199 | end 200 | end 201 | 202 | private 203 | 204 | def load_<%= model %> 205 | @<%= model %> = <%= model_camelize %>.find(params[:id]) 206 | end 207 | 208 | def params_for_model 209 | params.require(:<%= model %>).permit(<%= model_camelize %>.permitted_attributes) 210 | end 211 | end 212 | <%= e_module %> 213 | -------------------------------------------------------------------------------- /lib/generators/beautiful_scaffold_common_methods.rb: -------------------------------------------------------------------------------- 1 | module BeautifulScaffoldCommonMethods 2 | require 'erb' 3 | 4 | private 5 | 6 | ############# 7 | # Engine 8 | ############# 9 | 10 | def engine_opt 11 | options[:mountable_engine].to_s.downcase 12 | end 13 | 14 | def engine_name 15 | engine_opt.blank? ? '' : "#{engine_opt}/" 16 | end 17 | 18 | def engine_camel 19 | options[:mountable_engine].to_s.camelize 20 | end 21 | 22 | ############# 23 | # Namespace 24 | ############# 25 | 26 | def namespace_for_class 27 | str = namespace_alone 28 | str = str.camelcase + '::' if not str.blank? 29 | return str 30 | end 31 | 32 | def namespace_for_route 33 | str = namespace_alone 34 | str = str.downcase + '_' if not str.blank? 35 | return str 36 | end 37 | 38 | def namespace_for_url 39 | str = namespace_alone 40 | str = str.downcase + '/' if not str.blank? 41 | return str 42 | end 43 | 44 | def namespace_alone 45 | return options[:namespace].to_s.downcase 46 | end 47 | 48 | def render_partial(path) 49 | source = File.expand_path(find_in_source_paths(path.to_s)) 50 | result = ERB.new(::File.binread(source), trim_mode: '-').result(binding) 51 | return result 52 | end 53 | 54 | def regexp_an_string 55 | '\s*\R\s*' 56 | end 57 | 58 | ############ 59 | # Models 60 | ############ 61 | 62 | def model 63 | model_opt.underscore 64 | end 65 | 66 | def model_camelize 67 | model.camelize 68 | end 69 | 70 | def model_with_engine_camelize 71 | (engine_name.blank? ? model.camelize : "#{engine_camel}::#{model_camelize}") 72 | end 73 | 74 | def model_pluralize 75 | model.pluralize 76 | end 77 | 78 | def model_class 79 | model.camelize 80 | end 81 | 82 | ############ 83 | # Table 84 | ############ 85 | 86 | def plural_table_name 87 | model_pluralize 88 | end 89 | def singular_table_name 90 | model 91 | end 92 | 93 | ############ 94 | # I18n 95 | ############ 96 | 97 | def attribute_path_i18n(model, attribute) 98 | "app.models.#{model}.bs_attributes.#{attribute}" 99 | end 100 | 101 | def model_path_i18n(model) 102 | "app.models.#{model}.bs_caption" 103 | end 104 | 105 | def model_p_path_i18n(model) 106 | "app.models.#{model}.bs_caption_plural" 107 | end 108 | 109 | def i18n_t_a(model, attribute) 110 | "t('#{attribute_path_i18n(model, attribute)}', :default => '#{attribute}')" 111 | end 112 | 113 | def i18n_t_m(model) 114 | "t('#{model_path_i18n(model)}', :default => '#{model}')" 115 | end 116 | 117 | def i18n_t_m_p(model) 118 | "t('#{model_p_path_i18n(model)}', :default => '#{model}')" 119 | end 120 | 121 | def available_views 122 | %w(index edit show new _form) 123 | end 124 | 125 | def attributes 126 | # https://raw.github.com/rails/rails/master/railties/lib/rails/generators/generated_attribute.rb 127 | require 'rails/generators/generated_attribute' 128 | return myattributes.map{ |a| 129 | attr, type = a.split(":") 130 | Rails::Generators::GeneratedAttribute.new(attr, type.to_sym) 131 | } 132 | end 133 | 134 | def beautiful_attr_to_rails_attr #(for_migration = false) 135 | newmyattributes = [] 136 | myattributes.each{ |attr| 137 | a,t = attr.split(':') 138 | newt = t 139 | 140 | # Special columns 141 | if ['wysiwyg'].include?(t) 142 | newt = 'text' 143 | elsif t == 'price' 144 | newt = 'float' 145 | elsif ['references', 'reference'].include?(t) # Because Rails generate corrupted files (migrations) 146 | a = "#{a}_id" 147 | newt = 'integer:index' 148 | elsif t == 'color' 149 | newt = 'string' 150 | end 151 | 152 | newmyattributes << [a, newt].join(':') 153 | } 154 | 155 | return newmyattributes 156 | end 157 | 158 | def attributes_without_type 159 | newmyattributes = [] 160 | myattributes.each{ |attr| 161 | a,t = attr.split(':') 162 | 163 | if ['references', 'reference'].include?(t) 164 | a = a + '_id' 165 | end 166 | 167 | # Add the typetext to permitted_attr 168 | if t == 'wysiwyg' 169 | newmyattributes << "#{a}_typetext" 170 | end 171 | 172 | newmyattributes << a 173 | } 174 | 175 | return newmyattributes 176 | end 177 | 178 | def fulltext_attribute 179 | fulltext_field = [] 180 | myattributes.each{ |attr| 181 | a,t = attr.split(':') 182 | if ['wysiwyg'].include?(t) 183 | fulltext_field << a 184 | end 185 | } 186 | return fulltext_field 187 | end 188 | 189 | def richtext_type 190 | return ["html","text"] 191 | end 192 | 193 | def require_gems 194 | gems = { 195 | 'will_paginate' => nil, # v 3.1.5 196 | 'ransack' => nil, #'2.3.2', 197 | 'jquery-ui-rails' => nil, 198 | 'prawn' => nil, #'2.1.0', 199 | 'prawn-table' => nil, #'0.2.2', 200 | 'sanitize' => nil, 201 | #'twitter-bootstrap-rails' => '3.2.2', # Bootstrap 3 for Rails 6+ 202 | 'bootstrap' => '~> 5.1.0', # Bootstrap 4 for Rails 6+ 203 | 'font-awesome-sass' => '~> 5.13.0', 204 | 'momentjs-rails' => '>= 2.9.0', 205 | 'bootstrap4-datetime-picker-rails' => nil, 206 | 'jquery-rails' => '4.3.1', 207 | 'jstree-rails-4' => '3.3.8' 208 | } 209 | 210 | # Si engine il faut mettre les gems dans le gemspec et faire le require 211 | if !Dir.glob('./*.gemspec').empty? 212 | puts "============> Engine : You must add gems to your main app \n #{gems.to_a.map{ |a| "gem '#{a[0]}'#{(a[1].nil? ? '' : ", '#{a[1]}'")} " }.join("\n")}" 213 | end 214 | 215 | gemfile_content = File.read('Gemfile') 216 | gems.each{ |gem_to_add, version| 217 | # Bug add at every times, need to check if already present 218 | if !gemfile_content.include?(gem_to_add) 219 | gem(gem_to_add, version) 220 | end 221 | } 222 | end 223 | 224 | def add_relation 225 | myattributes.each{ |attr| 226 | a,t = attr.split(':') 227 | 228 | foreign_key = a 229 | 230 | if ['references', 'reference'].include?(t) 231 | foreign_key = "#{a}_id" 232 | 233 | # question (model) belongs_to user (a) 234 | inject_into_file("app/models/#{engine_name}#{model}.rb", "\n belongs_to :#{a}, optional: true", :after => "ApplicationRecord") 235 | inject_into_file("app/models/#{engine_name}#{a}.rb", "\n has_many :#{model_pluralize}, :dependent => :nullify", :after => "ApplicationRecord") 236 | end 237 | 238 | inject_into_file("app/models/#{engine_name}#{model}.rb", ":#{foreign_key},", :after => "def self.permitted_attributes\n return ") 239 | } 240 | end 241 | 242 | end 243 | -------------------------------------------------------------------------------- /lib/generators/beautiful_locale_generator.rb: -------------------------------------------------------------------------------- 1 | # encoding : utf-8 2 | class BeautifulLocaleGenerator < Rails::Generators::Base 3 | require_relative 'beautiful_scaffold_common_methods' 4 | include BeautifulScaffoldCommonMethods 5 | 6 | source_root File.expand_path('../templates', __FILE__) 7 | 8 | argument :name, :type => :string, :desc => "type of locale : fr, en, de, all" 9 | 10 | class_option :mountable_engine, :default => nil 11 | 12 | def list_locales 13 | availablelocale = ["fr", "en", "ja"] 14 | 15 | localestr = name.downcase 16 | (localestr == 'all' ? availablelocale : [localestr]) 17 | end 18 | 19 | def install_locale 20 | list_locales.each{ |temp_locale| 21 | 22 | ["beautiful_scaffold.#{temp_locale}.yml"].each do |filename| 23 | gem_localepath = "app/locales/#{filename}" 24 | app_localepath = "config/locales/#{filename}" 25 | begin 26 | copy_file gem_localepath, app_localepath 27 | rescue 28 | say_status("Error", "This beautiful_locale #{temp_locale} doesn't exist !", :red) 29 | end 30 | end 31 | 32 | rails_locale_file = "#{temp_locale}.yml" 33 | download_path = "https://raw.github.com/svenfuchs/rails-i18n/master/rails/locale/#{rails_locale_file}" 34 | begin 35 | get download_path, "config/locales/#{rails_locale_file}" 36 | rescue 37 | say_status("Error", "Error to download locale, verify if locale exist at : #{download_path}", :red) 38 | end 39 | 40 | willpaginate_locale_file = "will_paginate.#{temp_locale}.yml" 41 | download_path = "https://raw.githubusercontent.com/tigrish/will-paginate-i18n/master/config/locales/#{temp_locale}.yml" 42 | begin 43 | get download_path, "config/locales/#{willpaginate_locale_file}" 44 | say_status("Warning", "You must modify Will_paginate locale at : Rails.root/config/locale/#{willpaginate_locale_file}", :red) 45 | rescue 46 | say_status("Error", "Error to download locale, verify if locale exist at : #{download_path}", :red) 47 | end 48 | } 49 | 50 | say_status("Warning", "/!\\ Remember to update your application.rb file !", :yellow) 51 | end 52 | 53 | def regenerate_app_locale 54 | require 'net/http' 55 | 56 | app_path = (Rails.root || engine_opt) 57 | app_name = Rails.application.class.name.split('::').first.downcase 58 | prefix = engine_opt.blank? ? '' : "#{engine_opt.camelize}::" 59 | 60 | already_processed = {} 61 | hi18n = {} 62 | 63 | list_locales.each do |locale_str| 64 | locale = locale_str.downcase 65 | 66 | already_processed[locale] ||= {} 67 | 68 | filepath = File.join(app_path, 'config', 'locales', "#{app_name}.#{locale}.yml") 69 | begin 70 | if File.exist?(filepath) 71 | hi18n = YAML.load_file(filepath) 72 | end 73 | rescue 74 | puts "Error loading locale file (YAML invalid?) : #{filepath}" 75 | end 76 | 77 | hi18n[locale] ||= { 'app' => {} } 78 | hi18n[locale]['app'] ||= { 'models' => {} } 79 | hi18n[locale]['app']['models'] ||= {} 80 | 81 | # Feed data already translated 82 | hi18n[locale]['app']['models'].each do |modelname, hshtranslations| 83 | hshtranslations['bs_attributes'].each do |attr, translated_attr| 84 | already_processed[locale][attr] = translated_attr 85 | end 86 | end 87 | 88 | Dir.glob("app/models/**/*").each do |model_file| 89 | puts model_file 90 | 91 | next if File.directory?(model_file) or 92 | File.basename(model_file).first == '.' or 93 | model_file.include?('/concerns/') or 94 | model_file.include?('pdf_report.rb') or 95 | model_file.include?('application_record.rb') 96 | 97 | model = File.basename(model_file, File.extname(model_file)) 98 | 99 | klass = "#{prefix}#{model}".camelize.constantize 100 | sorted_attr = klass.attribute_names.sort 101 | 102 | newmodel = !hi18n[locale]['app']['models'].has_key?(model) 103 | 104 | hi18n[locale]['app']['models'][model] ||= { 105 | 'bs_caption' => model, 106 | 'bs_caption_plural' => model.pluralize, 107 | 'bs_attributes' => {}, 108 | } 109 | 110 | if newmodel then 111 | bs_caption = "" 112 | begin 113 | bs_caption = translate_string(locale, model) 114 | rescue Exception => e 115 | puts "Erreur traduction #{e.backtrace}" 116 | bs_caption = model 117 | end 118 | bs_caption_plural = "" 119 | begin 120 | bs_caption_plural = translate_string(locale, model.pluralize) 121 | rescue Exception => e 122 | puts "Erreur traduction #{e.backtrace}" 123 | bs_caption_plural = model.pluralize 124 | end 125 | 126 | hi18n[locale]['app']['models'][model]['bs_caption'] = bs_caption 127 | hi18n[locale]['app']['models'][model]['bs_caption_plural'] = bs_caption_plural 128 | end 129 | 130 | hi18n[locale]['app']['models'][model]['bs_attributes'] ||= {} 131 | 132 | sorted_attr.each do |k| 133 | # Si pas déjà renseigné 134 | if hi18n[locale]['app']['models'][model]['bs_attributes'][k].blank? 135 | # Si pas déjà traduit 136 | if already_processed[locale][k].blank? 137 | begin 138 | attr_translate = translate_string(locale, k) 139 | already_processed[locale][k] = attr_translate 140 | rescue 141 | puts "Plantage translate API" 142 | attr_translate = k 143 | end 144 | else 145 | attr_translate = already_processed[locale][k] 146 | end 147 | else 148 | # Récupère l'attribut traduit 149 | attr_translate = hi18n[locale]['app']['models'][model]['bs_attributes'][k] 150 | end 151 | 152 | hi18n[locale]['app']['models'][model]['bs_attributes'][k] = attr_translate 153 | end 154 | end 155 | 156 | File.unlink(filepath) if File.exist?(filepath) 157 | 158 | file = File.open(filepath, "w") 159 | file.write(hi18n[locale].to_yaml) 160 | file.close 161 | end 162 | end 163 | 164 | private 165 | 166 | def translate_string(locale, str) 167 | # See http://www.microsofttranslator.com/dev/ 168 | # 169 | if locale == "en" 170 | attr_translate = "#{str.gsub(/_/, " ")}" 171 | else 172 | url_domain = "api.mymemory.translated.net" 173 | url_query = "/get?q=#{str.gsub(/_/, "%20")}&langpair=en%7C#{locale}" 174 | 175 | json = JSON.parse(Net::HTTP.get(url_domain, url_query)) 176 | attr_translate = json["responseData"]["translatedText"].strip.downcase 177 | end 178 | raise 'Free Limit' if attr_translate =~ /mymemory/ 179 | 180 | return attr_translate 181 | end 182 | 183 | end 184 | -------------------------------------------------------------------------------- /test/lib/generators/beautiful_scaffold_generator_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | require 'generators/beautiful_scaffold_generator' 3 | require 'generators/beautiful_migration_generator' 4 | 5 | # In order to run test : in Beautiful-Scaffold dir just run : 6 | # rake test 7 | # 8 | # Source : 9 | # https://fossies.org/linux/rails/railties/test/generators/shared_generator_tests.rb 10 | # https://rossta.net/blog/testing-rails-generators.html 11 | 12 | class BeautifulScaffoldGeneratorTest < Rails::Generators::TestCase 13 | tests BeautifulScaffoldGenerator 14 | destination Rails.root.join('../tmp/dummyapp') # test/dummy/tmp/generators/dummyapp.... 15 | 16 | setup do 17 | #puts "SETUP " * 100 18 | prepare_destination # Create tmp 19 | Dir.chdir(File.dirname(destination_root)) { # dans test/tmp 20 | system "rails new dummyapp --skip-test-unit --skip-spring --skip-bootsnap --skip-webpack-install --skip-javascript" 21 | } 22 | end 23 | 24 | # At the end of test 25 | teardown do 26 | Dir.chdir(File.dirname(destination_root)) { 27 | system 'rm -rf dummyapp' 28 | } 29 | end 30 | 31 | test "generator runs without errors" do 32 | assert_nothing_raised do 33 | run_generator 'user email:string birthday:datetime children:integer biography:text -f'.split(' ') 34 | end 35 | end 36 | 37 | test "generator runs with relation" do 38 | run_generator 'family label:string -s'.split(' ') 39 | run_generator 'user email:string birthday:datetime children:integer biography:text family:references -f'.split(' ') 40 | 41 | assert_file 'app/models/user.rb' 42 | assert_file 'app/models/family.rb' 43 | 44 | assert_file 'app/models/user.rb', /belongs_to :family/ 45 | assert_file 'app/models/family.rb', /has_many :users/ 46 | 47 | assert_file 'app/models/user.rb' do |content| 48 | assert_match('self.permitted_attributes', content) 49 | end 50 | assert_file 'app/controllers/users_controller.rb' do |content| 51 | assert_match("session['fields']['user'] ||= (User.columns.map(&:name) - [\"id\"])[0..4]", content) 52 | end 53 | 54 | assert_file 'app/views/users/_form.html.erb' do |content| 55 | # Input family_id (foreign-key) 56 | assert_match('<%= f.collection_select :family_id, Family.all, :id, :caption, { :include_blank => true }, { :class => "form-control" } %>', content) 57 | # Label biography 58 | assert_match("<%= f.label :biography, t('app.models.user.bs_attributes.biography', :default => 'biography').capitalize, :class => \"control-label\" %>", content) 59 | # Input date (day) 60 | assert_match('<%= f.hidden_field("birthday(#{i+1}i)", value: @user.birthday&.send(meth), id: "user_birthday_input_#{i+1}i") %>', content) 61 | end 62 | 63 | end 64 | 65 | test "generator runs with files" do 66 | run_generator 'user email:string birthday:datetime children:integer biography:text -f'.split(' ') 67 | 68 | ############### 69 | # Assets 70 | ############### 71 | # 72 | # js 73 | assert_file 'app/assets/javascripts/application-bs.js' 74 | assert_file 'app/assets/javascripts/beautiful_scaffold.js' 75 | assert_file 'app/assets/javascripts/bootstrap-datetimepicker-for-beautiful-scaffold.js' 76 | assert_file 'app/assets/javascripts/bootstrap-wysihtml5.js' 77 | assert_file 'app/assets/javascripts/a-wysihtml5-0.3.0.min.js' 78 | assert_file 'app/assets/javascripts/fixed_menu.js' 79 | assert_file 'app/assets/javascripts/jstree.min.js' 80 | assert_file 'app/assets/javascripts/jquery-barcode.js' 81 | # 82 | # css 83 | assert_file 'app/assets/stylesheets/application-bs.scss' 84 | assert_file 'app/assets/stylesheets/beautiful-scaffold.css.scss' 85 | assert_file 'app/assets/stylesheets/bootstrap-wysihtml5.css' 86 | 87 | ############### 88 | # Controllers 89 | ############### 90 | assert_file 'app/controllers/beautiful_controller.rb' 91 | assert_file 'app/controllers/users_controller.rb' 92 | 93 | ############### 94 | # Helpers 95 | ############### 96 | assert_file 'app/helpers/beautiful_helper.rb' 97 | assert_file 'app/helpers/users_helper.rb' 98 | 99 | ############### 100 | # Models 101 | ############### 102 | assert_file 'app/models/concerns/caption_concern.rb' 103 | assert_file 'app/models/concerns/default_sorting_concern.rb' 104 | assert_file 'app/models/concerns/fulltext_concern.rb' 105 | assert_file 'app/models/user.rb' 106 | assert_file 'app/models/pdf_report.rb' 107 | 108 | assert_file 'app/models/concerns/caption_concern.rb' do |content| 109 | assert_no_match('module Dummyapp', content) 110 | assert_no_match('end #endofmodule', content) 111 | end 112 | assert_file 'app/models/concerns/default_sorting_concern.rb' do |content| 113 | assert_no_match('module Dummyapp', content) 114 | assert_no_match('end #endofmodule', content) 115 | end 116 | assert_file 'app/models/concerns/fulltext_concern.rb' do |content| 117 | assert_no_match('module Dummyapp', content) 118 | assert_no_match('end #endofmodule', content) 119 | end 120 | 121 | ############### 122 | # Views 123 | ############### 124 | assert_file 'app/views/beautiful/dashboard.html.erb' 125 | assert_file 'app/views/layouts/_beautiful_menu.html.erb' 126 | assert_file 'app/views/layouts/_form_habtm_tag.html.erb' 127 | assert_file 'app/views/layouts/_mass_inserting.html.erb' 128 | assert_file 'app/views/layouts/_modal_columns.html.erb' 129 | assert_file 'app/views/layouts/beautiful_layout.html.erb' 130 | 131 | assert_file 'app/views/users/index.html.erb' do |content| 132 | assert_match(" User.columns", content) 133 | # Table td biography 134 | assert_match(' class="bs-col-children <%= align_attribute("integer") %>">', content) 135 | # Search form 136 | assert_match('<%= ransack_field("user", "birthday", f, "Birthday") %>', content) 137 | # Table th children 138 | assert_match(' class="bs-col-email">', content) 139 | end 140 | 141 | assert_file 'app/views/users/show.html.erb' do |content| 142 | assert_match("<%= t('app.models.user.bs_attributes.email', :default => 'email') %>:", content) 143 | end 144 | 145 | assert_file 'app/views/users/_form.html.erb' do |content| 146 | # Label biography 147 | assert_match("<%= f.label :biography, t('app.models.user.bs_attributes.biography', :default => 'biography').capitalize, :class => \"control-label\" %>", content) 148 | # Input date (day) 149 | assert_match('<%= f.hidden_field("birthday(#{i+1}i)", value: @user.birthday&.send(meth), id: "user_birthday_input_#{i+1}i") %>', content) 150 | end 151 | 152 | assert_file 'app/views/layouts/_beautiful_menu.html.erb' do |content| 153 | assert_match('<%= link_to t(\'app.models.user.bs_caption_plural\', :default => \'user\').capitalize, users_path, class: "nav-link #{(params[:controller] == "users" ? "active" : "")}" %>', content) 154 | end 155 | 156 | ############### 157 | # Migrations 158 | ############### 159 | migration = "CreateUsers" 160 | assert_migration 'db/migrate/create_users.rb', /class #{migration} < ActiveRecord::Migration\[[0-9.]+\]/ 161 | 162 | # check precompile 163 | #assert_file 'config/initializers/assets.rb', /Rails\.application\.config\.assets\.precompile += \['application-bs\.css','application-bs\.js'\]/ 164 | assert_file 'config/initializers/ransack.rb' 165 | 166 | assert_file 'config/initializers/link_renderer.rb' do |content| 167 | assert_match('class BootstrapLinkRenderer < LinkRenderer', content) 168 | end 169 | 170 | assert_file 'config/initializers/ransack.rb' do |content| 171 | assert_match('Ransack.configure do |config|', content) 172 | end 173 | end 174 | end 175 | -------------------------------------------------------------------------------- /CHANGELOG: -------------------------------------------------------------------------------- 1 | == master 2 | 3 | * enhancement 4 | 5 | * bugfix 6 | 7 | == TODO 8 | 9 | ActionText / trix. 10 | Include pg_search for fulltext field. 11 | Remove "image_processing" specific version + add gem 'mini_magick' (pour la génération des variants) 12 | 13 | == 2.0.3 14 | 15 | * bugfix 16 | * _ids in permitted_attributes (beautiful_jointable) 17 | 18 | * enhancement 19 | * Replace tagit by select2 (+ _ids in permitted_attributes) 20 | * Bootstrap 4.3 -> 5.1 21 | 22 | == 2.0.2 23 | 24 | * enhancement 25 | * Native html colorpicker 26 | 27 | * bugfix 28 | * Datetime picker for model form 29 | * Display price in model form 30 | 31 | == 2.0.1 32 | 33 | * enhancement 34 | * Replace Devise by Sorcery 35 | * Tests Sorcery and Cancancan generators 36 | * New generator for ActiveStorage 37 | 38 | * bugfix 39 | * Locale : fix all locale in the same file. 40 | * Visual fix ('-' in menu) 41 | * Will_paginate right locales 42 | * Avoid adding gems multiple times 43 | 44 | == 2.0.0 45 | 46 | * enhancement 47 | * Bootstrap 4.5 for search field. 48 | 49 | * bugfix 50 | * Beautiful migration & Rails 6 (fix #23) 51 | 52 | == 2.0.0.pre 53 | 54 | * enhancement 55 | * replace chardinjs by driverjs : https://github.com/kamranahmedse/driver.js 56 | * replace bootstrapdatetimepicker by https://tempusdominus.github.io/bootstrap-4/Usage/ 57 | * Update JStreeview 58 | * Update Bootstrap 4.2 59 | * Update FontAwesome 5 60 | * Generate beautiful scaffold for mountable engine 61 | * Tests generators works ! 62 | * Refactoring ruby code. 63 | 64 | * bugfix 65 | * Bugfix for engine 66 | 67 | == 1.0.3 68 | 69 | * enhancement 70 | * You can generate scaffold in a mountable engine, now. (see README for syntax) 71 | 72 | * Bugfix 73 | * I18n translation default value (model.column). 74 | 75 | == 1.0.2 76 | 77 | * enhancement 78 | * Using static twitter bootstrap files 79 | * UI : button back & submit on the same line 80 | * Add tests (better late than never :/) 81 | 82 | * bugfix 83 | * Fix #18 : Responsive Theme Navbar Overlaps Content When Resizing Below ~979px 84 | * Fix generator locales 85 | * Fix icon datetimepicker 86 | * render nothing: true replace with head :ok 87 | 88 | == 1.0.1 89 | 90 | * enhancement 91 | * Change datetimepicker (eyecon.ro -> eonasdan) 92 | * Support CamelCase syntaxe for models. 93 | 94 | * bugfix 95 | * Change keys symbol into string on session (begin at 0.3.5) 96 | 97 | == 1.0.0.pre 98 | 99 | * enhancement 100 | * Remove Markitup 101 | * Remove Livequery 102 | * Compatibility Rails 5 103 | * Update gem (prawn, ransack, willpaginate) 104 | 105 | == 0.3.6 106 | 107 | * enhancement 108 | * Prevent dbclick on form. Thanks to @fazelmk, again. 109 | * Replace "create" by "new" for validation. Thanks to @fazelmk 110 | 111 | == 0.3.5 112 | 113 | * enhancement 114 | * Option to avoid to add bad gem for the rails app. 115 | * Add concern to models 116 | * Add concern for the routes 117 | 118 | * bugfix 119 | * Change symbol into string (access session) 120 | * Table Checkbox (All checkbox bugfix) 121 | 122 | == 0.3.4 123 | 124 | * bugfix 125 | * Show number element in table (i18n bug) 126 | * Improve translation (keep manual translation and check previous translated string) 127 | * alert-error -> alert-danger (bootstrap 3) 128 | * Insert field to search-and-filter div before the accordion panel 129 | * Columns and sort in table fixed (2 commits) 130 | 131 | == 0.3.3 132 | 133 | * enhancement 134 | * Icons aligned to center, id aligned to right in table 135 | * Accordion for option 136 | * Add vertical-align on icon 137 | * Add title for search-and-filter box 138 | 139 | * bugfix 140 | * Mass inserting set focus for first input 141 | * I18n for caption in index table 142 | * Rails 4 all -> to_a 143 | * Improve sorting with treeview 144 | * Responsive for Tablet / Mobile / Desktop 145 | * Foreignkey in mass-insert and filter-columns error 146 | 147 | == 0.3.2 148 | 149 | * bugfix 150 | * Don't try to translate EN to EN (beautiful_locale) 151 | * rails destroy beautiful_scaffold don't remove app/controllers directory 152 | 153 | * enhancement 154 | * Add error class to control-group (bootstrap + rails validates) 155 | * :data => { :confirm => "" } replace :confirm => "" (Rails 4 deprecation warning) 156 | * activerelation.all -> .to_a (rails 4) 157 | 158 | == 0.3.1 159 | 160 | * enhancement 161 | * Spinner works with turbolink 162 | 163 | * bugfix 164 | * Remove PJAX references 165 | * i18n bug names of columns 166 | * Flash notice/error with redirect_to fixed 167 | * Change Info string to Error in flash[:error] div 168 | * Add new attributes in model's permitted attributes and columns select and mass inserting form 169 | 170 | == 0.3.0.rc6 171 | 172 | * enhancement 173 | * Add title for show, edit, destroy icon 174 | 175 | * bugfix 176 | * require jquery-barcode to application-bs.js 177 | * avoid to display log of require 178 | * jointable def up def down -> def change (migration) 179 | 180 | == 0.3.0.rc5 181 | 182 | * bugfix 183 | * avoid to crash if translate limit is overflowed 184 | * avoid to display log of translation 185 | 186 | == 0.3.0.rc4 187 | 188 | * Bugfix 189 | * Refactoring and bugfix for input_type (number_field for integer, checkbox for boolean...) 190 | * i18n for attributes (default option) 191 | 192 | == 0.3.0.rc3 193 | 194 | * Bugfix 195 | * Avoid normal behavior for link and button for chardinjs 196 | 197 | == 0.3.0.rc2 198 | 199 | * enhancement 200 | * Barcode support (set README for usage) 201 | * Chardinjs for overlay instruction (set README for usage) 202 | 203 | * Bugfix 204 | * i18n some bugfix i18n 205 | * i18n download willpaginate i18n file 206 | * Body hidden in firefox 207 | * Add space after icon for some button 208 | * Bug treeview double quote in attribute (data-opened=""") 209 | 210 | == 0.3.0.rc1 211 | 212 | * enhancement 213 | * Refactoring i18n (avoid reserved words) : 214 | * t(:my_model) -> t('models.my_model.caption') 215 | * t(:my_attribute) -> t('models.my_model.attributes.my_attribute') 216 | * Add javascript/css to change DOM for a fixed menu 217 | * Add responsive menu 218 | * Update for rails 4 219 | * Replace PJAX with turbolinks 220 | * Update prawn version 1.0.0.rc2 221 | * Big refactoring with javascript 222 | * Using twitter-bootstrap-rails with turbolinks compatibility and last version of bootstrap and fontawesome 223 | * Generate locale file with auto translation 224 | 225 | * Bugfix 226 | * Avoid to re-generate created_at, updated_at, id search field at each migration 227 | * Bugfix for several responsive behavior 228 | 229 | == 0.2.7 230 | 231 | * enhancement 232 | * Info class for input with content in search form 233 | * Add current locale for price in number_to_currency 234 | 235 | * Bugfix 236 | * Add type decimal for align 237 | * Bug css rules ignored for alignment in table (td, th) 238 | 239 | == 0.2.6 240 | 241 | * Bugfix 242 | * Thanks to : gregwis Problem with models ending with s (http://api.rubyonrails.org/classes/ActiveSupport/Inflector.html#method-i-classify) 243 | 244 | == 0.2.5 245 | 246 | * enhancement 247 | * Capitalize label (forms) 248 | * Capitalize placeholder (massinserting) 249 | 250 | * Bugfix 251 | * Massinserting with boolean 252 | * Treeview with namespace works 253 | 254 | == 0.2.4 255 | 256 | * Bugfix 257 | * Massinserting with 'admin' namespace 258 | 259 | == 0.2.3 260 | 261 | * enhancement 262 | * Add preselect collection_select in mass inserting with filter params 263 | * Adapt ransack_field helper for nested resource 264 | * Add custom caption for treeview element in build_treeview helper 265 | * I18n for menu 266 | * I18n for fields name (placeholder mass inserting and select fields) 267 | * I18n for title h2 268 | * I18n for button New 269 | * Show caption in table instead of #id 270 | 271 | == Previous versions 272 | 273 | * See commits on github 274 | -------------------------------------------------------------------------------- /lib/generators/templates/app/views/index.html.erb: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |
    4 |

    <%%= t(:listing, :default => "Listing") %> <%%= <%= i18n_t_m_p(singular_table_name) %> %>

    5 |

    6 | <%%= link_to ' '.html_safe + t(:new, :default => "New") + ' ' + <%= i18n_t_m(model) %>, new_<%= namespace_for_route %><%= singular_table_name %>_path, :class => "btn btn-outline-secondary" %> 7 | <%% if <%= model_with_engine_camelize %>.columns.map(&:name).include?("<%= model %>_id") %> 8 | <%%= link_to ' '.html_safe + t(:treeview, :default => "Treeview") + ' ' + <%= i18n_t_m(model) %>, treeview_<%= namespace_for_route %><%= model_pluralize %>_path, :class => "btn btn-outline-secondary" %> 9 | <%% end %> 10 |

    11 |
    12 |
    13 |
    14 | 15 | <%%= render :partial => "layouts/mass_inserting", :locals => { :engine => '<%= engine_opt %>', :namespace => '<%= namespace_alone %>', :model_name => '<%= model %>', :model_columns => [<%= attributes.map{ |e| "'#{e.name}'" }.join(',') %>] } %> 16 | 17 | <%%# Set your scopes below (string in array) %> 18 | <%% scopes = [] %> 19 | <%% if !scopes.blank? %> 20 |
    " data-present-description="<%%= t(:help_scope_description, :default => "Filter by scope") %>" data-present-order="1"> 21 |
    22 |
    23 |
    24 | <%%= link_to "All (#{@<%= model %>_scope_for_scope.count})", <%= namespace_for_route %><%= plural_table_name %>_path(:scope => ""), :class => "btn btn-outline-secondary #{((session[:scope][:<%= model %>].to_s == '') ? 'active' : '')}" %> 25 | <%% for scope in scopes %> 26 | <%%= link_to "#{scope} (#{@<%= model %>_scope_for_scope.send(scope).count})", <%= namespace_for_route %><%= plural_table_name %>_path(:scope => scope), :class => "btn btn-outline-secondary #{((session[:scope][:<%= model %>].to_s == scope) ? 'active' : '')}" %> 27 | <%% end %> 28 |
    29 |
    30 |
    31 |
    32 | <%% end %> 33 | 34 |
    35 |
    36 |
    37 | <%%= form_tag batch_<%= namespace_for_route %><%= plural_table_name %>_path do %> 38 |
    39 |
    40 |
    " data-present-description="<%%= t(:help_batch_description, :default => "Batch processing description") %>" data-present-order="2"> 41 | 42 | 43 | 49 | 50 |
    51 | 61 |
    62 |
    63 | 64 | <%%= render :partial => "layouts/modal_columns", :locals => { :engine_name => '<%= engine_opt %>', :model_name => "<%= singular_table_name %>", :model_columns => [<%= (attributes.map{ |e| "'#{e.name}'" }.to_a + ["'created_at'", "'updated_at'"]).join(',') %>] } %> 65 | 66 | 67 | 68 | 69 | 72 | 75 | <%= render_partial 'app/views/partials/_index_header.html.erb' %> 76 | 77 | 80 | 83 | 89 | 90 | 91 | 92 | <%% @<%= plural_table_name %>.each do |<%= singular_table_name %>| %> 93 | "> 94 | 95 | 96 | <%= render_partial 'app/views/partials/_index_column.html.erb' %> 97 | 100 | 103 | 104 | 105 | 106 | 107 | <%% end %> 108 | 109 |
    70 | " > 71 | " data-present-description="<%%= t(:help_checkall_description, :default => "Check all elements visible on the page") %>" data-present-order="3"> 73 | <%%= check_box_tag :checkall, '' %> 74 | ", "created_at") %> class="bs-col-created_at"> 78 | <%%= sorting_header("<%= singular_table_name %>", "created_at", "<%= namespace_alone %>") %> 79 | ", "updated_at") %> class="bs-col-updated_at"> 81 | <%%= sorting_header("<%= singular_table_name %>", "updated_at", "<%= namespace_alone %>") %> 82 | 84 | 88 |
    <%%= <%= singular_table_name %>.id %><%%= check_box_tag "ids[]",<%= singular_table_name %>.id, false, :class => 'cbbatch' %>", "created_at") %> class="bs-col-created_at <%%= align_attribute("datetime") %>"> 98 | <%%= l(<%= singular_table_name %>.created_at, :format => :long) %> 99 | ", "updated_at") %> class="bs-col-updated_at <%%= align_attribute("datetime") %>"> 101 | <%%= l(<%= singular_table_name %>.updated_at, :format => :long) %> 102 | <%%= link_to ''.html_safe, <%= namespace_for_route %><%= singular_table_name %>_path(<%= singular_table_name %>), :title => t(:show, :default => "Show") %><%%= link_to ''.html_safe, edit_<%= namespace_for_route %><%= singular_table_name %>_path(<%= singular_table_name %>), :title => t(:edit, :default => "Edit") %><%%= link_to ''.html_safe, <%= namespace_for_route %><%= singular_table_name %>_path(<%= singular_table_name %>), :data => { :confirm => t(:are_you_sure, :default => "Are you sure?") }, :method => :delete, :title => t(:destroy, :default => "Destroy") %>
    110 | 111 |
    112 |
    113 | <%% clean_params %> 114 | <%%= will_paginate @<%= plural_table_name %>, 115 | :inner_window => 0, 116 | :outer_window => 0, 117 | :previous_label => t(:prev, :default => "Previous"), 118 | :next_label => t(:next, :default => "Next"), 119 | :renderer => WillPaginate::ActionView::BootstrapLinkRenderer 120 | %> 121 | 122 |
    123 |
    124 | <%%= link_to ' CSV'.html_safe, <%= namespace_for_route %><%= plural_table_name %>_path(:format => :csv), :class => "btn btn-outline-secondary" %> 125 | <%%= link_to ' XML'.html_safe, <%= namespace_for_route %><%= plural_table_name %>_path(:format => :xml), :class => "btn btn-outline-secondary" %> 126 | <%%= link_to ' JSON'.html_safe, <%= namespace_for_route %><%= plural_table_name %>_path(:format => :json), :class => "btn btn-outline-secondary" %> 127 | <%%= link_to ' PDF'.html_safe, <%= namespace_for_route %><%= plural_table_name %>_path(:format => :pdf), :class => "btn btn-outline-secondary" %> 128 |
    129 |
    130 | <%% end %> 131 |
    132 |
    133 | <%%= search_form_for @q, :url => <%= namespace_for_route + 'search_' + model_pluralize + '_path' %>, :html => { :class => "card bg-light mb-3 search-and-filter" }, :method => :post do |f| %> 134 |
    <%%= t("search_and_filter", :default => "Search & filter") %>
    135 |
    136 |
    137 | <%= render_partial 'app/views/partials/_index_search.html.erb' %> 138 | 139 |
    140 |
    141 | 146 |
    147 |
    148 | <%= render_partial 'app/views/partials/_index_search_default_fields.html.erb' %> 149 |
    150 |
    151 |
    152 |
    153 | <%%= f.submit t(:filter, :default => "Filter"), :class => "btn btn-primary btn-sm btn-block" %> 154 | <%%= link_to t(:cancel, :default => "Cancel"), <%= namespace_for_route %><%= model_pluralize %>_path(:nosearch => "ok"), :class => "btn btn-light btn-sm btn-block" %> 155 |
    156 |
    157 | <%% end %> 158 |
    159 |
    160 |
    161 | -------------------------------------------------------------------------------- /lib/generators/templates/app/assets/javascripts/modernizr.custom.js: -------------------------------------------------------------------------------- 1 | /* Modernizr 2.0.6 (Custom Build) | MIT & BSD 2 | * Build: http://www.modernizr.com/download/#-fontface-backgroundsize-borderimage-borderradius-boxshadow-flexbox-hsla-multiplebgs-opacity-rgba-textshadow-cssanimations-csscolumns-generatedcontent-cssgradients-cssreflections-csstransforms-csstransforms3d-csstransitions-applicationcache-canvas-canvastext-draganddrop-hashchange-history-audio-video-indexeddb-input-inputtypes-localstorage-postmessage-sessionstorage-websockets-websqldatabase-webworkers-geolocation-inlinesvg-smil-svg-svgclippaths-touch-webgl-iepp-cssclasses-teststyles-testprop-testallprops-hasevent-prefixes-domprefixes-load 3 | */ 4 | ;window.Modernizr=function(a,b,c){function H(){e.input=function(a){for(var b=0,c=a.length;b",a,""].join(""),k.id=i,k.innerHTML+=f,g.appendChild(k),h=c(k,a),k.parentNode.removeChild(k);return!!h},w=function(){function d(d,e){e=e||b.createElement(a[d]||"div"),d="on"+d;var f=d in e;f||(e.setAttribute||(e=b.createElement("div")),e.setAttribute&&e.removeAttribute&&(e.setAttribute(d,""),f=C(e[d],"function"),C(e[d],c)||(e[d]=c),e.removeAttribute(d))),e=null;return f}var a={select:"input",change:"input",submit:"form",reset:"form",error:"img",load:"img",abort:"img"};return d}(),x,y={}.hasOwnProperty,z;!C(y,c)&&!C(y.call,c)?z=function(a,b){return y.call(a,b)}:z=function(a,b){return b in a&&C(a.constructor.prototype[b],c)};var G=function(c,d){var f=c.join(""),g=d.length;v(f,function(c,d){var f=b.styleSheets[b.styleSheets.length-1],h=f.cssRules&&f.cssRules[0]?f.cssRules[0].cssText:f.cssText||"",i=c.childNodes,j={};while(g--)j[i[g].id]=i[g];e.touch="ontouchstart"in a||j.touch.offsetTop===9,e.csstransforms3d=j.csstransforms3d.offsetLeft===9,e.generatedcontent=j.generatedcontent.offsetHeight>=1,e.fontface=/src/i.test(h)&&h.indexOf(d.split(" ")[0])===0},g,d)}(['@font-face {font-family:"font";src:url("https://")}',["@media (",o.join("touch-enabled),("),i,")","{#touch{top:9px;position:absolute}}"].join(""),["@media (",o.join("transform-3d),("),i,")","{#csstransforms3d{left:9px;position:absolute}}"].join(""),['#generatedcontent:after{content:"',m,'";visibility:hidden}'].join("")],["fontface","touch","csstransforms3d","generatedcontent"]);r.flexbox=function(){function c(a,b,c,d){a.style.cssText=o.join(b+":"+c+";")+(d||"")}function a(a,b,c,d){b+=":",a.style.cssText=(b+o.join(c+";"+b)).slice(0,-b.length)+(d||"")}var d=b.createElement("div"),e=b.createElement("div");a(d,"display","box","width:42px;padding:0;"),c(e,"box-flex","1","width:10px;"),d.appendChild(e),g.appendChild(d);var f=e.offsetWidth===42;d.removeChild(e),g.removeChild(d);return f},r.canvas=function(){var a=b.createElement("canvas");return!!a.getContext&&!!a.getContext("2d")},r.canvastext=function(){return!!e.canvas&&!!C(b.createElement("canvas").getContext("2d").fillText,"function")},r.webgl=function(){return!!a.WebGLRenderingContext},r.touch=function(){return e.touch},r.geolocation=function(){return!!navigator.geolocation},r.postmessage=function(){return!!a.postMessage},r.websqldatabase=function(){var b=!!a.openDatabase;return b},r.indexedDB=function(){for(var b=-1,c=p.length;++b7)},r.history=function(){return!!a.history&&!!history.pushState},r.draganddrop=function(){return w("dragstart")&&w("drop")},r.websockets=function(){for(var b=-1,c=p.length;++b";return(a.firstChild&&a.firstChild.namespaceURI)==q.svg},r.smil=function(){return!!b.createElementNS&&/SVG/.test(n.call(b.createElementNS(q.svg,"animate")))},r.svgclippaths=function(){return!!b.createElementNS&&/SVG/.test(n.call(b.createElementNS(q.svg,"clipPath")))};for(var I in r)z(r,I)&&(x=I.toLowerCase(),e[x]=r[I](),u.push((e[x]?"":"no-")+x));e.input||H(),A(""),j=l=null,a.attachEvent&&function(){var a=b.createElement("div");a.innerHTML="";return a.childNodes.length!==1}()&&function(a,b){function s(a){var b=-1;while(++b "# Be sure to restart your server when you modify this file." ) 52 | else 53 | puts "============> Engine : You must add `Mime::Type.register_alias \"application/pdf\", :pdf` to your config/initializers/mime_types.rb main app !" 54 | end 55 | end 56 | end 57 | 58 | def generate_assets 59 | stylesheetspath = "app/assets/stylesheets/" 60 | stylesheetspath_dest = "#{stylesheetspath}#{engine_name}" 61 | 62 | # Css 63 | bc_css = [ 64 | "beautiful-scaffold.css.scss", 65 | "bootstrap-wysihtml5.css" 66 | ] 67 | 68 | javascriptspath = "app/assets/javascripts/" 69 | javascriptspath_dest = "#{javascriptspath}#{engine_name}" 70 | 71 | bc_css.each do |path| 72 | copy_file "#{stylesheetspath}#{path}", "#{stylesheetspath_dest}#{path}" 73 | end 74 | copy_file "#{stylesheetspath}application-bs.css", "#{stylesheetspath_dest}application-bs.scss" 75 | 76 | # Jstree theme 77 | directory "#{stylesheetspath}themes", "#{stylesheetspath}#{engine_name}themes" 78 | 79 | if !engine_name.blank? 80 | ['beautiful-scaffold', 81 | 'bootstrap-wysihtml5'].each do |fileassets| 82 | gsub_file File.join(stylesheetspath_dest, "application-bs.scss"), " *= require #{fileassets}", " *= require #{engine_name}#{fileassets}" 83 | end 84 | 85 | # Issue otherwise 86 | gsub_file File.join(stylesheetspath_dest, "application-bs.scss"), '@import "tempusdominus-bootstrap-4.css";', '@import "../tempusdominus-bootstrap-4.css";' 87 | gsub_file File.join(stylesheetspath_dest, "application-bs.scss"), 'require themes/default/style', "require #{engine_name}themes/default/style" 88 | 89 | # treeview 90 | gsub_file File.join(stylesheetspath_dest, 'themes', 'default', 'style.scss'), 'asset-url("themes', "asset-url(\"#{engine_name}themes" 91 | gsub_file File.join(stylesheetspath_dest, 'themes', 'default-dark', 'style.scss'), 'asset-url("themes', "asset-url(\"#{engine_name}themes" 92 | end 93 | 94 | # Js 95 | bc_js = [ 96 | "application-bs.js", 97 | "beautiful_scaffold.js", 98 | "bootstrap-datetimepicker-for-beautiful-scaffold.js", 99 | "jquery-barcode.js", 100 | "jstree.min.js", 101 | "a-wysihtml5-0.3.0.min.js", 102 | "bootstrap-wysihtml5.js", 103 | "fixed_menu.js" 104 | ] 105 | 106 | [bc_js].flatten.each{ |path| 107 | copy_file "#{javascriptspath}#{path}", "#{javascriptspath_dest}#{path}" 108 | } 109 | 110 | if !engine_name.blank? 111 | ['a-wysihtml5-0.3.0.min', 112 | 'bootstrap-datetimepicker-for-beautiful-scaffold', 113 | 'bootstrap-wysihtml5', 114 | 'jstree.min.js', 115 | 'jquery-barcode', 116 | 'beautiful_scaffold', 117 | 'fixed_menu'].each do |fileassets| 118 | gsub_file File.join(javascriptspath_dest, "application-bs.js"), "//= require #{fileassets}", "//= require #{engine_name}#{fileassets}" 119 | end 120 | end 121 | 122 | # Images 123 | dir_image = "app/assets/images" 124 | dir_image_dest = "app/assets/images/#{engine_opt}" 125 | directory dir_image, dir_image_dest 126 | 127 | # Precompile BS assets 128 | path_to_assets_rb = "config/initializers/assets.rb" 129 | if !File.exist?(path_to_assets_rb) && !engine_name.blank? # Engine 130 | path_to_assets_rb = File.join("test", "dummy", "config/initializers/assets.rb") 131 | end 132 | 133 | append_to_file(path_to_assets_rb, "Rails.application.config.assets.precompile += ['#{engine_name}application-bs.css','#{engine_name}application-bs.js']") 134 | if !engine_name.blank? 135 | manifest_prefix = "#{engine_opt}_" 136 | else 137 | manifest_prefix = "" 138 | end 139 | #append_to_file("app/assets/config/#{manifest_prefix}manifest.js", '//= link_directory ../stylesheets/faq .css') 140 | append_to_file("app/assets/config/#{manifest_prefix}manifest.js", '//= link_directory ../javascripts .js') 141 | end 142 | 143 | def generate_layout 144 | template "app/views/layout.html.erb", "app/views/layouts/#{engine_name}beautiful_layout.html.erb" 145 | 146 | gsub_file "app/views/layouts/#{engine_name}beautiful_layout.html.erb", '"layouts/beautiful_menu"', "\"layouts/#{engine_name}beautiful_menu\"" 147 | 148 | if !File.exist?("app/views/layouts/#{engine_name}_beautiful_menu.html.erb") 149 | template "app/views/_beautiful_menu.html.erb", "app/views/layouts/#{engine_name}_beautiful_menu.html.erb" 150 | end 151 | 152 | empty_directory "app/views/#{engine_name}beautiful" 153 | template "app/views/dashboard.html.erb", "app/views/#{engine_name}beautiful/dashboard.html.erb" 154 | copy_file "app/views/_modal_columns.html.erb", "app/views/layouts/#{engine_name}_modal_columns.html.erb" 155 | copy_file "app/views/_mass_inserting.html.erb", "app/views/layouts/#{engine_name}_mass_inserting.html.erb" 156 | 157 | action_ctrl = "#{namespace_for_url}#{model.pluralize}" 158 | 159 | inject_into_file("app/views/layouts/#{engine_name}_beautiful_menu.html.erb", 160 | "\n" + '<%= link_to ' + i18n_t_m_p(model) + '.capitalize, ' + namespace_for_route + model.pluralize + '_path, class: "nav-link #{(params[:controller] == "' + action_ctrl + '" ? "active" : "")}" %>', 161 | :after => "") 162 | end 163 | 164 | def generate_model 165 | generate("model", "#{model} #{beautiful_attr_to_rails_attr.join(' ')} #{@fulltext_field.join(' ')}") 166 | directory "app/models/concerns", "app/models/concerns/#{engine_name}" 167 | 168 | copy_file "app/models/pdf_report.rb", "app/models/#{engine_name}pdf_report.rb" 169 | 170 | if !engine_name.blank? 171 | ['caption_concern', 'default_sorting_concern','fulltext_concern'].each do |f| 172 | path_to_the_concern = "app/models/concerns/#{engine_name}#{f}.rb" 173 | inject_into_file path_to_the_concern, "module #{engine_camel}\n", before: "module #{f.camelcase}" 174 | append_to_file path_to_the_concern, "\nend #endofmodule \n" 175 | end 176 | 177 | path_to_the_pdf_report = "app/models/#{engine_name}pdf_report.rb" 178 | inject_into_file path_to_the_pdf_report, "module #{engine_camel}\n", before: "class PdfReport" 179 | append_to_file path_to_the_pdf_report, "\nend #endofmodule \n" 180 | end 181 | 182 | gsub_file "app/models/#{engine_name}#{model}.rb", 'ActiveRecord::Base', 'ApplicationRecord' # Rails 4 -> 5 183 | inject_into_file("app/models/#{engine_name}#{model}.rb",' 184 | 185 | include DefaultSortingConcern 186 | include FulltextConcern 187 | include CaptionConcern 188 | 189 | cattr_accessor :fulltext_fields do 190 | [' + fulltext_attribute.map{ |e| ('"' + e + '"') }.join(",") + '] 191 | end 192 | 193 | def self.permitted_attributes 194 | return ' + attributes_without_type.map{ |attr| ":#{attr}" }.join(",") + ' 195 | end', :after => "class #{model_camelize} < ApplicationRecord") 196 | 197 | end 198 | 199 | def add_to_model 200 | add_relation 201 | end 202 | 203 | def generate_controller 204 | beautiful_ctrl_path = "app/controllers/#{engine_name}beautiful_controller.rb" 205 | copy_file "app/controllers/master_base.rb", beautiful_ctrl_path 206 | # beautiful_controller in the context of engine 207 | if !engine_name.empty? 208 | inject_into_file beautiful_ctrl_path, "module #{engine_camel}\n", before: "class BeautifulController" 209 | #gsub_file beautiful_ctrl_path, '< ApplicationController', "< ::#{engine_camel}::ApplicationController" # Rails 4 -> 5 'BeautifulController < ApplicationController' 210 | append_to_file beautiful_ctrl_path, "end #endofmodule \n" 211 | 212 | gsub_file beautiful_ctrl_path, 'layout "beautiful_layout"', "layout \"#{engine_name}beautiful_layout\"" 213 | end 214 | dirs = ['app', 'controllers', engine_name, options[:namespace]].compact 215 | # Avoid to remove app/controllers directory (https://github.com/rivsc/Beautiful-Scaffold/issues/6) 216 | empty_directory File.join(dirs) if !options[:namespace].blank? 217 | dest_ctrl_file = File.join([dirs, "#{model_pluralize}_controller.rb"].flatten) 218 | template "app/controllers/base.rb", dest_ctrl_file 219 | end 220 | 221 | def generate_helper 222 | dest_bs_helper_file = "app/helpers/#{engine_name}beautiful_helper.rb" 223 | template "app/helpers/beautiful_helper.rb", dest_bs_helper_file 224 | 225 | dirs = ['app', 'helpers', engine_name, options[:namespace]].compact 226 | empty_directory File.join(dirs) 227 | dest_helper_file = File.join([dirs, "#{model_pluralize}_helper.rb"].flatten) 228 | template "app/helpers/model_helper.rb", dest_helper_file 229 | end 230 | 231 | def generate_views 232 | namespacedirs = ["app", "views", engine_name, options[:namespace]].compact 233 | empty_directory File.join(namespacedirs) 234 | 235 | dirs = [namespacedirs, model_pluralize] 236 | empty_directory File.join(dirs) 237 | 238 | [available_views, 'treeview'].flatten.each do |view| 239 | filename = view + ".html.erb" 240 | current_template_path = File.join([dirs, filename].flatten) 241 | empty_template_path = File.join(["app", "views", filename].flatten) 242 | template empty_template_path, current_template_path 243 | 244 | gsub_file current_template_path, '"layouts/modal_columns"', "\"layouts/#{engine_name}modal_columns\"" 245 | gsub_file current_template_path, '"layouts/mass_inserting"', "\"layouts/#{engine_name}mass_inserting\"" 246 | end 247 | 248 | copy_file "app/views/_form_habtm_tag.html.erb", "app/views/layouts/#{engine_name}_form_habtm_tag.html.erb" 249 | end 250 | 251 | def install_ransack_intializer 252 | copy_file "app/initializers/ransack.rb", "config/initializers/ransack.rb" 253 | end 254 | 255 | def install_willpaginate_renderer_for_bootstrap 256 | copy_file "app/initializers/link_renderer.rb", "config/initializers/link_renderer.rb" 257 | end 258 | 259 | def routes 260 | myroute = < 'beautiful#dashboard' 262 | match ':model_sym/select_fields' => 'beautiful#select_fields', as: :select_fields, via: [:get, :post] 263 | 264 | concern :bs_routes do 265 | collection do 266 | post :batch 267 | get :treeview 268 | match :search_and_filter, action: :index, as: :search, via: [:get, :post] 269 | end 270 | member do 271 | post :treeview_update 272 | end 273 | end 274 | 275 | # Add route with concerns: :bs_routes here # Do not remove 276 | EOF 277 | 278 | inject_into_file("config/routes.rb", myroute, :after => "routes.draw do\n") 279 | 280 | myroute = "\n " 281 | myroute += "namespace :#{namespace_alone} do\n " if !namespace_alone.blank? 282 | myroute += "resources :#{model_pluralize}, concerns: :bs_routes\n " 283 | myroute += "end\n" if !namespace_alone.blank? 284 | 285 | inject_into_file("config/routes.rb", myroute, :after => ":bs_routes here # Do not remove") 286 | end 287 | end 288 | --------------------------------------------------------------------------------