├── .document ├── .gitignore ├── .ruby-version ├── Gemfile ├── Gemfile.lock ├── LICENSE.txt ├── README.md ├── Rakefile ├── VERSION ├── bin └── scaffold_pico ├── certs └── gudata.pem ├── checksum └── #{built_gem_path}.sha512 ├── create_checksum.rb ├── doc └── screenshot_index.jpg ├── generate_scaffold_with_all_options.sh ├── lib ├── scaffold │ ├── cli.rb │ ├── erb_context.rb │ ├── generators │ │ ├── base_generator.rb │ │ ├── controller_generator.rb │ │ ├── fabricator_generator.rb │ │ ├── locales_generator.rb │ │ ├── models_generator.rb │ │ ├── routes_generator.rb │ │ └── views_generator.rb │ ├── main.rb │ ├── models │ │ └── resource.rb │ ├── rails.rb │ ├── services │ │ ├── controller.rb │ │ ├── nested_in_resources.rb │ │ ├── path.rb │ │ ├── resource.rb │ │ ├── route.rb │ │ └── view.rb │ └── template_engines │ │ └── slim.rb ├── scaffold_pico.rb └── templates │ └── pico │ ├── controller.rb.erb │ ├── fabricators │ └── fabrication.rb.erb │ ├── locales │ ├── bg.scaffold_pico.yml.erb │ └── en.scaffold_pico.yml.erb │ ├── model.rb.erb │ ├── search.rb.erb │ └── views │ ├── materialize │ └── slim │ │ ├── _form.html.slim.erb │ │ ├── edit.html.slim.erb │ │ ├── index.html.slim.erb │ │ ├── new.html.slim.erb │ │ └── show.html.slim.erb │ ├── twitter_bootstrap_4x │ └── slim │ │ ├── _form.html.slim.erb │ │ ├── edit.html.slim.erb │ │ ├── index.html.slim.erb │ │ ├── new.html.slim.erb │ │ └── show.html.slim.erb │ └── zurb │ └── slim │ ├── _form.html.slim.erb │ ├── edit.html.slim.erb │ ├── index.html.slim.erb │ ├── new.html.slim.erb │ └── show.html.slim.erb ├── rerun.sh ├── scaffold_pico.gemspec └── test ├── helper.rb └── test_scaffold_pico.rb /.document: -------------------------------------------------------------------------------- 1 | lib/**/*.rb 2 | bin/* 3 | - 4 | features/**/*.feature 5 | LICENSE.txt 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # rcov generated 2 | coverage 3 | coverage.data 4 | 5 | # rdoc generated 6 | rdoc 7 | 8 | # yard generated 9 | doc 10 | .yardoc 11 | 12 | # bundler 13 | .bundle 14 | 15 | # jeweler generated 16 | pkg 17 | 18 | # Have editor/IDE/OS specific files you need to ignore? Consider using a global gitignore: 19 | # 20 | # * Create a file at ~/.gitignore 21 | # * Include files you want ignored 22 | # * Run: git config --global core.excludesfile ~/.gitignore 23 | # 24 | # After doing this, these files will be ignored in all your git projects, 25 | # saving you from having to 'pollute' every project you touch with them 26 | # 27 | # Not sure what to needs to be ignored for particular editors/OSes? Here's some ideas to get you started. (Remember, remove the leading # of the line) 28 | # 29 | # For MacOS: 30 | # 31 | #.DS_Store 32 | 33 | # For TextMate 34 | #*.tmproj 35 | #tmtags 36 | 37 | # For emacs: 38 | #*~ 39 | #\#* 40 | #.\#* 41 | 42 | # For vim: 43 | #*.swp 44 | 45 | # For redcar: 46 | #.redcar 47 | 48 | # For rubinius: 49 | #*.rbc 50 | *.gem 51 | 52 | deploy.sh 53 | -------------------------------------------------------------------------------- /.ruby-version: -------------------------------------------------------------------------------- 1 | ruby-2.6.2 2 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "http://rubygems.org" 2 | # Add dependencies required to use your gem here. 3 | # Example: 4 | # gem "activesupport", ">= 2.3.5" 5 | 6 | gem "activesupport" 7 | gem 'choice' 8 | gem 'require_all' 9 | 10 | # Add dependencies to develop your gem here. 11 | # Include everything needed to run rake, tests, features, etc. 12 | group :development do 13 | gem "bundler", "~> 1.0" 14 | # gem "jeweler", "~> 2.0.1" 15 | gem "jeweler", github: 'technicalpickles/jeweler' 16 | end 17 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GIT 2 | remote: git://github.com/technicalpickles/jeweler.git 3 | revision: 2ab86309fc2494ba2a4e9c86c514742cd4f681c2 4 | specs: 5 | jeweler (2.3.9) 6 | builder 7 | bundler 8 | git (>= 1.2.5) 9 | github_api (~> 0.16.0) 10 | highline (>= 1.6.15) 11 | nokogiri (>= 1.5.10) 12 | psych 13 | rake 14 | rdoc 15 | semver2 16 | 17 | GEM 18 | remote: http://rubygems.org/ 19 | specs: 20 | activesupport (6.0.3) 21 | concurrent-ruby (~> 1.0, >= 1.0.2) 22 | i18n (>= 0.7, < 2) 23 | minitest (~> 5.1) 24 | tzinfo (~> 1.1) 25 | zeitwerk (~> 2.2, >= 2.2.2) 26 | addressable (2.4.0) 27 | builder (3.2.4) 28 | choice (0.2.0) 29 | concurrent-ruby (1.1.6) 30 | descendants_tracker (0.0.4) 31 | thread_safe (~> 0.3, >= 0.3.1) 32 | faraday (0.9.2) 33 | multipart-post (>= 1.2, < 3) 34 | git (1.7.0) 35 | rchardet (~> 1.8) 36 | github_api (0.16.0) 37 | addressable (~> 2.4.0) 38 | descendants_tracker (~> 0.0.4) 39 | faraday (~> 0.8, < 0.10) 40 | hashie (>= 3.4) 41 | mime-types (>= 1.16, < 3.0) 42 | oauth2 (~> 1.0) 43 | hashie (4.1.0) 44 | highline (2.0.3) 45 | i18n (1.8.2) 46 | concurrent-ruby (~> 1.0) 47 | jwt (2.2.1) 48 | mime-types (2.99.3) 49 | mini_portile2 (2.4.0) 50 | minitest (5.14.1) 51 | multi_json (1.14.1) 52 | multi_xml (0.6.0) 53 | multipart-post (2.1.1) 54 | nokogiri (1.10.9) 55 | mini_portile2 (~> 2.4.0) 56 | oauth2 (1.4.4) 57 | faraday (>= 0.8, < 2.0) 58 | jwt (>= 1.0, < 3.0) 59 | multi_json (~> 1.3) 60 | multi_xml (~> 0.5) 61 | rack (>= 1.2, < 3) 62 | psych (3.1.0) 63 | rack (2.2.2) 64 | rake (13.0.1) 65 | rchardet (1.8.0) 66 | rdoc (6.2.1) 67 | require_all (3.0.0) 68 | semver2 (3.4.2) 69 | thread_safe (0.3.6) 70 | tzinfo (1.2.7) 71 | thread_safe (~> 0.1) 72 | zeitwerk (2.3.0) 73 | 74 | PLATFORMS 75 | ruby 76 | 77 | DEPENDENCIES 78 | activesupport 79 | bundler (~> 1.0) 80 | choice 81 | jeweler! 82 | require_all 83 | 84 | BUNDLED WITH 85 | 1.17.2 86 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016 gudata 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Scaffolding done right 2 | Soon or later the scaffold from which you start becomes your basic application. I have tried a lot of dynamic administration tooling and soon or later I land on the moment to work and invest time how to make my code work with the admin tool instead of develop my logic. 3 | 4 | I beleave that the appication is born and gets old. Starting with great foundation gives you a boost and then the application become mature without the pain. 5 | 6 | No learning curve - only what you already know - Ruby on Rails & scaffold 7 | 8 | * Support namespaces for the model and the controllers 9 | * You can generate scaffolds for nested resources 10 | * Can override every file per project 11 | * Supports different css frameworks 12 | - twitter-boostrap-4.x 13 | - materializecss 14 | - zurb 15 | * Generate fabricators 16 | * Define #index, #edit/new and search during the scafold creation 17 | * You can specify includes/joins for the #index action 18 | * Implement basic search service 19 | 20 | # SYNOPSIS 21 | 22 | scaffold_pico \ 23 | -m Admin::Vector -n administration -b AdminController \ 24 | --nested_in_resources Assets::VectorFrame \ 25 | --fields name: description:text featured:boolean license:belongs_to group_id:integer svg:file \ 26 | --index-fields id name imported featured \ 27 | --search-fields name aspect_ratio license created_at \ 28 | --fabrication \ 29 | --services_folder=services \ 30 | --debug --css_framework=materialize \ 31 | --custom_inflection vector vectors 32 | 33 | 34 | ![alt tag](https://raw.githubusercontent.com/gudata/scaffold_pico/master/doc/screenshot_index.jpg) 35 | 36 | 37 | 38 | # Install 39 | 40 | This project assumes that you use simple_form and kaminari, but if you have other preferences you can change them. 41 | 42 | You should already have in your Gemfile something like 43 | 44 | gem 'kaminari' 45 | gem 'slim-rails' 46 | gem 'simple_form' 47 | 48 | # And one of those 49 | # 50 | # gem 'materialize-sass' 51 | # gem 'foundation-rails' 52 | 53 | There is no need to have it in your gem file. 54 | 55 | gem install scaffold_pico 56 | 57 | Scaffold Pico is cryptographically signed. To be sure the gem you install hasn’t been tampered with: 58 | 59 | Add my public key (if you haven’t already) as a trusted certificate 60 | 61 | gem cert --add <(curl -Ls https://raw.githubusercontent.com/gudata/scaffold_pico/master/certs/gudata.pem) 62 | 63 | gem install scaffold_pico -P MediumSecurity 64 | 65 | The MediumSecurity trust profile will verify signed gems, but allow the installation of unsigned dependencies. 66 | 67 | This is necessary in case not all of Scaffold Pico’s dependencies are signed, so we cannot use HighSecurity. 68 | 69 | 70 | 71 | 72 | # Overriding 73 | If you want to change something you can override/change 74 | 75 | RAILS_ROOT/config/locales/en.scaffold_pico.yml 76 | RAILS_ROOT/lib/templates/pico/ 77 | /controller.rb 78 | /search.rb 79 | /fabricators 80 | fabrication.rb.erb 81 | /views/{materialize|zurb} <<< Note ! 82 | /materialize 83 | _form.html.slim.erb 84 | edit.html.slim.erb 85 | index.html.slim.erb 86 | new.html.slim.erb 87 | show.html.slim.erb 88 | /zurb 89 | _form.html.slim.erb 90 | edit.html.slim.erb 91 | index.html.slim.erb 92 | new.html.slim.erb 93 | show.html.slim.erb 94 | 95 | # Develop 96 | 97 | Clone the repo in ~/scaffold_pico or whatever, then in some rails project run the binary like this 98 | 99 | export SCAFFOLD_PICO_HOME=~/scaffold_pico 100 | ruby -I $SCAFFOLD_PICO_HOME/lib $SCAFFOLD_PICO_HOME/bin/scaffold_pico \ 101 | --css_framework=materialize \ 102 | --template=slim \ 103 | -m User -n Admin -b AdminController \ 104 | --fields name:string 105 | 106 | # Known Issues 107 | If you can't start scaffold_pico you might need to update your bundler 108 | 109 | gem update bundler 110 | 111 | Help please: For some of the generated models/views there are extra blank links. It would be great if someone knows how to parse the erb and skip the new lines 112 | 113 | You can't generate scaffolds for a mounted named engines. I don't know if we need this feature. 114 | 115 | 116 | ## License MIT 117 | 118 | Copyright (c) 2017 gudata. See LICENSE.txt for further details. 119 | 120 | [![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fgudata%2Fscaffold_pico.svg?type=shield)](https://app.fossa.io/projects/git%2Bgithub.com%2Fgudata%2Fscaffold_pico?ref=badge_shield) 121 | 122 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | require 'rubygems' 4 | require 'bundler' 5 | begin 6 | Bundler.setup(:default, :development) 7 | rescue Bundler::BundlerError => e 8 | $stderr.puts e.message 9 | $stderr.puts "Run `bundle install` to install missing gems" 10 | exit e.status_code 11 | end 12 | require 'rake' 13 | 14 | require 'jeweler' 15 | Jeweler::Tasks.new do |gem| 16 | # gem is a Gem::Specification... see http://guides.rubygems.org/specification-reference/ for more options 17 | gem.name = "scaffold_pico" 18 | gem.homepage = "http://github.com/gudata/scaffold_pico" 19 | gem.license = "MIT" 20 | gem.summary = %Q{Scaffold should be simple} 21 | gem.description = %Q{Scaffolding} 22 | gem.email = "i.bardarov@gmail.com" 23 | gem.authors = ["gudata"] 24 | gem.executables = ['scaffold_pico'] 25 | gem.files = Dir.glob('lib/**/*.rb') + Dir.glob('lib/**/*.erb') 26 | # dependencies defined in Gemfile 27 | end 28 | Jeweler::RubygemsDotOrgTasks.new 29 | 30 | require 'rake/testtask' 31 | Rake::TestTask.new(:test) do |test| 32 | test.libs << 'lib' << 'test' 33 | test.pattern = 'test/**/test_*.rb' 34 | test.verbose = true 35 | end 36 | 37 | task :default => :test 38 | 39 | require 'rdoc/task' 40 | Rake::RDocTask.new do |rdoc| 41 | version = File.exist?('VERSION') ? File.read('VERSION') : "" 42 | 43 | rdoc.rdoc_dir = 'rdoc' 44 | rdoc.title = "scaffold_pico #{version}" 45 | rdoc.rdoc_files.include('README*') 46 | rdoc.rdoc_files.include('lib/**/*.rb') 47 | end 48 | -------------------------------------------------------------------------------- /VERSION: -------------------------------------------------------------------------------- 1 | 2.2.2 2 | -------------------------------------------------------------------------------- /bin/scaffold_pico: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require_relative '../lib/scaffold/cli' 4 | 5 | cli = Scaffold::CLI.new 6 | cli.run 7 | -------------------------------------------------------------------------------- /certs/gudata.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIEQDCCAqigAwIBAgIBATANBgkqhkiG9w0BAQsFADAlMSMwIQYDVQQDDBppLmJh 3 | cmRhcm92L0RDPWdtYWlsL0RDPWNvbTAeFw0xODA4MTAxOTM4MjZaFw0xOTA4MTAx 4 | OTM4MjZaMCUxIzAhBgNVBAMMGmkuYmFyZGFyb3YvREM9Z21haWwvREM9Y29tMIIB 5 | ojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAr8OaYhHdQILEsF0DBfb4ntCS 6 | AaxE06XJ3cYyAs4lXdQ7ynewFtyKapafpEdqzzzpif+qF3tMpAwtISylSMw5Vktx 7 | GSyPo7tnu1yztgOk0PHG/KiJU3FxZiLtMJ1aCsHkOThCkwpp01mROvnbHXr5f7YF 8 | lgg7PvQVY52ayP0yo0ZX2EbsZS+lRefJNDZ3WZe9JgiBkVMnmVZmDw1Wmcx+Pyy9 9 | ADNl67De6qhdd1Jf0IciD8IGRzL5MFf8kHSMLYULhHby1HuuCJtcbO9uvcwCC/Dz 10 | cBhi0d32sZErBUk/24Dzfe/7z9+DCdppuYUkQ9IBtbKbPJm1TwDfc23vEl7iCCwH 11 | bFasWZvX5rSDKniJRP+QZS/dABTdZ/9Q2QVyaZHlMt0moS9YxZv1jloplK0DLoSM 12 | ugFVKUVt8phqWOlLzmN3RxFdhsqvcImAbGmkZd6lhI5FQftUoc8YG4K+kW9401gm 13 | o+5CfsFm4ydANM1pb54ZEVmUyjJLzDalZoNZ54GZAgMBAAGjezB5MAkGA1UdEwQC 14 | MAAwCwYDVR0PBAQDAgSwMB0GA1UdDgQWBBTlOCXeafNJoOswL4uxcyXtvTD/UzAf 15 | BgNVHREEGDAWgRRpLmJhcmRhcm92QGdtYWlsLmNvbTAfBgNVHRIEGDAWgRRpLmJh 16 | cmRhcm92QGdtYWlsLmNvbTANBgkqhkiG9w0BAQsFAAOCAYEAMuxWslyT3FqJciSV 17 | 5hDuEO7fmxuGGhcVHpuTcYd4lmwWJEwau0dj08J0d6v7TWu8pv0tV2wkT+7JE5J6 18 | fQcWCKezCUC55IUqageAXXUUTIwzlVysWqEajupNQmy5vDry3eHQLrHUx0FQjmJS 19 | K3C0eWWF7rHGjPr4l6yjSgLli9au6qYmZWQ2o1+AY6Jiu16Xr+DS7WKx7ohIeV7/ 20 | 1mPDGxk5xgFXwG9Bnk1xNj5WptY/14AlQnPF0RFoS2fQi0xPr6Ek8Y2rxdEDj5CS 21 | 694ufoeUeMB2njHLVm4XkGzq/HXZYcJxGLf8K6/3uyGmnTP6lfpHGEy5mmIuhdVL 22 | DUR1r1GKCf7LUfHwFEZHeRUBYv1VBjIhdHO9U7pHwz176aCmgMwGGE4dWyH6uI+j 23 | 5LR4UjQ3akf4/MbEJeoM8Sc0GqM+9S8xL/NpZj+ynKHmvI6OuhIq48V3mb2fcLev 24 | hB8NP2TLfsvCr2oKZveeA5SHCWlWzyDwUFu7irtaHqGoBGOp 25 | -----END CERTIFICATE----- 26 | -------------------------------------------------------------------------------- /checksum/#{built_gem_path}.sha512: -------------------------------------------------------------------------------- 1 | 01b090f4419ee7ae3d12348a06c12d5a9d272261ee56c690fb941bbe8765691cb1e0c3a75ce30414b0a5bdf1637cc286114de0824ca623a4f4756c06f1afa010 -------------------------------------------------------------------------------- /create_checksum.rb: -------------------------------------------------------------------------------- 1 | require 'digest/sha2' 2 | version = File.read('VERSION').chomp 3 | built_gem_path = "scaffold_pico-#{version}.gem" 4 | puts "Calc for #{built_gem_path}" 5 | checksum = Digest::SHA512.new.hexdigest(File.read(built_gem_path)) 6 | checksum_path = 'checksum/#{built_gem_path}.sha512' 7 | File.open(checksum_path, 'w' ) {|f| f.write(checksum) } 8 | -------------------------------------------------------------------------------- /doc/screenshot_index.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gudata/scaffold_pico/04bdf0213f51e6743da584e31944cafe7ae569cc/doc/screenshot_index.jpg -------------------------------------------------------------------------------- /generate_scaffold_with_all_options.sh: -------------------------------------------------------------------------------- 1 | export SCAFFOLD_PICO_HOME=~/scaffold_pico 2 | ruby -I $SCAFFOLD_PICO_HOME/lib $SCAFFOLD_PICO_HOME/bin/scaffold_pico \ 3 | -m Admin::Vector -n administration -b AdminController \ 4 | --fields name: description:text featured:boolean license:belongs_to group_id:integer svg:file \ 5 | --index-fields id name imported featured \ 6 | --search-fields name aspect_ratio license created_at \ 7 | --fabrication \ 8 | --services_folder=services \ 9 | --debug --css_framework=materialize --force 10 | # --nested_in_resources \ 11 | -------------------------------------------------------------------------------- /lib/scaffold/cli.rb: -------------------------------------------------------------------------------- 1 | require_relative '../scaffold_pico' 2 | require 'choice' 3 | 4 | module Scaffold 5 | class CLI 6 | 7 | def initialize 8 | Choice.options do 9 | header 'Synopsys: scaffold_pico.rb -m User -n Admin --includes :roles :company --joins :roles :company --fields name:string --fabrication' 10 | separator '' 11 | header 'Specific options:' 12 | 13 | option :debug do 14 | short '-d' 15 | long '--debug' 16 | desc 'print some debug info' 17 | end 18 | 19 | option :force do 20 | long '--force' 21 | desc 'Overwrite without asking questions' 22 | end 23 | 24 | option :fabrication do 25 | long '--fabrication' 26 | desc 'Generate fabrication fabricator https://github.com/paulelliott/fabrication' 27 | end 28 | 29 | option :controller_namespaces do 30 | short '-n' 31 | long '--controller_namespaces=namespace1/namespace2' 32 | validate /\A(\w+(?:\/\w+)*)\z/ 33 | desc 'Optional namespaces for the controllers. Example: -n admin/secret_area. ' 34 | end 35 | 36 | option :nested_in_resources do 37 | short '-nr' 38 | long '--nested_in_resources=Reports::Client/Building' 39 | validate /\A(?:::)?(?:(?:[A-Z]\w*(?:::[A-Z]\w*)*)\/?)*\z/ 40 | desc 'Optional nest resource in other resources. Example: -nr Reports::Client/Building -m Room' 41 | end 42 | 43 | option :base_controller do 44 | short '-b' 45 | long '--base_controller=AdminController' 46 | validate /((::)?([A-Z])+[a-z]*)*\z/ 47 | desc 'Optional base controller. Example: -b Admin::BaseController' 48 | end 49 | 50 | option :model, :required => true do 51 | short '-m' 52 | long '--model=SuperAdmin::User' 53 | desc 'The model. It could be with modules. Example: ModuleA::ModuleB::SomeClassName' 54 | validate /\A(?:::)?([A-Z]\w*(?:::[A-Z]\w*)*)\z/ 55 | end 56 | 57 | option :no_model do 58 | long '--no-model' 59 | desc 'Do not generate file. It can help when you need to recreate the views on a model which already exists and have some code' 60 | end 61 | 62 | option :includes do 63 | long '--includes=*INCLUDES' 64 | desc 'add .includes(*includes) into the collection request, we have join also' 65 | default [] 66 | end 67 | 68 | option :joins do 69 | long '--joins=*JOINS' 70 | desc 'add .joins(*joins) into the collection request, we have includes also' 71 | default [] 72 | end 73 | 74 | option :fields do 75 | long '-f' 76 | long '--fields *FIELDS' 77 | desc 'title:string library:belongs_to body:text published:boolean amount:decimal attachment:file tracking_id:integer:uniq ' 78 | end 79 | 80 | option :index_fields do 81 | long '-i' 82 | long '--index-fields *FIELDS' 83 | desc 'The fields you wish to see on the index page. Example: --index-fields title body published amount tracking_id' 84 | end 85 | 86 | option :search_fields do 87 | long '-s' 88 | long '--search-fields *FIELDS' 89 | desc 'Fields on which the search will be done. Example: --search-fields title body published amount tracking_id' 90 | end 91 | 92 | option :custom_inflection do 93 | long '--custom_inflection *FIELDS' 94 | desc 'Custom singular and plural form. Example: --custom_inflection campus campuses' 95 | end 96 | 97 | separator '' 98 | separator 'Defaults: ' 99 | 100 | option :template do 101 | long '-t' 102 | long '--template template' 103 | validate /\A(slim)\z/ 104 | default 'slim' 105 | desc 'slim' 106 | end 107 | 108 | option :css_framework do 109 | long '-c' 110 | long '--css_framework zurb' 111 | default 'twitter_bootstrap_4x' 112 | validate /\A(zurb|materialize|twitter_bootstrap_4x)\z/ 113 | desc 'zurb, materialize, twitter_bootstrap_4x' 114 | end 115 | 116 | option :services_folder do 117 | long '--services_folder=services' 118 | validate /\A(\w+(?:\/\w+)*)\z/ 119 | default 'services' 120 | desc 'Where to put the search model. Example: --services=actions' 121 | end 122 | 123 | separator '' 124 | separator 'Common options: ' 125 | 126 | option :help do 127 | long '--help' 128 | desc 'Show this message' 129 | end 130 | 131 | end 132 | end 133 | 134 | def run 135 | if Choice[:model] 136 | add_custom_inflection(Choice.choices[:custom_inflection]) if Choice.choices[:custom_inflection] 137 | scaffold = Scaffold::Main.new(Choice.choices) 138 | scaffold.run 139 | else 140 | puts "try #{$0} --help" 141 | end 142 | end 143 | 144 | private 145 | 146 | def add_custom_inflection(inflection) 147 | ActiveSupport::Inflector.inflections do |inflect| 148 | inflect.irregular inflection.first, inflection.last 149 | end 150 | end 151 | end 152 | end 153 | -------------------------------------------------------------------------------- /lib/scaffold/erb_context.rb: -------------------------------------------------------------------------------- 1 | class ErbContext 2 | def initialize(hash) 3 | hash.each_pair do |key, value| 4 | instance_variable_set('@' + key.to_s, value) 5 | end 6 | end 7 | 8 | def get_binding 9 | binding 10 | end 11 | end -------------------------------------------------------------------------------- /lib/scaffold/generators/base_generator.rb: -------------------------------------------------------------------------------- 1 | require 'readline' 2 | 3 | module Scaffold 4 | module Generators 5 | class BaseGenerator 6 | @@aways_ovewrite = false 7 | 8 | def initialize rails 9 | @rails = rails 10 | end 11 | 12 | def objectify 13 | OpenStruct.new(vars).instance_eval { binding } 14 | end 15 | 16 | # where is the root of the gem 17 | def root 18 | Scaffold.root 19 | end 20 | 21 | def project_root 22 | Dir.pwd 23 | end 24 | 25 | # Check to see if it is overridden and use it 26 | def find_root *segments 27 | original = File.join(root, segments) 28 | overridden = File.join(project_root, segments) 29 | File.exists?(overridden) ? overridden : original 30 | end 31 | 32 | # the root of the templates 33 | def templates 34 | 'lib/templates/pico/' 35 | end 36 | 37 | def ask(prompt="", newline=false) 38 | prompt += "\n" if newline 39 | Readline.readline(prompt, true).squeeze(" ").strip 40 | end 41 | 42 | def write_with_confirmation(target_file_path, content) 43 | if !File.exists?(target_file_path) || @rails.choice[:force].present? 44 | IO.write(target_file_path, content) 45 | return 46 | end 47 | 48 | answer = if @@aways_ovewrite 49 | 'a' 50 | else 51 | ask("#{target_file_path} exists, overwrite? [yaN]").downcase 52 | end 53 | 54 | @@aways_ovewrite = answer == 'a' 55 | 56 | IO.write(target_file_path, content) if answer == 'y' || @@aways_ovewrite 57 | end 58 | 59 | def parse_template(content, context_hash) 60 | # http://www.stuartellis.eu/articles/erb/ 61 | # content = ::ERB.new(content, nil, '-').result(@params.instance_eval{ binding })#.gsub(/\s+\n$/, "") 62 | 63 | # content = ::ERB.new(content, 0, '>').result(ErbContext.new(context_hash).get_binding) 64 | # https://github.com/jeremyevans/erubi 65 | content = ::ERB.new(content, 0, '-').result(ErbContext.new(context_hash).get_binding) 66 | end 67 | 68 | end 69 | end 70 | end -------------------------------------------------------------------------------- /lib/scaffold/generators/controller_generator.rb: -------------------------------------------------------------------------------- 1 | module Scaffold 2 | module Generators 3 | class ControllerGenerator < Scaffold::Generators::BaseGenerator 4 | 5 | def generate 6 | controller_file_path = create_path(@rails.controller.file_name) 7 | 8 | # puts "Creating #{controller_file_path}" 9 | 10 | filepath = find_root(templates, 'controller.rb.erb') 11 | content = File.read(filepath) 12 | 13 | content = parse_template(content, {rails: @rails}) 14 | 15 | # puts content 16 | write_with_confirmation(controller_file_path, content) 17 | end 18 | 19 | def create_path file_name 20 | controller_path = File.join(Dir.pwd, 'app', 'controllers', @rails.controller.namespaces_as_path) 21 | controller_file_path = File.join(controller_path, file_name) 22 | FileUtils.mkpath(controller_path) 23 | controller_file_path 24 | end 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /lib/scaffold/generators/fabricator_generator.rb: -------------------------------------------------------------------------------- 1 | module Scaffold 2 | module Generators 3 | class FabricatorGenerator < Scaffold::Generators::BaseGenerator 4 | def generate 5 | fabricators_path = create_fabricators_path 6 | source_file_name = "fabrication.rb.erb" 7 | target_file_name = "#{@rails.resource.name}_fabricator.rb" 8 | source_file_path = find_root(templates, 'fabricators', source_file_name) 9 | content = File.read(source_file_path) 10 | 11 | content = parse_template(content, {rails: @rails}) 12 | 13 | target_file_path = File.join(fabricators_path, target_file_name) 14 | 15 | write_with_confirmation(target_file_path, content) 16 | end 17 | 18 | def create_fabricators_path 19 | fabricators_path = File.join(Dir.pwd, 'spec', 'fabricators') 20 | FileUtils.mkpath(fabricators_path) 21 | fabricators_path 22 | end 23 | 24 | end 25 | end 26 | end -------------------------------------------------------------------------------- /lib/scaffold/generators/locales_generator.rb: -------------------------------------------------------------------------------- 1 | module Scaffold 2 | module Generators 3 | class LocalesGenerator < Scaffold::Generators::BaseGenerator 4 | def generate 5 | return if existing_locale? 6 | generate_locale('en') 7 | generate_locale('bg') 8 | end 9 | 10 | def existing_locale? 11 | Dir.glob(File.join(locales_path, '**')).each do |name| 12 | return true if File.basename(name) =~ /scaffold_pico.yml/ 13 | end 14 | return false 15 | end 16 | 17 | def generate_locale(locale) 18 | locales_path = create_locales_path 19 | source_file_name = "#{locale}.scaffold_pico.yml.erb" 20 | target_file_name = "#{locale}.scaffold_pico.yml" 21 | source_file_path = find_root(templates, 'locales', source_file_name) 22 | content = File.read(source_file_path) 23 | 24 | content = parse_template(content, {rails: @rails}) 25 | 26 | target_file_path = File.join(locales_path, target_file_name) 27 | 28 | write_with_confirmation(target_file_path, content) 29 | end 30 | 31 | def locales_path 32 | locales_path = File.join(Dir.pwd, 'config', 'locales') 33 | end 34 | 35 | def create_locales_path 36 | FileUtils.mkpath(locales_path) 37 | locales_path 38 | end 39 | 40 | end 41 | end 42 | end -------------------------------------------------------------------------------- /lib/scaffold/generators/models_generator.rb: -------------------------------------------------------------------------------- 1 | module Scaffold 2 | module Generators 3 | class ModelsGenerator < Scaffold::Generators::BaseGenerator 4 | def generate 5 | searches_path = create_searches_path 6 | puts "Add '#{@rails.resource.services_folder}' folder in your autoload_paths (application.rb)" 7 | create_search_object searches_path 8 | create_model 9 | end 10 | 11 | def create_model 12 | source_file_name = "model.rb.erb" 13 | target_file_name = @rails.resource.file_name 14 | 15 | source_file_path = find_root(templates, source_file_name) 16 | content = File.read(source_file_path) 17 | 18 | content = parse_template(content, {rails: @rails}) 19 | 20 | if @rails.resource.modules? 21 | model_path = File.join(Dir.pwd, 'app', 'models') 22 | else 23 | model_path = File.join(Dir.pwd, 'app', 'models', @rails.resource.modules.map(&:underscore)) 24 | FileUtils.mkdir_p model_path 25 | end 26 | 27 | target_file_path = File.join(model_path, target_file_name) 28 | 29 | write_with_confirmation(target_file_path, content) 30 | end 31 | 32 | def create_search_object searches_path 33 | source_file_name = "search.rb.erb" 34 | target_file_name = "#{@rails.resource.name.pluralize}_search.rb" 35 | 36 | source_file_path = find_root(templates, source_file_name) 37 | content = File.read(source_file_path) 38 | 39 | content = parse_template(content, {rails: @rails}) 40 | 41 | target_file_path = File.join(searches_path, target_file_name) 42 | write_with_confirmation(target_file_path, content) 43 | end 44 | 45 | def create_searches_path 46 | searches_path = File.join(Dir.pwd, 'app', @rails.resource.services_folder, 'search', @rails.controller.namespaces_as_path) 47 | FileUtils.mkpath(searches_path) 48 | searches_path 49 | end 50 | 51 | end 52 | end 53 | end -------------------------------------------------------------------------------- /lib/scaffold/generators/routes_generator.rb: -------------------------------------------------------------------------------- 1 | module Scaffold 2 | module Generators 3 | class RoutesGenerator < Scaffold::Generators::BaseGenerator 4 | 5 | def generate 6 | puts "Insert something like this in routes.rb" 7 | 8 | nested_resources = @rails.nested_in_resources.resources 9 | nested_resources_count = nested_resources.count 10 | 11 | namespaces_count = @rails.controller.namespaces_as_path.count 12 | 13 | route_string = "#{spaces(nested_resources_count + namespaces_count + 2)}resources :#{@rails.route.resource_name}" 14 | 15 | nested_resources.reverse.each_with_index do |resource, index| 16 | route_string = resources_block(resource.name, namespaces_count + nested_resources_count - index, route_string) 17 | end 18 | 19 | @rails.controller.namespaces_as_path.reverse.each_with_index do |namespace, index| 20 | index 21 | route_string = namespace_block(namespace, namespaces_count - index, route_string) 22 | end 23 | puts route_string 24 | end 25 | 26 | def spaces(count) 27 | " "*2*(count) 28 | end 29 | 30 | def resources_block resource_name, index, route_string 31 | ident = spaces(index+1) 32 | 33 | begin_resource_name_line = "#{ident}resources :#{resource_name.pluralize} do" 34 | end_resource_name_line = "#{ident}end" 35 | "#{begin_resource_name_line}\n#{route_string}\n#{end_resource_name_line}" 36 | end 37 | 38 | def namespace_block namespace, index, route_string 39 | ident = spaces(index+1) 40 | 41 | begin_namespace_line = "#{ident}namespace :#{namespace} do" 42 | end_namespace_line = "#{ident}end" 43 | "#{begin_namespace_line}\n#{route_string}\n#{end_namespace_line}" 44 | end 45 | 46 | end 47 | end 48 | end -------------------------------------------------------------------------------- /lib/scaffold/generators/views_generator.rb: -------------------------------------------------------------------------------- 1 | module Scaffold 2 | module Generators 3 | class ViewsGenerator < Scaffold::Generators::BaseGenerator 4 | attr :css_framework, false 5 | attr :template, false 6 | 7 | def generate template, css_framework 8 | @css_framework = css_framework 9 | @template = template 10 | 11 | views_path = create_views_path 12 | templating_engine = choose_templating_engine 13 | 14 | # print "Creating view:" 15 | %w(index new edit show _form).each do |view_name| 16 | # print view_name, ' ' 17 | create views_path, view_name, templating_engine, css_framework 18 | end 19 | # print "\n" 20 | end 21 | 22 | def create views_path, view_name, templating_engine, css_framework 23 | source_file_name = "#{view_name}.html.#{templating_engine.extension}.erb" 24 | target_file_name = "#{view_name}.html.#{templating_engine.extension}" 25 | source_file_path = find_root(templates, 'views', 26 | css_framework, 27 | templating_engine.source_folder_name, 28 | source_file_name) 29 | content = File.read(source_file_path) 30 | 31 | content = parse_template(content, {rails: @rails}) 32 | 33 | 34 | target_file_path = File.join(create_views_path, target_file_name) 35 | write_with_confirmation(target_file_path, content) 36 | end 37 | 38 | def create_views_path 39 | views_path = File.join(Dir.pwd, 'app', 'views', @rails.controller.namespaces_as_path, @rails.view.folder_name) 40 | FileUtils.mkpath(views_path) 41 | views_path 42 | end 43 | 44 | 45 | def choose_templating_engine 46 | case template 47 | when 'slim' 48 | ::Scaffold::TemplateEngines::Slim.new 49 | else 50 | raise "I don't have defined templates for #{template}. However you can use [slim,]" 51 | end 52 | end 53 | 54 | 55 | end 56 | end 57 | end -------------------------------------------------------------------------------- /lib/scaffold/main.rb: -------------------------------------------------------------------------------- 1 | require_relative 'generators/base_generator' 2 | require_relative 'generators/controller_generator' 3 | require_relative 'generators/models_generator' 4 | require_relative 'generators/views_generator' 5 | require_relative 'generators/routes_generator' 6 | require_relative 'generators/fabricator_generator' 7 | require_relative 'generators/locales_generator' 8 | 9 | module Scaffold 10 | class Main 11 | def initialize choice 12 | @choice = choice 13 | @rails = Rails.new(@choice) 14 | end 15 | 16 | def run 17 | Scaffold::Generators::FabricatorGenerator.new(@rails).generate if @choice[:fabrication] 18 | Scaffold::Generators::ControllerGenerator.new(@rails).generate 19 | if @choice[:no_model] 20 | puts("Skipping generating the model...") 21 | else 22 | Scaffold::Generators::ModelsGenerator.new(@rails).generate 23 | end 24 | Scaffold::Generators::ViewsGenerator.new(@rails).generate(@choice[:template], @choice[:css_framework]) 25 | Scaffold::Generators::RoutesGenerator.new(@rails).generate 26 | Scaffold::Generators::LocalesGenerator.new(@rails).generate 27 | end 28 | 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /lib/scaffold/models/resource.rb: -------------------------------------------------------------------------------- 1 | module Scaffold 2 | module Models 3 | class Resource 4 | 5 | def initialize raw_input 6 | (@modules, @class_name) = parse_model(raw_input) 7 | end 8 | 9 | # company_ownership 10 | # 11 | # Doesn't include modules 12 | # 13 | # for usage in controllers: company_ownership = CompanyOwnership.find... 14 | # for usage in paths: link_to [company_ownership] do ... 15 | def name 16 | @class_name.tableize.singularize 17 | end 18 | 19 | def instance_name 20 | "@#{name}" 21 | end 22 | 23 | def param_name 24 | "#{name}_id" 25 | end 26 | 27 | # CompanyOwnership 28 | def class_name 29 | @class_name.singularize 30 | end 31 | 32 | def modules 33 | @modules 34 | end 35 | 36 | # is the resource in modules Reports::Payment 37 | def modules? 38 | @modules.blank? 39 | end 40 | 41 | # company_ownerships 42 | def collection_name 43 | @class_name.tableize 44 | end 45 | 46 | # Admin::User or only User 47 | def class_name_with_modules 48 | if modules.empty? 49 | class_name 50 | else 51 | "#{modules.join('::')}::#{class_name}" 52 | end 53 | end 54 | 55 | private 56 | 57 | # model is something like Admin::User 58 | def parse_model model 59 | chunks = model.split('::').select{|chunk| !chunk.empty?} 60 | class_name = chunks.pop 61 | [chunks, class_name] 62 | end 63 | 64 | end 65 | end 66 | end -------------------------------------------------------------------------------- /lib/scaffold/rails.rb: -------------------------------------------------------------------------------- 1 | require_relative 'services/controller' 2 | require_relative 'services/path' 3 | require_relative 'services/resource' 4 | require_relative 'services/nested_in_resources' 5 | require_relative 'services/route' 6 | require_relative 'services/view' 7 | 8 | module Scaffold 9 | class Rails 10 | attr :controller, false 11 | attr :path, false 12 | attr :choice, false 13 | attr :resource, false 14 | attr :nested_in_resources, false 15 | attr :route, false 16 | attr :view, false 17 | 18 | def initialize choice 19 | @choice = choice 20 | 21 | @resource = Services::Resource.new(self) 22 | @nested_in_resources = Services::NestedInResources.new(self) 23 | @controller = Services::Controller.new(self) 24 | @route = Services::Route.new(self) 25 | @path = Services::Path.new(self) 26 | @view = Services::View.new(self) 27 | 28 | debug_info if choice[:debug] 29 | end 30 | 31 | def debug_info 32 | puts "\n" 33 | puts "Debug:" 34 | puts "resource_class_name: #{@resource.class_name}, resource_name: #{@resource.name}, collection_name: #{@resource.collection_name}" 35 | puts "class_name_with_modules: #{@resource.class_name_with_modules}" 36 | 37 | puts "\ncontroller:" 38 | puts "class: #{@controller.class_name}" 39 | 40 | puts "\nroutes:" 41 | puts "route_resource_name: #{@route.resource_name}" 42 | puts "controller_namespaces: #{@controller.namespaces_as_path} (for urls helpers)" 43 | puts "resource_paths" 44 | puts " new_resource_path: #{@path.new_resource}" 45 | puts " resource_path: #{@path.resource}" 46 | puts " edit_resource_path: #{@path.edit_resource}" 47 | puts " instance_resource_path: #{@path.instance_resource}" 48 | puts " edit_instance_resource: #{@path.edit_instance_resource}" 49 | puts " collection_path: #{@path.collection}" 50 | puts "\n\n" 51 | end 52 | 53 | end 54 | end -------------------------------------------------------------------------------- /lib/scaffold/services/controller.rb: -------------------------------------------------------------------------------- 1 | module Scaffold 2 | module Services 3 | class Controller 4 | def initialize rails 5 | @rails = rails 6 | end 7 | 8 | # example: Manage::BooksController 9 | def class_name 10 | if self.namespaced? 11 | "#{self.namespaces_as_modules}::#{@rails.resource.class_name.pluralize}Controller" 12 | else 13 | "#{@rails.resource.class_name.pluralize}Controller" 14 | end 15 | end 16 | 17 | def file_name 18 | "#{@rails.resource.class_name.tableize}_controller.rb" 19 | end 20 | 21 | def base_controller 22 | @rails.choice[:base_controller] || 'ApplicationController' 23 | end 24 | 25 | # very bad idea to go so deep 26 | def index_joins 27 | @rails.choice[:joins] 28 | end 29 | 30 | def index_includes 31 | @rails.choice[:includes] 32 | end 33 | 34 | def namespaced? 35 | namespaces_as_string.present? 36 | end 37 | 38 | # Admin::ClassRooms::Etc 39 | def namespaces_as_modules 40 | puts "namespaces_as_path: #{namespaces_as_path}" 41 | puts "namespaces_as_path.join.camelize: #{namespaces_as_path.map(&:camelize).join('::')}" 42 | namespaces_as_path.map(&:camelize).join('::') 43 | end 44 | 45 | # ['admin', 'classrooms' ] 46 | def namespaces_as_path 47 | (namespaces_as_string || "").split('/').collect{|name| name.underscore} 48 | end 49 | 50 | 51 | # which fields will be visible on the index page 52 | def index_fields 53 | if @rails.choice[:index_fields].blank? 54 | @rails.resource.fields.keys 55 | else 56 | @rails.choice[:index_fields].collect{|field| field.split(':').first} 57 | end 58 | end 59 | 60 | def search_fields 61 | if @rails.choice[:search_fields].blank? 62 | @rails.resource.fields.keys 63 | else 64 | @rails.choice[:search_fields].collect{|field| field.split(':').first} 65 | end 66 | end 67 | 68 | # convert company => company_id 69 | # Used in the #index action 70 | def permitted_search_fields 71 | fields = @rails.resource.fields 72 | 73 | to_ids = [] 74 | 75 | expanded_fields = expand_association_to_ids fields 76 | 77 | search_fields.each do |key| 78 | next unless expanded_fields.keys.include?(key) 79 | type = expanded_fields[key] 80 | to_ids << (['references', 'belongs_to'].include?(type.downcase) ? "#{key}_id" : key) 81 | end 82 | to_ids 83 | end 84 | 85 | def permitted_fields 86 | associations_to_ids(@rails.resource.fields) 87 | end 88 | 89 | 90 | private 91 | 92 | # admin/classrooms/etc 93 | def namespaces_as_string 94 | @namespaces_as_path ||= @rails.choice[:controller_namespaces] 95 | end 96 | 97 | 98 | # if the field is belongs_to 99 | # make so that fields contains the `field` and field_id 100 | def expand_association_to_ids fields 101 | expanded = {} 102 | fields.each_pair do |name, type| 103 | case type 104 | when 'belongs_to' 105 | expanded["#{name}_id"] = 'integer' 106 | end 107 | end 108 | fields.merge(expanded) 109 | end 110 | 111 | # convert company => company_id 112 | def associations_to_ids hash 113 | to_ids = {} 114 | hash.each_pair do |key, type| 115 | key_name = ['references', 'belongs_to'].include?(type.downcase) ? "#{key}_id" : key 116 | to_ids[key_name] = type 117 | end 118 | to_ids 119 | end 120 | end 121 | end 122 | end -------------------------------------------------------------------------------- /lib/scaffold/services/nested_in_resources.rb: -------------------------------------------------------------------------------- 1 | module Scaffold 2 | module Services 3 | class NestedInResources 4 | attr :resources 5 | 6 | def initialize rails 7 | @rails = rails 8 | 9 | @resources = nested_in_resources_as_array.collect do |raw_input| 10 | Scaffold::Models::Resource.new(raw_input) 11 | end 12 | end 13 | 14 | def nested? 15 | !resources.empty? 16 | end 17 | 18 | def each &block 19 | resources.each do |resource| 20 | yield resource 21 | end 22 | end 23 | 24 | def collect &block 25 | resources.collect do |resource| 26 | yield resource 27 | end 28 | end 29 | 30 | private 31 | 32 | def nested_in_resources_as_array 33 | (nested_in_resources || "").split('/') 34 | end 35 | 36 | def nested_in_resources 37 | @rails.choice[:nested_in_resources] 38 | end 39 | 40 | end 41 | end 42 | end -------------------------------------------------------------------------------- /lib/scaffold/services/path.rb: -------------------------------------------------------------------------------- 1 | module Scaffold 2 | module Services 3 | class Path 4 | def initialize rails 5 | @rails = rails 6 | end 7 | 8 | def new_resource 9 | segments = [':new'] 10 | segments += namespace_segments if @rails.controller.namespaced? 11 | segments += nested_resource_segments if @rails.route.nested? 12 | segments << ":#{@rails.resource.name}" 13 | "[#{segments.join(', ')}]" 14 | end 15 | 16 | def resource 17 | segments = [] 18 | segments += namespace_segments if @rails.controller.namespaced? 19 | segments += nested_resource_segments if @rails.route.nested? 20 | segments << @rails.resource.name 21 | "[#{segments.join(', ')}]" 22 | end 23 | 24 | def edit_resource 25 | segments = [':edit'] 26 | segments += namespace_segments if @rails.controller.namespaced? 27 | segments += nested_resource_segments if @rails.route.nested? 28 | segments << @rails.resource.name 29 | "[#{segments.join(', ')}]" 30 | end 31 | 32 | def instance_resource 33 | segments = [] 34 | segments += namespace_segments if @rails.controller.namespaced? 35 | segments += nested_resource_segments if @rails.route.nested? 36 | segments << "@#{@rails.resource.name}" 37 | "[#{segments.join(', ')}]" 38 | end 39 | 40 | def edit_instance_resource 41 | segments = [':edit'] 42 | segments += namespace_segments if @rails.controller.namespaced? 43 | segments += nested_resource_segments if @rails.route.nested? 44 | segments << "@#{@rails.resource.name}" 45 | "[#{segments.join(', ')}]" 46 | end 47 | 48 | def collection 49 | segments = [] 50 | segments += namespace_segments if @rails.controller.namespaced? 51 | segments += nested_resource_segments if @rails.route.nested? 52 | segments << ":#{@rails.resource.collection_name}" 53 | "[#{segments.join(', ')}]" 54 | end 55 | 56 | private 57 | 58 | # [":admin", ":companies"] 59 | def namespace_segments 60 | @rails.controller.namespaces_as_path.map{|segment| ":#{segment}".to_sym } 61 | end 62 | 63 | # [client] 64 | def nested_resource_segments 65 | @rails.nested_in_resources.collect do |nested_resource| 66 | nested_resource.name 67 | end 68 | end 69 | 70 | end 71 | end 72 | end -------------------------------------------------------------------------------- /lib/scaffold/services/resource.rb: -------------------------------------------------------------------------------- 1 | module Scaffold 2 | module Services 3 | class Resource 4 | attr :resource 5 | 6 | def initialize rails 7 | @rails = rails 8 | @resource = Scaffold::Models::Resource.new(@rails.choice[:model]) 9 | end 10 | 11 | def model_name 12 | @resource.model_name 13 | end 14 | 15 | def modules 16 | @resource.modules 17 | end 18 | 19 | def modules? 20 | @resource.modules? 21 | end 22 | 23 | def name 24 | @resource.name 25 | end 26 | 27 | def class_name 28 | @resource.class_name 29 | end 30 | 31 | def collection_name 32 | @resource.collection_name 33 | end 34 | 35 | def class_name_with_modules 36 | @resource.class_name_with_modules 37 | end 38 | 39 | def file_name 40 | "#{name}.rb" 41 | end 42 | 43 | # Search::Manage:BooksSearch 44 | def search_class_name 45 | if @rails.controller.namespaced? 46 | "Search::#{@rails.controller.namespaces_as_modules}::#{class_name.pluralize}Search" 47 | else 48 | "Search::#{class_name.pluralize}Search" 49 | end 50 | end 51 | 52 | def services_folder 53 | @rails.choice[:services_folder] 54 | end 55 | 56 | def fields 57 | expand_default_types(hasherize_fields(@rails.choice[:fields])) 58 | end 59 | 60 | private 61 | 62 | def expand_default_types hash 63 | hash.each_pair do |key, value| 64 | hash[key] = 'string' if value.blank? 65 | end 66 | end 67 | 68 | def hasherize_fields fields_array 69 | fields = {} 70 | fields_array.each do |field_string| 71 | (key, value) = field_string.split(':') 72 | fields[key] = value 73 | end 74 | fields 75 | end 76 | 77 | end 78 | end 79 | end -------------------------------------------------------------------------------- /lib/scaffold/services/route.rb: -------------------------------------------------------------------------------- 1 | module Scaffold 2 | module Services 3 | class Route 4 | def initialize rails 5 | @rails = rails 6 | end 7 | 8 | # resources :company_ownerships 9 | def resource_name 10 | @rails.resource.collection_name 11 | end 12 | 13 | def nested? 14 | @rails.nested_in_resources.present? 15 | end 16 | 17 | 18 | end 19 | end 20 | end -------------------------------------------------------------------------------- /lib/scaffold/services/view.rb: -------------------------------------------------------------------------------- 1 | module Scaffold 2 | module Services 3 | class View 4 | def initialize rails 5 | @rails = rails 6 | end 7 | 8 | def folder_name 9 | @rails.resource.collection_name 10 | end 11 | 12 | end 13 | end 14 | end -------------------------------------------------------------------------------- /lib/scaffold/template_engines/slim.rb: -------------------------------------------------------------------------------- 1 | module Scaffold 2 | module TemplateEngines 3 | class Slim 4 | 5 | def extension 6 | 'slim' 7 | end 8 | 9 | def source_folder_name 10 | 'slim' 11 | end 12 | 13 | end 14 | end 15 | end -------------------------------------------------------------------------------- /lib/scaffold_pico.rb: -------------------------------------------------------------------------------- 1 | require 'active_support/inflector' 2 | require 'active_support/core_ext/object/blank' 3 | require 'ostruct' 4 | 5 | require 'scaffold/rails' 6 | require 'scaffold/main' 7 | require 'scaffold/erb_context' # I hate this erb 8 | 9 | require 'scaffold/models/resource.rb' 10 | 11 | require 'scaffold/template_engines/slim' 12 | require 'erb' 13 | require 'fileutils' 14 | 15 | module Scaffold 16 | def self.root 17 | File.dirname __dir__ 18 | end 19 | end -------------------------------------------------------------------------------- /lib/templates/pico/controller.rb.erb: -------------------------------------------------------------------------------- 1 | class <%= @rails.controller.class_name %> < <%= @rails.controller.base_controller %> 2 | 3 | protect_from_forgery with: :exception 4 | 5 | <% @rails.nested_in_resources.each do |resource| -%> 6 | def <%= resource.name %> 7 | <%= resource.instance_name %> ||= <%= resource.class_name_with_modules %>.find(params[:<%= resource.param_name %>]) 8 | end 9 | helper_method :<%= resource.name %> 10 | 11 | <% end %> 12 | 13 | def index 14 | <%= "@#{@rails.resource.collection_name}_search = #{@rails.resource.search_class_name}.new(search_#{@rails.resource.collection_name}_params)" %> 15 | <%= 16 | active_record = ["@#{@rails.resource.collection_name}_search"] 17 | active_record << [ "results" ] 18 | active_record << [ "includes(#{@rails.controller.index_includes.lazy.map(&:strip).map{|v| v.gsub(',', '')}.to_a.join(', ')})" ] if @rails.controller.index_includes and !@rails.controller.index_includes.empty? 19 | active_record << [ "joins(#{@rails.controller.index_joins.join(', ')})" ] if @rails.controller.index_joins and !@rails.controller.index_joins.empty? 20 | active_record << [ "page(params[:page])", "per(params[:per])" ] 21 | active_record << [ "all" ] 22 | "@#{@rails.resource.collection_name} = #{active_record.join('.')}" 23 | %> 24 | end 25 | 26 | def show 27 | @<%= @rails.resource.name %> = <%= @rails.resource.class_name_with_modules %>.find(params[:id]) 28 | end 29 | 30 | def new 31 | @<%= @rails.resource.name %> = <%= @rails.resource.class_name_with_modules %>.new 32 | end 33 | 34 | def edit 35 | @<%= @rails.resource.name %> = <%= @rails.resource.class_name_with_modules %>.find(params[:id]) 36 | end 37 | 38 | def create 39 | @<%= @rails.resource.name %> = <%= @rails.resource.class_name_with_modules %>.new(<%= @rails.resource.collection_name %>_params) 40 | 41 | respond_to do |format| 42 | if @<%= @rails.resource.name %>.save 43 | <%= "message = t('create', scope: [:scaffold, :notices, :success], model: #{@rails.resource.class_name}.model_name.human.mb_chars.downcase)" %> 44 | format.html { redirect_to <%= @rails.path.collection %>, notice: message } 45 | else 46 | format.html { render :new } 47 | end 48 | end 49 | end 50 | 51 | def update 52 | @<%= @rails.resource.name %> = <%= @rails.resource.class_name_with_modules %>.find(params[:id]) 53 | respond_to do |format| 54 | if @<%= @rails.resource.name %>.update(<%= @rails.resource.collection_name %>_params) 55 | <%= "message = t('update', scope: [:scaffold, :notices, :success], model: #{@rails.resource.class_name}.model_name.human.mb_chars.downcase)" %> 56 | format.html { redirect_to <%= @rails.path.collection %>, notice: message } 57 | else 58 | format.html { render :edit } 59 | end 60 | end 61 | end 62 | 63 | def destroy 64 | @<%= @rails.resource.name %> = <%= @rails.resource.class_name_with_modules %>.find(params[:id]) 65 | @<%= @rails.resource.name %>.destroy 66 | respond_to do |format| 67 | <%= "message = t('destroy', scope: [:scaffold, :notices, :success], model: #{@rails.resource.class_name}.model_name.human.mb_chars.downcase)" %> 68 | format.html { redirect_to <%= @rails.path.collection %>, notice: message } 69 | end 70 | end 71 | 72 | private 73 | def <%= @rails.resource.collection_name %>_params 74 | params[:<%= @rails.resource.name %>].permit(<%= @rails.controller.permitted_fields.keys.map{|field| ":#{field}"}.join(', ') %>) 75 | end 76 | 77 | def search_<%= @rails.resource.collection_name %>_params 78 | return {} if params[:<%= @rails.resource.collection_name %>_search].blank? 79 | params[:<%= @rails.resource.collection_name %>_search].permit(<%= @rails.controller.permitted_search_fields.collect{|field| ":#{field}"}.join(', ') %>) 80 | end 81 | 82 | end 83 | -------------------------------------------------------------------------------- /lib/templates/pico/fabricators/fabrication.rb.erb: -------------------------------------------------------------------------------- 1 | <% if @rails.resource.modules.empty? -%> 2 | Fabricator(:<%= @rails.resource.name %>) do 3 | <% else -%> 4 | Fabricator(:<%= @rails.resource.name %>, class_name: '<%= @rails.resource.class_name_with_modules -%>') do 5 | <% end -%> 6 | <% @rails.resource.fields.each_pair do |field_name, kind| -%> 7 | <% if kind == 'belongs_to' -%> 8 | <%= field_name %> 9 | <% elsif kind == 'text' -%> 10 | <%= field_name -%> { Faker::Lorem.sentence } 11 | <% elsif kind == 'string' -%> 12 | <%= field_name -%> { Faker::Lorem.word } 13 | <% end -%> 14 | <% end -%> 15 | end 16 | -------------------------------------------------------------------------------- /lib/templates/pico/locales/bg.scaffold_pico.yml.erb: -------------------------------------------------------------------------------- 1 | bg: 2 | scaffold: 3 | confirm: 'Сигурни ли сте?' 4 | notices: 5 | success: 6 | create: Създадохме %{model} 7 | update: Обновихме %{model} 8 | destroy: Изтрихме %{model} 9 | failed: 10 | create: Неуспешно създаване на %{model} 11 | update: Неуспешно обновяване на %{model} 12 | destroy: Неуспешно изтриване на %{model} 13 | new: 14 | title: "Нов %{model}" 15 | show: 16 | title: "%{model}" 17 | edit: 18 | title: "Редакция %{model}" 19 | index: 20 | title: "%{model}" 21 | edit: Редакция 22 | show: Преглед 23 | destroy: Изтрии 24 | actions: Действия 25 | search: 26 | header: Търсене 27 | button: Търси 28 | reset: Ново 29 | 30 | actions: 31 | index: Списък %{model} 32 | actions: 'Действия' 33 | new: Нов %{model} 34 | edit: Редакция %{model} 35 | create: "Създай %{model}" 36 | time: 37 | formats: 38 | very_short: "%F %H:%M" 39 | very_short_with_seconds: "%F %T" -------------------------------------------------------------------------------- /lib/templates/pico/locales/en.scaffold_pico.yml.erb: -------------------------------------------------------------------------------- 1 | en: 2 | scaffold: 3 | confirm: 'Are you sure?' 4 | notices: 5 | success: 6 | create: Was successfully created %{model} 7 | update: Was successfully updated %{model} 8 | destroy: Was successfully destroyed %{model} 9 | failed: 10 | create: Fail creating %{model} 11 | update: Fail updating %{model} 12 | destroy: Fail destroying %{model} 13 | new: 14 | title: "New %{model}" 15 | show: 16 | title: "%{model}" 17 | edit: 18 | title: "Edit %{model}" 19 | index: 20 | title: "%{model}" 21 | edit: Edit 22 | show: Show 23 | destroy: Delete 24 | actions: Action 25 | search: 26 | header: Search 27 | button: Search 28 | reset: Reset 29 | 30 | actions: 31 | index: List %{model} 32 | actions: 'Actions' 33 | new: New %{model} 34 | edit: Edit %{model} 35 | create: "Create %{model}" 36 | time: 37 | formats: 38 | very_short: "%F %H:%M" 39 | very_short_with_seconds: "%F %T" -------------------------------------------------------------------------------- /lib/templates/pico/model.rb.erb: -------------------------------------------------------------------------------- 1 | class <%= @rails.resource.class_name_with_modules %> < ApplicationRecord 2 | <% @rails.resource.fields.select {|k,v| @rails.controller.search_fields.include?(k) }.each_pair do |field_name, field_type| -%> 3 | <% if field_type == 'belongs_to' -%> 4 | belongs_to :<%= field_name %> 5 | <% end -%> 6 | <% end -%> 7 | end -------------------------------------------------------------------------------- /lib/templates/pico/search.rb.erb: -------------------------------------------------------------------------------- 1 | class <%= @rails.resource.search_class_name %> 2 | include ActiveModel::Model 3 | 4 | attr_accessor( 5 | <% @rails.controller.search_fields.map do |field_name| %> 6 | <%= 7 | if @rails.resource.fields[field_name] == 'belongs_to' 8 | ":#{field_name}_id," 9 | else 10 | ":#{field_name}," 11 | end 12 | -%> 13 | <% end %> 14 | ) 15 | 16 | <% @rails.controller.search_fields.select { |search_filed| @rails.resource.fields[search_filed] == 'integer' }.each do |search_filed| %> 17 | <%= 18 | "validates :#{search_filed}, numericality: { only_integer: true }, allow_blank: true" 19 | -%> 20 | <% end %> 21 | 22 | def initialize *args 23 | @relation = <%= @rails.resource.class_name_with_modules %> 24 | super *args 25 | end 26 | 27 | def results 28 | <% resource_name = @rails.resource.name.pluralize %> 29 | <%= resource_name -%> = @relation 30 | 31 | if valid? 32 | <% @rails.controller.search_fields.each do |field_name| %> 33 | <%= 34 | case @rails.resource.fields[field_name] 35 | when 'string', 'text' 36 | "#{resource_name} = #{resource_name}.where(\"#{field_name} LIKE ?\", \"%\#{#{field_name}}%\") if #{field_name}.present?" 37 | when 'boolean' 38 | "#{resource_name} = #{resource_name}.where(#{field_name}: true) if #{field_name} == '1'" 39 | when 'belongs_to' 40 | "#{resource_name} = #{resource_name}.where(#{field_name}_id: #{field_name}_id) if #{field_name}_id.present?" 41 | when 'int', 'integer' 42 | "#{resource_name} = #{resource_name}.where(#{field_name}: #{field_name}.to_i) if #{field_name}.present?" 43 | else 44 | "#{resource_name} = #{resource_name}.where(#{field_name}: #{field_name}) if #{field_name}.present?" 45 | end 46 | -%> 47 | <% end %> 48 | 49 | if <%= @rails.resource.class_name_with_modules -%>.attribute_names.include?('updated_at') 50 | <%= resource_name %>.order(updated_at: :desc) 51 | else 52 | <%= resource_name %> 53 | end 54 | end 55 | end 56 | end -------------------------------------------------------------------------------- /lib/templates/pico/views/materialize/slim/_form.html.slim.erb: -------------------------------------------------------------------------------- 1 | = form.error_notification 2 | .form-inputs 3 | <% @rails.resource.fields.each_pair do |field_name, kind| %> 4 | <%- next if field_name == 'id' -%> 5 | <%= 6 | case kind 7 | when 'file' 8 | "= form.input :#{field_name}, as: :file" 9 | when 'belongs_to' 10 | "= form.association :#{field_name}" 11 | when 'text' 12 | "= form.input :#{field_name}, as: :text" 13 | when 'date', 'datetime' 14 | "= form.input :#{field_name}, as: :date, html5: true" 15 | when 'boolean' 16 | "= form.input :#{field_name}, as: :boolean" 17 | else 18 | "= form.input :#{field_name}" 19 | end 20 | -%> 21 | <% end %> -------------------------------------------------------------------------------- /lib/templates/pico/views/materialize/slim/edit.html.slim.erb: -------------------------------------------------------------------------------- 1 | .container 2 | h1 = t('scaffold.edit.title', model: <%= @rails.resource.class_name_with_modules %>.model_name.human(count: 1).mb_chars.downcase) 3 | 4 | .section 5 | .left 6 | = link_to <%= @rails.path.collection %>, class: 'waves-effect waves-light btn' do 7 | i.material-icons.right view_list 8 | = t('scaffold.show.actions.index', model: <%= @rails.resource.class_name_with_modules %>.model_name.human(count: 2).mb_chars.downcase) 9 | 10 | 11 | .right 12 | = link_to <%= @rails.path.new_resource %>, class: 'btn waves-effect waves-light' do 13 | i.material-icons.right add 14 | = t('scaffold.actions.new', model: <%= @rails.resource.class_name_with_modules %>.model_name.human(count: 1).mb_chars.downcase) 15 | 16 | .clearfix 17 | 18 | .card 19 | .card-content 20 | = simple_form_for <%= @rails.path.instance_resource %> do |form| 21 | = render 'form', form: form 22 | .form-actions 23 | = form.button :button, t('scaffold.actions.update', model: <%= @rails.resource.class_name_with_modules %>.model_name.human(count: 1)), :class => 'btn btn-large waves-effect waves' 24 | -------------------------------------------------------------------------------- /lib/templates/pico/views/materialize/slim/index.html.slim.erb: -------------------------------------------------------------------------------- 1 | .container 2 | h1 = t('scaffold.index.title', model: <%= @rails.resource.class_name_with_modules %>.model_name.human(count: 2)) 3 | 4 | .fixed-action-btn style="bottom: 45px; right: 24px;" 5 | = link_to <%= @rails.path.new_resource %>, class: 'btn-floating btn-large waves-effect waves-light red' do 6 | i.material-icons.right add 7 | = t('scaffold.actions.new', model: <%= @rails.resource.class_name_with_modules %>.model_name.human(count: 1).mb_chars.downcase) 8 | 9 | 10 | 11 | .row 12 | .col.s12.m9 13 | .card 14 | .card-content 15 | table.striped.highlight.responsive-table 16 | thead 17 | tr 18 | <% @rails.resource.fields.select {|k,v| @rails.controller.index_fields.include?(k) }.keys.each do |field_name| %> 19 | th = <%= @rails.resource.class_name_with_modules -%>.human_attribute_name(:<%= field_name -%>) 20 | <% end %> 21 | th colspan=3 = t('scaffold.index.actions') 22 | 23 | tbody 24 | - @<%= @rails.resource.collection_name %>.each do |<%= @rails.resource.name %>| 25 | tr 26 | <% @rails.resource.fields.select {|k,v| @rails.controller.index_fields.include?(k) }.each_pair do |field_name, field_type| %> 27 | <% if ['date', 'datetime'].include?(field_type) -%> 28 | td.nowrap = l(<%= @rails.resource.name %>.<%= field_name -%>, format: :very_short) 29 | <% else -%> 30 | td = <%= @rails.resource.name %>.<%= field_name -%> 31 | <% end -%> 32 | <% end %> 33 | td = link_to t('scaffold.index.show'), <%= @rails.path.resource %> 34 | td = link_to t('scaffold.index.edit'), <%= @rails.path.edit_resource %> 35 | td = link_to t('scaffold.index.destroy'), <%= @rails.path.resource %>, data: {:confirm => t('scaffold.confirm')}, :method => :delete 36 | .card-action 37 | = paginate @<%= @rails.resource.collection_name %> 38 | 39 | .col.s12.m3 40 | 41 | aside.card.search 42 | = simple_form_for <%= "@#{@rails.resource.collection_name}_search" %>, as: :<%= @rails.resource.collection_name %>_search, url: <%= @rails.path.collection %>, method: :get do |form| 43 | .card-content 44 | h5 = t('scaffold.index.search.header') 45 | = form.error_notification 46 | 47 | <% @rails.resource.fields.select {|k,v| @rails.controller.search_fields.include?(k) }.each_pair do |field_name, field_type| %> 48 | <% if field_type == 'belongs_to' -%> 49 | = form.input :<%= field_name -%>_id 50 | <% elsif ['date', 'datetime'].include?(field_type) -%> 51 | = form.input :<%= field_name -%>, as: :date, html5: true 52 | <% elsif field_type == 'boolean' -%> 53 | = form.input :<%= field_name -%>, as: :boolean, input_html: {class: 'filled-in'} 54 | <% else %> 55 | = form.input :<%= field_name -%> 56 | <% end -%> 57 | <% end %> 58 | 59 | .card-action 60 | = link_to t('scaffold.index.search.reset'), url_for(<%= @rails.path.collection %>), class: 'waves-effect waves-light btn lime lighten-5 black-text' 61 | = form.submit t('scaffold.index.search.button'), class: 'right waves-effect waves-light btn' 62 | .clearfix 63 | -------------------------------------------------------------------------------- /lib/templates/pico/views/materialize/slim/new.html.slim.erb: -------------------------------------------------------------------------------- 1 | .container 2 | h1 = t('scaffold.new.title', model: <%= @rails.resource.class_name_with_modules %>.model_name.human(count: 1).mb_chars.downcase) 3 | 4 | .section 5 | .left 6 | = link_to <%= @rails.path.collection %>, class: 'waves-effect waves-light btn' do 7 | i.material-icons.right view_list 8 | = t('scaffold.show.actions.index', model: <%= @rails.resource.class_name_with_modules %>.model_name.human(count: 2).mb_chars.downcase) 9 | 10 | .clearfix 11 | 12 | .card 13 | .card-content 14 | = simple_form_for <%= @rails.path.instance_resource %> do |form| 15 | = render 'form', form: form 16 | .form-actions 17 | = form.button :button, t('scaffold.actions.create', model: <%= @rails.resource.class_name_with_modules %>.model_name.human(count: 1)), :class => 'btn btn-large waves-effect waves' 18 | -------------------------------------------------------------------------------- /lib/templates/pico/views/materialize/slim/show.html.slim.erb: -------------------------------------------------------------------------------- 1 | .container 2 | h1 = t('scaffold.show.title', model: <%= @rails.resource.class_name_with_modules %>.model_name.human(count: 1).mb_chars.downcase) 3 | 4 | .section 5 | .left 6 | = link_to <%= @rails.path.collection %>, class: 'waves-effect waves-light btn' do 7 | i.material-icons.right view_list 8 | = t('scaffold.actions.index', model: <%= @rails.resource.class_name_with_modules %>.model_name.human(count: 2).mb_chars.downcase) 9 | .right 10 | = link_to <%= @rails.path.edit_instance_resource %>, class: 'btn waves-effect waves-light' do 11 | i.material-icons.right mode_edit 12 | = t('scaffold.actions.edit', model: <%= @rails.resource.class_name_with_modules %>.model_name.human(count: 1).mb_chars.downcase) 13 | = link_to <%= @rails.path.new_resource %>, class: 'btn waves-effect waves-light' do 14 | i.material-icons.right add 15 | = t('scaffold.actions.new', model: <%= @rails.resource.class_name_with_modules %>.model_name.human(count: 1).mb_chars.downcase) 16 | 17 | .clearfix 18 | 19 | .card 20 | .card-content 21 | table.striped.highlight.responsive-table 22 | tbody 23 | <% @rails.resource.fields.keys.each do |field_name| %> 24 | tr 25 | td = <%= @rails.resource.class_name_with_modules -%>.human_attribute_name(:<%= field_name -%>) 26 | td = @<%= @rails.resource.name %>.<%= field_name -%> 27 | <% end %> 28 | -------------------------------------------------------------------------------- /lib/templates/pico/views/twitter_bootstrap_4x/slim/_form.html.slim.erb: -------------------------------------------------------------------------------- 1 | = form.error_notification 2 | .form-inputs 3 | <% @rails.resource.fields.each_pair do |field_name, kind| -%> 4 | <% if kind == 'file' -%> 5 | = form.input :<%= field_name -%>, as: :file 6 | <% elsif kind == 'belongs_to' -%> 7 | = form.association :<%= field_name %> 8 | <% elsif kind == 'text' -%> 9 | = form.input :<%= field_name -%>, as: :text 10 | <% elsif ['date', 'datetime'].include?(kind) -%> 11 | = form.input :<%= field_name -%>, as: :date, html5: true 12 | <% else -%> 13 | = form.input :<%= field_name %> 14 | <% end -%> 15 | <% end -%> -------------------------------------------------------------------------------- /lib/templates/pico/views/twitter_bootstrap_4x/slim/edit.html.slim.erb: -------------------------------------------------------------------------------- 1 | .d-flex.justify-content-between.flex-wrap.flex-md-nowrap.align-items-center.pb-2.mb-3.border-bottom 2 | h1 = t('scaffold.edit.title', model: <%= @rails.resource.class_name_with_modules %>.model_name.human(count: 1).mb_chars.downcase) 3 | 4 | .btn-toolbar.mb-2.mb-md-0 5 | .btn-group.mr-2 6 | = link_to t('scaffold.actions.new', model: <%= @rails.resource.class_name_with_modules %>.model_name.human(count: 1).mb_chars.downcase), <%= @rails.path.new_resource %>, class: 'btn btn-sm btn-outline-secondary' 7 | .btn-group 8 | = link_to t('scaffold.show.actions.index', model: <%= @rails.resource.class_name_with_modules %>.model_name.human(count: 2).mb_chars.downcase), <%= @rails.path.collection %>, class: "btn btn-sm btn-outline-secondary" 9 | 10 | 11 | = simple_form_for <%= @rails.path.instance_resource %> do |form| 12 | = render 'form', form: form 13 | .form-actions 14 | = form.button :button, t('scaffold.actions.update', model: <%= @rails.resource.class_name_with_modules %>.model_name.human(count: 1)), :class => 'btn btn-primary' 15 | -------------------------------------------------------------------------------- /lib/templates/pico/views/twitter_bootstrap_4x/slim/index.html.slim.erb: -------------------------------------------------------------------------------- 1 | h1 = t('scaffold.index.title', model: <%= @rails.resource.class_name_with_modules %>.model_name.human(count: 2).mb_chars.downcase) 2 | 3 | .row 4 | .col-sm-8.col-12 5 | table.table.table-bordered.table-striped 6 | thead 7 | tr 8 | <% @rails.resource.fields.select {|k,v| @rails.controller.index_fields.include?(k) }.keys.each do |field_name| %> 9 | th = <%= @rails.resource.class_name_with_modules -%>.human_attribute_name(:<%= field_name -%>) 10 | <% end %> 11 | th colspan=3 = t('scaffold.index.actions') 12 | 13 | tbody 14 | - @<%= @rails.resource.collection_name %>.each do |<%= @rails.resource.name %>| 15 | tr 16 | <% @rails.resource.fields.select {|k,v| @rails.controller.index_fields.include?(k) }.keys.each do |field_name| %> 17 | td = <%= @rails.resource.name %>.<%= field_name -%> 18 | <% end %> 19 | td = link_to t('scaffold.index.show'), <%= @rails.path.resource %> 20 | td = link_to t('scaffold.index.edit'), <%= @rails.path.edit_resource %> 21 | td = link_to t('scaffold.index.destroy'), <%= @rails.path.resource %>, data: {:confirm => t('scaffold.confirm')}, :method => :delete 22 | = paginate @<%= @rails.resource.collection_name %> 23 | 24 | .col-sm-4.col-12 25 | .card 26 | .card-body 27 | aside.actions 28 | .btn-toolbar.mb-2.mb-md-0 29 | btn-group.mr-2 30 | = link_to t('scaffold.actions.new', model: <%= @rails.resource.class_name_with_modules %>.model_name.human.mb_chars.downcase), <%= @rails.path.new_resource %>, class: 'btn btn-outline-primary btn-sm', role: "button" 31 | 32 | .card.mt-2 33 | .card-body 34 | aside.search 35 | h5 = t('scaffold.index.search.header') 36 | 37 | = simple_form_for <%= "@#{@rails.resource.collection_name}_search" %>, as: :<%= @rails.resource.collection_name %>_search, url: <%= @rails.path.collection %>, method: :get do |form| 38 | = form.error_notification 39 | .form-inputs 40 | <% @rails.resource.fields.select {|k,v| @rails.controller.search_fields.include?(k) }.each_pair do |field_name, field_type| %> 41 | <% if field_type == 'belongs_to' -%> 42 | = form.input :<%= field_name -%>_id 43 | <% elsif ['date', 'datetime'].include?(field_type) -%> 44 | = form.input :<%= field_name -%>, as: :date, html5: true 45 | <% elsif field_type == 'boolean' -%> 46 | = form.input :<%= field_name -%>, as: :boolean, input_html: {class: 'filled-in'} 47 | <% else -%> 48 | = form.input :<%= field_name -%> 49 | <% end -%> 50 | <% end %> 51 | 52 | .form-actions 53 | = form.button :submit, t('scaffold.index.search.button'), class: 'btn-primary' 54 | 55 | = form.button :button, "Cancel", type: "reset", class: "btn-outline-secondary float-right" 56 | 57 | -------------------------------------------------------------------------------- /lib/templates/pico/views/twitter_bootstrap_4x/slim/new.html.slim.erb: -------------------------------------------------------------------------------- 1 | .d-flex.justify-content-between.flex-wrap.flex-md-nowrap.align-items-center.pb-2.mb-3.border-bottom 2 | h1 = t('scaffold.new.title', model: <%= @rails.resource.class_name_with_modules %>.model_name.human(count: 1).mb_chars.downcase) 3 | 4 | .btn-toolbar.mb-2.mb-md-0 5 | .btn-group 6 | = link_to t('scaffold.show.actions.index', model: <%= @rails.resource.class_name_with_modules %>.model_name.human(count: 2).mb_chars.downcase), <%= @rails.path.collection %>, class: "btn btn-sm btn-outline-secondary" 7 | 8 | = simple_form_for <%= @rails.path.instance_resource %> do |form| 9 | = render 'form', form: form 10 | .form-actions 11 | = form.button :button, t('scaffold.actions.create', model: <%= @rails.resource.class_name_with_modules %>.model_name.human(count: 1)), :class => 'btn btn-primary' 12 | -------------------------------------------------------------------------------- /lib/templates/pico/views/twitter_bootstrap_4x/slim/show.html.slim.erb: -------------------------------------------------------------------------------- 1 | .d-flex.justify-content-between.flex-wrap.flex-md-nowrap.align-items-center.pb-2.mb-3.border-bottom 2 | h1 = t('scaffold.show.title', model: <%= @rails.resource.class_name_with_modules %>.model_name.human(count: 1).mb_chars.downcase) 3 | 4 | .btn-toolbar.mb-2.mb-md-0 5 | .btn-group.mr-2 6 | = link_to t('scaffold.actions.edit', model: <%= @rails.resource.class_name_with_modules %>.model_name.human(count: 1).mb_chars.downcase), <%= @rails.path.edit_instance_resource %>, class: 'btn btn-sm btn-outline-secondary' 7 | = link_to t('scaffold.actions.new', model: <%= @rails.resource.class_name_with_modules %>.model_name.human(count: 1).mb_chars.downcase), <%= @rails.path.new_resource %>, class: 'btn btn-sm btn-outline-secondary' 8 | .btn-group 9 | = link_to t('scaffold.show.actions.index', model: <%= @rails.resource.class_name_with_modules %>.model_name.human(count: 2).mb_chars.downcase), <%= @rails.path.collection %>, class: 'btn btn-sm btn-outline-primary' 10 | 11 | .table-responsive 12 | table.table.table-striped 13 | tbody 14 | <% @rails.resource.fields.keys.each do |field_name| %> 15 | tr 16 | td = <%= @rails.resource.class_name_with_modules -%>.human_attribute_name(:<%= field_name -%>) 17 | td = @<%= @rails.resource.name %>.<%= field_name -%> 18 | <% end %> 19 | -------------------------------------------------------------------------------- /lib/templates/pico/views/zurb/slim/_form.html.slim.erb: -------------------------------------------------------------------------------- 1 | = form.error_notification 2 | .form-inputs 3 | <% @rails.resource.fields.each_pair do |field_name, kind| -%> 4 | <% if kind == 'file' -%> 5 | = form.input :<%= field_name -%>, as: :file 6 | <% elsif kind == 'belongs_to' -%> 7 | = form.association :<%= field_name %> 8 | <% elsif kind == 'text' -%> 9 | = form.input :<%= field_name -%>, as: :text 10 | <% elsif ['date', 'datetime'].include?(kind) -%> 11 | = form.input :<%= field_name -%>, as: :date, html5: true 12 | <% else -%> 13 | = form.input :<%= field_name %> 14 | <% end -%> 15 | <% end -%> -------------------------------------------------------------------------------- /lib/templates/pico/views/zurb/slim/edit.html.slim.erb: -------------------------------------------------------------------------------- 1 | h1 = t('scaffold.edit.title', model: <%= @rails.resource.class_name_with_modules %>.model_name.human(count: 1).mb_chars.downcase) 2 | 3 | .callout.clearfix 4 | .float-left 5 | = link_to t('scaffold.show.actions.index', model: <%= @rails.resource.class_name_with_modules %>.model_name.human(count: 2).mb_chars.downcase), <%= @rails.path.collection %>, class: 'button small primary' 6 | .float-right 7 | = link_to t('scaffold.actions.edit', model: <%= @rails.resource.class_name_with_modules %>.model_name.human(count: 1).mb_chars.downcase), <%= @rails.path.edit_instance_resource %>, class: 'button small warning' 8 | = link_to t('scaffold.actions.new', model: <%= @rails.resource.class_name_with_modules %>.model_name.human(count: 1).mb_chars.downcase), <%= @rails.path.new_resource %>, class: 'button small secondary' 9 | 10 | = simple_form_for <%= @rails.path.instance_resource %> do |form| 11 | = render 'form', form: form 12 | .form-actions 13 | = form.submit class: 'waves-effect waves btn-large' -------------------------------------------------------------------------------- /lib/templates/pico/views/zurb/slim/index.html.slim.erb: -------------------------------------------------------------------------------- 1 | h1 = t('scaffold.index.title', model: <%= @rails.resource.class_name_with_modules %>.model_name.human(count: 2).mb_chars.downcase) 2 | 3 | .row 4 | .small-12.medium-10.columns 5 | table 6 | thead 7 | tr 8 | <% @rails.resource.fields.select {|k,v| @rails.controller.index_fields.include?(k) }.keys.each do |field_name| %> 9 | th = <%= @rails.resource.class_name_with_modules -%>.human_attribute_name(:<%= field_name -%>) 10 | <% end %> 11 | th colspan=3 = t('scaffold.index.actions') 12 | 13 | tbody 14 | - @<%= @rails.resource.collection_name %>.each do |<%= @rails.resource.name %>| 15 | tr 16 | <% @rails.resource.fields.select {|k,v| @rails.controller.index_fields.include?(k) }.keys.each do |field_name| %> 17 | td = <%= @rails.resource.name %>.<%= field_name -%> 18 | <% end %> 19 | td = link_to t('scaffold.index.show'), <%= @rails.path.resource %> 20 | td = link_to t('scaffold.index.edit'), <%= @rails.path.edit_resource %> 21 | td = link_to t('scaffold.index.destroy'), <%= @rails.path.resource %>, data: {:confirm => t('scaffold.confirm')}, :method => :delete 22 | = paginate @<%= @rails.resource.collection_name %> 23 | 24 | .small-12.medium-2.columns 25 | aside.callout.clearfix.actions 26 | = link_to t('scaffold.actions.new', model: <%= @rails.resource.class_name_with_modules %>.model_name.human.mb_chars.downcase), <%= @rails.path.new_resource %>, class: 'button small secondary' 27 | 28 | aside.search.callout.clearfix 29 | h5 = t('scaffold.index.search.header') 30 | 31 | = simple_form_for <%= "@#{@rails.resource.collection_name}_search" %>, as: :<%= @rails.resource.collection_name %>_search, url: <%= @rails.path.collection %>, method: :get do |form| 32 | = form.error_notification 33 | .form-inputs 34 | <% @rails.resource.fields.select {|k,v| @rails.controller.search_fields.include?(k) }.each_pair do |field_name, field_type| %> 35 | <% if field_type == 'belongs_to' -%> 36 | = form.input :<%= field_name -%>_id 37 | <% elsif ['date', 'datetime'].include?(field_type) -%> 38 | = form.input :<%= field_name -%>, as: :date, html5: true 39 | <% elsif field_type == 'boolean' -%> 40 | = form.input :<%= field_name -%>, as: :boolean, input_html: {class: 'filled-in'} 41 | <% else %> 42 | = form.input :<%= field_name -%> 43 | <% end -%> 44 | <% end %> 45 | 46 | .form-actions 47 | = form.submit t('scaffold.index.search.button'), class: 'button' 48 | = link_to t('scaffold.index.search.reset'), url_for(<%= @rails.path.collection %>), class: 'button small secondary' 49 | -------------------------------------------------------------------------------- /lib/templates/pico/views/zurb/slim/new.html.slim.erb: -------------------------------------------------------------------------------- 1 | h1 = t('scaffold.new.title', model: <%= @rails.resource.class_name_with_modules %>.model_name.human(count: 1).mb_chars.downcase) 2 | 3 | .callout.clearfix 4 | .float-left 5 | = link_to t('scaffold.show.actions.index', model: <%= @rails.resource.class_name_with_modules %>.model_name.human(count: 2).mb_chars.downcase), <%= @rails.path.collection %>, class: 'button small primary' 6 | 7 | = simple_form_for <%= @rails.path.instance_resource %> do |form| 8 | = render 'form', form: form 9 | .form-actions 10 | = form.submit class: 'waves-effect waves btn-large' -------------------------------------------------------------------------------- /lib/templates/pico/views/zurb/slim/show.html.slim.erb: -------------------------------------------------------------------------------- 1 | h1 = t('scaffold.show.title', model: <%= @rails.resource.class_name_with_modules %>.model_name.human(count: 1).mb_chars.downcase) 2 | 3 | .callout.clearfix 4 | .float-left 5 | = link_to t('scaffold.show.actions.index', model: <%= @rails.resource.class_name_with_modules %>.model_name.human(count: 2).mb_chars.downcase), <%= @rails.path.collection %>, class: 'button small primary' 6 | .float-right 7 | = link_to t('scaffold.actions.edit', model: <%= @rails.resource.class_name_with_modules %>.model_name.human(count: 1).mb_chars.downcase), <%= @rails.path.edit_instance_resource %>, class: 'button small warning' 8 | = link_to t('scaffold.actions.new', model: <%= @rails.resource.class_name_with_modules %>.model_name.human(count: 1).mb_chars.downcase), <%= @rails.path.new_resource %>, class: 'button small secondary' 9 | 10 | table 11 | tbody 12 | <% @rails.resource.fields.keys.each do |field_name| %> 13 | tr 14 | td = <%= @rails.resource.class_name_with_modules -%>.human_attribute_name(:<%= field_name -%>) 15 | td = @<%= @rails.resource.name %>.<%= field_name -%> 16 | <% end %> 17 | -------------------------------------------------------------------------------- /rerun.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # http://superuser.com/questions/181517/how-to-execute-a-command-whenever-a-file-changes 4 | 5 | while true; do 6 | change=$(inotifywait -e close_write,moved_to,create -r /home/guda/scaffold_pico) 7 | change=${change#./ * } 8 | # if [ "$change" = "myfile.py" ]; then ./myfile.py; fi 9 | 10 | # exec 3>&1 11 | # output=$( asaa 2>&1 1>&3 ) 12 | # exec 3>&- 13 | 14 | ruby -I ~/scaffold_pico/lib ~/scaffold_pico/bin/scaffold_pico.rb -d -m Assistant -n administation/pencho --fields email:string first_name:string last_name:string terms:string 15 | 16 | 17 | if [ $? -ne 0 ]; then 18 | notify-send "Failed" 19 | fi 20 | 21 | 22 | done -------------------------------------------------------------------------------- /scaffold_pico.gemspec: -------------------------------------------------------------------------------- 1 | # Generated by jeweler 2 | # DO NOT EDIT THIS FILE DIRECTLY 3 | # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec' 4 | # -*- encoding: utf-8 -*- 5 | # stub: scaffold_pico 2.2.2 ruby lib 6 | 7 | Gem::Specification.new do |s| 8 | s.name = "scaffold_pico".freeze 9 | s.version = "2.2.2" 10 | 11 | s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version= 12 | s.require_paths = ["lib".freeze] 13 | s.authors = ["gudata".freeze] 14 | s.date = "2020-12-21" 15 | s.description = "Scaffolding".freeze 16 | s.email = "i.bardarov@gmail.com".freeze 17 | s.executables = ["scaffold_pico".freeze] 18 | s.extra_rdoc_files = [ 19 | "LICENSE.txt", 20 | "README.md" 21 | ] 22 | s.files = [ 23 | "lib/scaffold/cli.rb", 24 | "lib/scaffold/erb_context.rb", 25 | "lib/scaffold/generators/base_generator.rb", 26 | "lib/scaffold/generators/controller_generator.rb", 27 | "lib/scaffold/generators/fabricator_generator.rb", 28 | "lib/scaffold/generators/locales_generator.rb", 29 | "lib/scaffold/generators/models_generator.rb", 30 | "lib/scaffold/generators/routes_generator.rb", 31 | "lib/scaffold/generators/views_generator.rb", 32 | "lib/scaffold/main.rb", 33 | "lib/scaffold/models/resource.rb", 34 | "lib/scaffold/rails.rb", 35 | "lib/scaffold/services/controller.rb", 36 | "lib/scaffold/services/nested_in_resources.rb", 37 | "lib/scaffold/services/path.rb", 38 | "lib/scaffold/services/resource.rb", 39 | "lib/scaffold/services/route.rb", 40 | "lib/scaffold/services/view.rb", 41 | "lib/scaffold/template_engines/slim.rb", 42 | "lib/scaffold_pico.rb", 43 | "lib/templates/pico/controller.rb.erb", 44 | "lib/templates/pico/fabricators/fabrication.rb.erb", 45 | "lib/templates/pico/locales/bg.scaffold_pico.yml.erb", 46 | "lib/templates/pico/locales/en.scaffold_pico.yml.erb", 47 | "lib/templates/pico/model.rb.erb", 48 | "lib/templates/pico/search.rb.erb", 49 | "lib/templates/pico/views/materialize/slim/_form.html.slim.erb", 50 | "lib/templates/pico/views/materialize/slim/edit.html.slim.erb", 51 | "lib/templates/pico/views/materialize/slim/index.html.slim.erb", 52 | "lib/templates/pico/views/materialize/slim/new.html.slim.erb", 53 | "lib/templates/pico/views/materialize/slim/show.html.slim.erb", 54 | "lib/templates/pico/views/twitter_bootstrap_4x/slim/_form.html.slim.erb", 55 | "lib/templates/pico/views/twitter_bootstrap_4x/slim/edit.html.slim.erb", 56 | "lib/templates/pico/views/twitter_bootstrap_4x/slim/index.html.slim.erb", 57 | "lib/templates/pico/views/twitter_bootstrap_4x/slim/new.html.slim.erb", 58 | "lib/templates/pico/views/twitter_bootstrap_4x/slim/show.html.slim.erb", 59 | "lib/templates/pico/views/zurb/slim/_form.html.slim.erb", 60 | "lib/templates/pico/views/zurb/slim/edit.html.slim.erb", 61 | "lib/templates/pico/views/zurb/slim/index.html.slim.erb", 62 | "lib/templates/pico/views/zurb/slim/new.html.slim.erb", 63 | "lib/templates/pico/views/zurb/slim/show.html.slim.erb" 64 | ] 65 | s.homepage = "http://github.com/gudata/scaffold_pico".freeze 66 | s.licenses = ["MIT".freeze] 67 | s.rubygems_version = "3.0.3".freeze 68 | s.summary = "Scaffold should be simple".freeze 69 | 70 | if s.respond_to? :specification_version then 71 | s.specification_version = 4 72 | 73 | if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then 74 | s.add_runtime_dependency(%q.freeze, [">= 0"]) 75 | s.add_runtime_dependency(%q.freeze, [">= 0"]) 76 | s.add_runtime_dependency(%q.freeze, [">= 0"]) 77 | s.add_development_dependency(%q.freeze, ["~> 1.0"]) 78 | s.add_development_dependency(%q.freeze, [">= 0"]) 79 | else 80 | s.add_dependency(%q.freeze, [">= 0"]) 81 | s.add_dependency(%q.freeze, [">= 0"]) 82 | s.add_dependency(%q.freeze, [">= 0"]) 83 | s.add_dependency(%q.freeze, ["~> 1.0"]) 84 | s.add_dependency(%q.freeze, [">= 0"]) 85 | end 86 | else 87 | s.add_dependency(%q.freeze, [">= 0"]) 88 | s.add_dependency(%q.freeze, [">= 0"]) 89 | s.add_dependency(%q.freeze, [">= 0"]) 90 | s.add_dependency(%q.freeze, ["~> 1.0"]) 91 | s.add_dependency(%q.freeze, [">= 0"]) 92 | end 93 | end 94 | 95 | -------------------------------------------------------------------------------- /test/helper.rb: -------------------------------------------------------------------------------- 1 | require 'simplecov' 2 | 3 | module SimpleCov::Configuration 4 | def clean_filters 5 | @filters = [] 6 | end 7 | end 8 | 9 | SimpleCov.configure do 10 | clean_filters 11 | load_adapter 'test_frameworks' 12 | end 13 | 14 | ENV["COVERAGE"] && SimpleCov.start do 15 | add_filter "/.rvm/" 16 | end 17 | require 'rubygems' 18 | require 'bundler' 19 | begin 20 | Bundler.setup(:default, :development) 21 | rescue Bundler::BundlerError => e 22 | $stderr.puts e.message 23 | $stderr.puts "Run `bundle install` to install missing gems" 24 | exit e.status_code 25 | end 26 | require 'test/unit' 27 | require 'shoulda' 28 | 29 | $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib')) 30 | $LOAD_PATH.unshift(File.dirname(__FILE__)) 31 | require 'scaffold_pico' 32 | 33 | class Test::Unit::TestCase 34 | end 35 | -------------------------------------------------------------------------------- /test/test_scaffold_pico.rb: -------------------------------------------------------------------------------- 1 | require 'helper' 2 | 3 | class TestScaffoldPico < Test::Unit::TestCase 4 | should "probably rename this file and start testing for real" do 5 | flunk "hey buddy, you should probably rename this file and start testing for real" 6 | end 7 | end 8 | --------------------------------------------------------------------------------