', :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 |
2 |
3 |
4 |
8 |
9 | <%%= form_for('<%= model %>', :as => '<%= model %>', :url => password_path('<%= model %>'), :html => { :method => :post, :class => "form-horizontal" }) do |f| %>
10 |
16 |
21 | <%% end %>
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 | "#{f.check_box(col.to_sym, {}, true, false)}#{t(i18n_translate_path(model_name, col), default: "#{model_name}.#{col}").capitalize} ".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 |
26 |
34 |
35 |
36 | Home
37 | <%%= render :partial => "layouts/beautiful_menu" %>
38 |
39 |
40 |
41 |
42 |
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 |
20 | <%- elsif (datetime_field = @beautiful_attributes.include?(attribute.name + ':datetime')) || @beautiful_attributes.include?(attribute.name + ':date') -%>
21 |
53 | <%- elsif @beautiful_attributes.include?(attribute.name + ':color') -%>
54 |
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 |
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 | <%%= t(:batch, :default => "Batch") %>
42 |
43 |
44 | <%%= t(:destroy, :default => "Destroy") %>
45 | <%%= t(:touch, :default => "Touch") %>
46 | <%= render_partial 'app/views/partials/_index_batch.html.erb' %>
47 |
48 |
49 | <%%= t(:process, :default => "Process") %>
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 |
70 | " >
71 |
72 | " 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 |
75 | <%= render_partial 'app/views/partials/_index_header.html.erb' %>
76 |
77 | ", "created_at") %> class="bs-col-created_at">
78 | <%%= sorting_header("<%= singular_table_name %>", "created_at", "<%= namespace_alone %>") %>
79 |
80 | ", "updated_at") %> class="bs-col-updated_at">
81 | <%%= sorting_header("<%= singular_table_name %>", "updated_at", "<%= namespace_alone %>") %>
82 |
83 |
84 | " data-present-description="<%%= t(:help_checkallall_description, :default => "Check all elements, that match current filter even if they are not visible on the page") %>" data-present-order="4">
85 | <%%= check_box_tag :checkallelt, 'all' %>
86 | <%%= pluralize(@<%= singular_table_name %>_scope.count, <%= i18n_t_m(singular_table_name) %>, <%= i18n_t_m_p(singular_table_name) %>) %>
87 |
88 |
89 |
90 |
91 |
92 | <%% @<%= plural_table_name %>.each do |<%= singular_table_name %>| %>
93 | ">
94 | <%%= <%= singular_table_name %>.id %>
95 | <%%= check_box_tag "ids[]",<%= singular_table_name %>.id, false, :class => 'cbbatch' %>
96 | <%= render_partial 'app/views/partials/_index_column.html.erb' %>
97 | ", "created_at") %> class="bs-col-created_at <%%= align_attribute("datetime") %>">
98 | <%%= l(<%= singular_table_name %>.created_at, :format => :long) %>
99 |
100 | ", "updated_at") %> class="bs-col-updated_at <%%= align_attribute("datetime") %>">
101 | <%%= l(<%= singular_table_name %>.updated_at, :format => :long) %>
102 |
103 | <%%= link_to ' '.html_safe, <%= namespace_for_route %><%= singular_table_name %>_path(<%= singular_table_name %>), :title => t(:show, :default => "Show") %>
104 | <%%= link_to ' '.html_safe, edit_<%= namespace_for_route %><%= singular_table_name %>_path(<%= singular_table_name %>), :title => t(:edit, :default => "Edit") %>
105 | <%%= 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") %>
106 |
107 | <%% end %>
108 |
109 |
110 |
111 |
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 |
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 |
--------------------------------------------------------------------------------