├── .gitignore ├── .rspec ├── .ruby-gemset ├── .ruby-version ├── CHANGELOG.md ├── Gemfile ├── LICENSE.txt ├── README.md ├── Rakefile ├── bin └── scaffold ├── lib ├── schema_to_scaffold.rb └── schema_to_scaffold │ ├── attribute.rb │ ├── cli.rb │ ├── clipboard.rb │ ├── help.rb │ ├── path.rb │ ├── schema.rb │ ├── table.rb │ └── version.rb ├── schema_to_scaffold.gemspec └── spec ├── attribute_spec.rb ├── cli_spec.rb ├── clipboard_spec.rb ├── help_spec.rb ├── path_spec.rb ├── schema_spec.rb ├── schema_to_scaffold_spec.rb ├── spec_helper.rb ├── support ├── empty_schema.rb ├── no_schema │ └── .keep ├── schema.rb └── table.rb └── table_spec.rb /.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | *.rbc 3 | .bundle 4 | .config 5 | .yardoc 6 | Gemfile.lock 7 | InstalledFiles 8 | _yardoc 9 | coverage 10 | doc/ 11 | lib/bundler/man 12 | pkg 13 | rdoc 14 | spec/reports 15 | test/tmp 16 | test/version_tmp 17 | tmp 18 | .project 19 | /schema.rb 20 | .idea 21 | spec/examples.txt 22 | .byebug_history 23 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --color 2 | --require spec_helper 3 | -------------------------------------------------------------------------------- /.ruby-gemset: -------------------------------------------------------------------------------- 1 | schema-to-scaffold -------------------------------------------------------------------------------- /.ruby-version: -------------------------------------------------------------------------------- 1 | 2.7.4 2 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | All notable changes to this project will be documented in this file. 3 | 4 | ## 0.6.0 - 2014-Nov-19 5 | ### Added 6 | - all fields ending with "_id" will be treated as reference ex: t.integer "manager_id" will parse to manager:references 7 | 8 | ## 0.6.1 - 2014-Nov-19 9 | ### Added 10 | - this changelog 11 | 12 | ## 0.6.2 - 2014-Nov-19 13 | ### Added 14 | - ruby version dependency 15 | 16 | ## 0.7.0 - 2014-Nov-19 17 | ### More options to choose which tables will be used to generate a script -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | # Specify your gem's dependencies in schema_to_scaffold.gemspec 4 | gemspec 5 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 Humberto Pinto, João Soares 2 | 3 | MIT License 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Schema to Scaffold 2 | 3 | [![Code Climate](https://codeclimate.com/github/frenesim/schema_to_scaffold.png)](https://codeclimate.com/github/frenesim/schema_to_scaffold) 4 | 5 | This Gem generates Rails command strings based on a Rails database schema you already have. Unlike traditional migrations, which modify the database as they generate Rails scaffolding code, this Gem reads the schema for your database and generates the Rails code which matches your database's existing columns. 6 | 7 | This Gem does not modify anything; it simply prints a string which you can then use to invoke the Rails generators, and optionally copies the string to your clipboard. Generated string commands available are: 8 | ```bash 9 | rails generate scaffold 10 | rails generate factory_bot:model 11 | ``` 12 | 13 | Use your schema.rb file from `/db` or generated with `rake db:schema:dump`. You can optionally rename schema.rb to `schema_your_fav_name.rb` and it will still be found. Unique schema file names will prevent schema.rb from being overwritten if you use migrations and run `rake db:migrate`. 14 | 15 | Schema to Scaffold output looks like this: 16 | 17 | rails generate scaffold users fname:string lname:string bdate:date email:string encrypted_password:string 18 | 19 | It's possible to generate scripts for all your tables at once. Just enter `*` when selecting the table. 20 | 21 | ## Installation 22 | 23 | Assuming that you have rubygems-bundler installed, just type: 24 | 25 | gem install schema_to_scaffold 26 | 27 | ## Usage 28 | 29 | ```bash 30 | $ scaffold [options] 31 | ``` 32 | 33 | Generate a rails scaffold script for a given schema.rb 34 | ``` 35 | -h Displays help. 36 | -p It specifies a path to a folder or to a file. 37 | -c Will copy the script to your clipboard. Requires xclip be installed on Linux. 38 | -f Generates a factory_bot:model rather than a full scaffold. 39 | -m Add migration (use if your schema comes from a different database) 40 | ``` 41 | Examples: 42 | ```bash 43 | $ scaffold 44 | $ scaffold -c -p ~/work/rails/my_app 45 | $ scaffold -c -p ~/work/rails/my_app/db/schema.rb 46 | ``` 47 | 48 | ## Generators 49 | 50 | Since this gem assists you in invoking Rails generators for your scaffolding, make sure you've configured your generators with your preferred settings before you start. There's a good [Rails Guide](http://guides.rubyonrails.org/generators.html) and you can invoke `$ rails g scaffold -h` to see options. 51 | 52 | Generator options are configured in `config/application.rb`. Here is an example setting: 53 | 54 | ```ruby 55 | module YourApplication 56 | class Application < Rails::Application 57 | config.generators do |g| 58 | g.hidden_namespaces << :test_unit << :erb # Hide unwanted generators 59 | g.template_engine :slim # Select template engine 60 | g.helper false # Don't create view helpers 61 | g.test_framework :rspec, :view_specs => false 62 | g.integration_tool :rspec 63 | g.fixture_replacement :factory_bot # Choose between fixtures and factories 64 | g.factory_bot dir: 'spec/factories' 65 | g.javascript_engine :js # Disable coffeescript 66 | g.scaffold_controller "responders_controller" # from responders gem 67 | end 68 | end 69 | end 70 | ``` 71 | If you configure factory_bot as your fixture_replacement here, there is no need to invoke factory_bot separately with the `scaffold -f` command. 72 | 73 | ## Migrations 74 | 75 | Schema to Scaffold is set up by default to support creating scaffolds for your existing database, presuming that you have generated schema.rb with `rake db:schema:dump`. Therefore, no migrations are necessary, because the database already contains the desired table. If instead you are using a schema.rb that was generated from a database other than current development database, you can use the `-m` option to build a generator command that includes migrations. 76 | 77 | ## To install xclip on Linux 78 | 79 | `apt-get install xclip` 80 | 81 | ## Contributing 82 | 83 | Want to contribute? Great! 84 | 85 | 1. Fork it. 86 | 2. Create a branch (`git checkout -b my_schema_to_scaffold`) 87 | 3. Commit your changes (`git commit -am "Added great stuff"`) 88 | 4. Push to the branch (`git push origin my_schema_to_scaffold`) 89 | 5. Open a [Pull Request][1] 90 | 6. That's all!! 91 | 92 | [1]: http://github.com/frenesim/schema_to_scaffold/pulls 93 | 94 | ## Collaborate 95 | 96 | If you want to collaborate, please send me an email, or just create an issue or pull request. 97 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require "bundler/gem_tasks" 2 | require "rspec/core/rake_task" 3 | 4 | RSpec::Core::RakeTask.new(:spec) 5 | 6 | task default: :spec -------------------------------------------------------------------------------- /bin/scaffold: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # encoding: UTF-8 3 | 4 | # resolve bin path, ignoring symlinks 5 | require "pathname" 6 | bin_file = Pathname.new(__FILE__).realpath 7 | # add self to libpath 8 | $:.unshift File.expand_path("../../lib", bin_file) 9 | 10 | require 'schema_to_scaffold/cli' 11 | 12 | SchemaToScaffold::CLI.start(*ARGV) 13 | -------------------------------------------------------------------------------- /lib/schema_to_scaffold.rb: -------------------------------------------------------------------------------- 1 | require 'schema_to_scaffold/version' 2 | require 'schema_to_scaffold/schema' 3 | require 'schema_to_scaffold/table' 4 | require 'schema_to_scaffold/attribute' 5 | require 'schema_to_scaffold/path' 6 | require 'schema_to_scaffold/cli' 7 | require 'schema_to_scaffold/help' 8 | require 'schema_to_scaffold/clipboard' 9 | 10 | module SchemaToScaffold 11 | end 12 | -------------------------------------------------------------------------------- /lib/schema_to_scaffold/attribute.rb: -------------------------------------------------------------------------------- 1 | module SchemaToScaffold 2 | class Attribute 3 | 4 | attr_reader :name, :type 5 | 6 | def initialize(name, type) 7 | @name, @type = name, type 8 | end 9 | 10 | def to_script 11 | "#{name}:#{type}" unless ["created_at","updated_at"].include?(name) 12 | end 13 | 14 | def self.parse(attribute) 15 | match = attribute.match(/t\.(\w+)\s+"(\w+)"/) 16 | if match 17 | name = match.captures[1].sub(/_id$/, "") 18 | type = $&.nil? ? match.captures[0] : "references" 19 | Attribute.new(name, type) 20 | end 21 | end 22 | 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /lib/schema_to_scaffold/cli.rb: -------------------------------------------------------------------------------- 1 | require "schema_to_scaffold" 2 | module SchemaToScaffold 3 | class CLI 4 | 5 | TABLE_OPTIONS = "\nOptions are:\n4 for table 4; (4..6) for table 4 to 6; [4,6] for tables 4 and 6; * for all Tables" 6 | 7 | def self.start(*args) 8 | ## Argument conditions 9 | opts = parse_arguments(args) 10 | 11 | if opts[:help] 12 | puts Help.message 13 | exit 0 14 | end 15 | 16 | ## looking for /schema\S*.rb$/ in user directory 17 | paths = Path.new(opts[:path]) 18 | path = paths.choose unless opts[:path].to_s.match(/\.rb$/) 19 | 20 | ## Opening file 21 | path ||= opts[:path] 22 | begin 23 | data = File.open(path, 'r') { |f| f.read } 24 | rescue 25 | puts "\nUnable to open file '#{path}'" 26 | exit 1 27 | rescue Interrupt => e 28 | exit 1 29 | end 30 | 31 | ## Generate scripts from schema 32 | schema = Schema.new(data) 33 | 34 | begin 35 | raise if schema.table_names.empty? 36 | puts "\nLoaded tables:" 37 | schema.print_table_names 38 | puts TABLE_OPTIONS 39 | print "\nSelect a table: " 40 | rescue 41 | puts "Could not find tables in '#{path}'" 42 | exit 1 43 | end 44 | 45 | input = STDIN.gets.strip 46 | begin 47 | tables = schema.select_tables(input) 48 | raise if tables.empty? 49 | rescue 50 | puts "Not a valid input. #{TABLE_OPTIONS}" 51 | exit 1 52 | rescue Interrupt => e 53 | exit 1 54 | end 55 | 56 | script = [] 57 | target = opts[:factory_bot] ? "factory_bot:model" : "scaffold" 58 | migration_flag = opts[:migration] ? true : false 59 | 60 | tables.each do |table_id| 61 | script << generate_script(schema, table_id, target, migration_flag) 62 | end 63 | output = script.join("") 64 | puts "\nScript for #{target}:\n\n" 65 | puts output 66 | 67 | if opts[:clipboard] 68 | puts("\n(copied to your clipboard)") 69 | Clipboard.new(output).command 70 | end 71 | end 72 | 73 | ## 74 | # Parses ARGV and returns a hash of options. 75 | def self.parse_arguments(argv) 76 | if argv_index = argv.index("-p") 77 | path = argv.delete_at(argv_index + 1) 78 | argv.delete('-p') 79 | end 80 | 81 | args = { 82 | clipboard: argv.delete('-c'), # check for clipboard flag 83 | factory_bot: argv.delete('-f'), # factory_bot instead of scaffold 84 | migration: argv.delete('-m'), # generate migrations 85 | help: argv.delete('-h'), # check for help flag 86 | path: path # get path to file(s) 87 | } 88 | 89 | if argv.empty? 90 | args 91 | else 92 | puts "\n------\nWrong set of arguments.\n------\n" 93 | puts Help.message 94 | exit 95 | end 96 | end 97 | 98 | ## 99 | # Generates the rails scaffold script 100 | def self.generate_script(schema, table=nil, target, migration_flag) 101 | schema = Schema.new(schema) unless schema.is_a?(Schema) 102 | return schema.to_script if table.nil? 103 | schema.table(table).to_script(target, migration_flag) 104 | end 105 | 106 | end 107 | end 108 | -------------------------------------------------------------------------------- /lib/schema_to_scaffold/clipboard.rb: -------------------------------------------------------------------------------- 1 | module SchemaToScaffold 2 | class Clipboard 3 | attr_reader :output 4 | 5 | def initialize(output) 6 | @output = output 7 | end 8 | 9 | def command 10 | case platform 11 | when /darwin/i then darwin_command 12 | when /linux/i then linux_command 13 | when /mingw/i then win_command 14 | when /win/i then win_command 15 | end 16 | end 17 | 18 | private 19 | 20 | def darwin_command 21 | exec("echo '#{output}' | tr -d '\n' | pbcopy") 22 | end 23 | 24 | def linux_command 25 | exec("echo '#{output}' | tr -d '\n' | xclip -selection c") 26 | end 27 | 28 | def win_command 29 | exec("echo '#{output}' | clip") 30 | end 31 | 32 | def platform 33 | RUBY_PLATFORM 34 | end 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /lib/schema_to_scaffold/help.rb: -------------------------------------------------------------------------------- 1 | module SchemaToScaffold 2 | class Help 3 | ## Usage help text to print in all platforms 4 | GENERIC_HELP = <<-END_OF_HELP 5 | 6 | Usage: scaffold [options] 7 | Generate a rails scaffold script for a given schema.rb 8 | -h Displays help. 9 | -p It specifies a path to a folder or to a file. 10 | -c Will copy the script to your clipboard. Requires xclip be installed on Linux. 11 | -f Generates a factory_bot:model rather than a full scaffold. 12 | -m Add migration (use if your schema comes from a different database) 13 | 14 | END_OF_HELP 15 | 16 | ## Windows specific usage help text 17 | WINDOWS_HELP = <<-WINDOWS_SAMPLE 18 | Examples: 19 | scaffold 20 | scaffold -p C:\\Users\\JohnDoe 21 | scaffold -c -p C:\\Users\\JohnDoe\\Documents\\schema.rb 22 | WINDOWS_SAMPLE 23 | 24 | ## Linux specific usage help text 25 | LINUX_HELP = <<-LINUX_SAMPLE 26 | Examples: 27 | scaffold 28 | scaffold -c -p ~/work/rails/my_app 29 | scaffold -c -p ~/work/rails/my_app/db/schema.rb 30 | LINUX_SAMPLE 31 | 32 | def self.message 33 | return GENERIC_HELP + 34 | case platform 35 | when /darwin/i then LINUX_HELP 36 | when /linux/i then LINUX_HELP 37 | when /mingw/i then WINDOWS_HELP 38 | when /win/i then WINDOWS_HELP 39 | end 40 | end 41 | 42 | def self.platform 43 | RUBY_PLATFORM 44 | end 45 | end 46 | end -------------------------------------------------------------------------------- /lib/schema_to_scaffold/path.rb: -------------------------------------------------------------------------------- 1 | require 'find' 2 | module SchemaToScaffold 3 | ## 4 | # Deal with the path argument 5 | 6 | class Path 7 | 8 | def initialize(path) 9 | @path = path || Dir.pwd 10 | end 11 | 12 | ## 13 | # Return the chosen path 14 | def choose 15 | validate_path 16 | search_paths_list = search_paths 17 | if search_paths_list.empty? 18 | puts "\nThere is no /schema[^\/]*.rb$/ in the directory #{@path}" 19 | exit 20 | end 21 | 22 | search_paths_list.each_with_index {|path,indx| puts "#{indx}. #{path}" } 23 | 24 | begin 25 | print "\nSelect a path to the target schema: " 26 | while search_paths_list[(id = STDIN.gets.to_i)].nil?; end 27 | rescue Interrupt => e 28 | exit 1 29 | end 30 | 31 | 32 | search_paths_list[id] 33 | end 34 | 35 | private 36 | ## 37 | # Validate if a given path leads to a directory 38 | def validate_path 39 | if File.directory?(@path.to_s) 40 | puts "\nLooking for schema.rb in #{@path}" 41 | else 42 | puts "\nSorry #{@path} is not a valid directory!\nHere is an example:\nscaffold -p /home/foo/bar" 43 | exit 44 | end 45 | end 46 | 47 | ## 48 | # Will search for /schema[^\/]*.rb$/ in the current directory 49 | def search_paths 50 | result = [] 51 | Find.find(@path) do |s_p| 52 | result << s_p if s_p[/schema[^\/]*.rb$/] 53 | end 54 | result 55 | end 56 | 57 | end 58 | end 59 | -------------------------------------------------------------------------------- /lib/schema_to_scaffold/schema.rb: -------------------------------------------------------------------------------- 1 | module SchemaToScaffold 2 | class Schema 3 | 4 | attr_reader :data, :tables 5 | 6 | def initialize(data) 7 | @data, @tables = data, Schema.parse(data) 8 | end 9 | 10 | def table_names 11 | tables.map(&:name) 12 | end 13 | 14 | def print_table_names 15 | table_names.each_with_index { |name, i| puts "#{i}. #{name}" } 16 | end 17 | 18 | def select_tables(input) 19 | case input 20 | when "*" 21 | table_range.to_a 22 | when /^\d/ 23 | table_range.include?(input.to_i) ? [input.to_i] : [] 24 | end 25 | end 26 | 27 | def table(id) 28 | case id 29 | when Symbol then table(id.to_s) 30 | when String then tables[table_names.index(id)] 31 | when Integer then tables[id] 32 | else nil 33 | end 34 | end 35 | 36 | def to_script 37 | tables.map(&:to_script) 38 | end 39 | 40 | def self.parse(data) 41 | data.split(/^\s*create_/)[1..-1].map {|table_data| Table.parse(table_data) }.reject{ |e| e.nil? } 42 | end 43 | 44 | private 45 | 46 | def table_range 47 | 0...table_names.count 48 | end 49 | end 50 | end 51 | -------------------------------------------------------------------------------- /lib/schema_to_scaffold/table.rb: -------------------------------------------------------------------------------- 1 | module SchemaToScaffold 2 | require 'active_support/inflector' 3 | ## 4 | # fetch table names and convert them to a scaffold syntax 5 | 6 | class Table 7 | 8 | attr_reader :attributes, :name 9 | 10 | def initialize(name, attributes) 11 | @name, @attributes = name, attributes 12 | end 13 | 14 | def to_script(target, migration_flag) 15 | begin 16 | attributes_list = attributes.map(&:to_script).reject { |x| x.nil? || x.empty? }.join(' ') 17 | rescue => e 18 | puts "\n ---------------------------------------------" 19 | puts e.message 20 | puts "Table \n\n\n #{self.inspect} \n\n\n" 21 | puts "\n ---------------------------------------------" 22 | end 23 | script = [] 24 | script << "rails generate #{target} #{modelize(name)} #{attributes_list}" 25 | script << " --no-migration" unless migration_flag 26 | script << "\n\n" 27 | script 28 | end 29 | 30 | def self.parse(table_data) 31 | return unless name = table_data[/table "([^"]+?)"/] 32 | name = $1 33 | table_fields = table_fields_of(table_data) 34 | Table.new(name, table_fields) 35 | end 36 | 37 | private 38 | def self.table_fields_of(table_data) 39 | table_data.lines.to_a.select { |line| line =~ /t\.(?!index)\w+/ }.map { |att| Attribute.parse(att) } 40 | end 41 | 42 | private 43 | 44 | def modelize(string) 45 | string.camelize.singularize 46 | end 47 | 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /lib/schema_to_scaffold/version.rb: -------------------------------------------------------------------------------- 1 | module SchemaToScaffold 2 | MAJOR = 0 3 | MINOR = 8 4 | REVISION = 2 5 | VERSION = [MAJOR, MINOR, REVISION].join('.') 6 | end 7 | -------------------------------------------------------------------------------- /schema_to_scaffold.gemspec: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | lib = File.expand_path('../lib', __FILE__) 3 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 4 | require 'schema_to_scaffold/version' 5 | 6 | Gem::Specification.new do |gem| 7 | gem.name = "schema_to_scaffold" 8 | gem.version = SchemaToScaffold::VERSION 9 | gem.authors = ["João Soares", "Humberto Pinto"] 10 | gem.email = ["jsoaresgeral@gmail.com", "hlsp999@gmail.com"] 11 | gem.description = <<-EOD 12 | Command line app which parses a schema.rb file obtained from your rails repo or by running rake:schema:dump 13 | EOD 14 | gem.summary = %q{Generate rails scaffold script from a schema.rb file.} 15 | gem.homepage = "http://github.com/frenesim/schema_to_scaffold" 16 | gem.bindir = "bin" 17 | gem.files = `git ls-files`.split($/) 18 | gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) } 19 | gem.require_paths = ["lib"] 20 | gem.licenses = ['MIT'] 21 | gem.required_ruby_version = '>= 1.9.3' 22 | 23 | gem.add_runtime_dependency('activesupport', '~> 7') 24 | gem.add_development_dependency('byebug', '~> 11') 25 | gem.add_development_dependency('rspec', '~> 3') 26 | gem.add_development_dependency('simplecov', '~> 0.21') 27 | end 28 | -------------------------------------------------------------------------------- /spec/attribute_spec.rb: -------------------------------------------------------------------------------- 1 | module SchemaToScaffold 2 | RSpec.describe Attribute do 3 | 4 | describe ".parse" do 5 | it "parses _id attribute to references" do 6 | attribute = Attribute.parse('t.integer "user_id"') 7 | expect(attribute.type).to eq("references") 8 | expect(attribute.name).to eq("user") 9 | end 10 | 11 | it "parses string attributes" do 12 | attribute = Attribute.parse('t.string "filename"') 13 | expect(attribute.type).to eq("string") 14 | expect(attribute.name).to eq("filename") 15 | end 16 | 17 | it "parses datetime attributes" do 18 | attribute = Attribute.parse('t.datetime "created_at", null: false') 19 | expect(attribute.type).to eq("datetime") 20 | expect(attribute.name).to eq("created_at") 21 | end 22 | 23 | it "parses limit value" do 24 | pending 25 | attribute = Attribute.parse('t.integer "quota", limit: 8') 26 | expect(attribute.type).to eq("integer") 27 | expect(attribute.name).to eq("quota") 28 | expect(attribute.limit).to eq("8") 29 | end 30 | 31 | it "parses decimals precision and scale values" do 32 | pending 33 | attribute = Attribute.parse('t.decimal "price", precision: 20, scale: 2, default: 0.0') 34 | expect(attribute.type).to eq("decimal") 35 | expect(attribute.name).to eq("price") 36 | expect(attribute.precision).to eq("20") 37 | expect(attribute.scale).to eq("2") 38 | end 39 | end 40 | 41 | describe "#to_script" do 42 | it "returns name and type" do 43 | attribute = Attribute.parse('t.string "filename"') 44 | expect(attribute.to_script).to eq("filename:string") 45 | end 46 | 47 | it "ignores updated_at" do 48 | attribute = Attribute.parse('t.datetime "updated_at", null: false') 49 | expect(attribute.to_script).to be_nil 50 | end 51 | 52 | it "ignores created_at" do 53 | attribute = Attribute.parse('t.datetime "created_at", null: false') 54 | expect(attribute.to_script).to be_nil 55 | end 56 | 57 | it "adds limit value to integer" do 58 | pending 59 | attribute = Attribute.parse('t.integer "quota", limit: 8') 60 | expect(attribute.to_script).to eq("quota:integer{8}") 61 | end 62 | 63 | it "adds precision and scale value to decimal" do 64 | pending 65 | attribute = Attribute.parse('t.decimal "price", precision: 20, scale: 2, default: 0.0') 66 | expect(attribute.to_script).to eq("price:decimal{20,2}") 67 | end 68 | end 69 | end 70 | end 71 | -------------------------------------------------------------------------------- /spec/cli_spec.rb: -------------------------------------------------------------------------------- 1 | module SchemaToScaffold 2 | RSpec.describe CLI do 3 | 4 | let(:empty_schema_file) { File.expand_path(File.dirname(__FILE__)) + '/support/empty_schema.rb' } 5 | let(:schema_file) { File.expand_path(File.dirname(__FILE__)) + '/support/schema.rb' } 6 | 7 | describe ".start" do 8 | it "prints help message" do 9 | expect { 10 | begin 11 | SchemaToScaffold::CLI.start("-h") 12 | rescue SystemExit 13 | end 14 | }.to output(/Usage:/).to_stdout 15 | end 16 | 17 | it "prints file not found message" do 18 | expect { 19 | begin 20 | SchemaToScaffold::CLI.start("-p", "/support/schema.rb") 21 | rescue SystemExit 22 | end 23 | }.to output(/Unable to open file /).to_stdout 24 | end 25 | 26 | it "prints could not find tables message" do 27 | expect { 28 | begin 29 | SchemaToScaffold::CLI.start("-p", empty_schema_file) 30 | rescue SystemExit 31 | end 32 | }.to output(/Could not find tables /).to_stdout 33 | end 34 | 35 | context "choose table" do 36 | before do 37 | allow(STDIN).to receive(:gets) { "" } 38 | end 39 | 40 | it "prints Not a valid input message" do 41 | expect { 42 | begin 43 | SchemaToScaffold::CLI.start("-p", schema_file) 44 | rescue SystemExit 45 | end 46 | }.to output(/Not a valid input/).to_stdout 47 | end 48 | end 49 | 50 | context "choose all tables" do 51 | before do 52 | allow(STDIN).to receive(:gets).once { "*" } 53 | end 54 | 55 | it "prints scaffold lines message" do 56 | expect { 57 | begin 58 | SchemaToScaffold::CLI.start("-p", schema_file) 59 | rescue SystemExit 60 | end 61 | }.to output(/Script for/).to_stdout 62 | end 63 | end 64 | 65 | context "copies first table to clipboard" do 66 | before do 67 | allow(STDIN).to receive(:gets).once { "0" } 68 | expect_any_instance_of(Kernel).to receive(:exec).with("echo 'rails generate scaffold User email:string encrypted_password:string reset_password_token:string reset_password_sent_at:datetime remember_created_at:datetime sign_in_count:integer current_sign_in_at:datetime last_sign_in_at:datetime current_sign_in_ip:string last_sign_in_ip:string name:string role:integer --no-migration\n\n' | tr -d '\n' | pbcopy") 69 | expect_any_instance_of(Clipboard).to receive(:platform).and_return("/darwin/i") 70 | end 71 | 72 | it "prints copied to your clipboard message" do 73 | expect { 74 | begin 75 | SchemaToScaffold::CLI.start("-c", "-p", schema_file) 76 | rescue SystemExit 77 | end 78 | }.to output(/copied to your clipboard/).to_stdout 79 | end 80 | end 81 | end 82 | 83 | describe ".parse_arguments" do 84 | it "handles wrong arguments" do 85 | expect { 86 | begin 87 | SchemaToScaffold::CLI.parse_arguments(["--help"]) 88 | rescue SystemExit 89 | end 90 | }.to output(/Wrong set of arguments/).to_stdout 91 | end 92 | 93 | context "handles arguments" do 94 | it "help argument" do 95 | expect(CLI.parse_arguments(["-h"])).to eq({clipboard: nil, factory_bot: nil, migration: nil, help: "-h", path: nil}) 96 | end 97 | 98 | it "path argument" do 99 | expect(CLI.parse_arguments(["-p", "/some/path"])).to eq({clipboard: nil, factory_bot: nil, migration: nil, help: nil, path: "/some/path"}) 100 | end 101 | 102 | it "clipboard argument" do 103 | expect(CLI.parse_arguments(["-c"])).to eq({clipboard: "-c", factory_bot: nil, migration: nil, help: nil, path: nil}) 104 | end 105 | 106 | it "factory girl argument" do 107 | expect(CLI.parse_arguments(["-f"])).to eq({clipboard: nil, factory_bot: "-f", migration: nil, help: nil, path: nil}) 108 | end 109 | 110 | it "migration argument" do 111 | expect(CLI.parse_arguments(["-m"])).to eq({clipboard: nil, factory_bot: nil, migration: "-m", help: nil, path: nil}) 112 | end 113 | end 114 | end 115 | end 116 | end 117 | -------------------------------------------------------------------------------- /spec/clipboard_spec.rb: -------------------------------------------------------------------------------- 1 | module SchemaToScaffold 2 | RSpec.describe Clipboard do 3 | let(:output) { "rails g model ....." } 4 | let(:clipboard) { Clipboard.new(output) } 5 | 6 | describe "#command" do 7 | context "darwin" do 8 | before do 9 | allow(clipboard).to receive(:platform).and_return("darwin") 10 | expect_any_instance_of(Kernel).to receive(:exec).with("echo 'rails g model .....' | tr -d '\n' | pbcopy") 11 | end 12 | 13 | it "executes darwin command" do 14 | clipboard.command 15 | end 16 | end 17 | 18 | context "linux" do 19 | before do 20 | allow(clipboard).to receive(:platform).and_return("linux") 21 | expect_any_instance_of(Kernel).to receive(:exec).with("echo 'rails g model .....' | tr -d '\n' | xclip -selection c") 22 | end 23 | 24 | it "executes linux command" do 25 | clipboard.command 26 | end 27 | end 28 | 29 | context "windows (win)" do 30 | before do 31 | allow(clipboard).to receive(:platform).and_return("win") 32 | expect_any_instance_of(Kernel).to receive(:exec).with("echo 'rails g model .....' | clip") 33 | end 34 | 35 | it "executes windows command" do 36 | clipboard.command 37 | end 38 | end 39 | 40 | context "windows (mingw)" do 41 | before do 42 | allow(clipboard).to receive(:platform).and_return("mingw") 43 | expect_any_instance_of(Kernel).to receive(:exec).with("echo 'rails g model .....' | clip") 44 | end 45 | 46 | it "executes windows command" do 47 | clipboard.command 48 | end 49 | end 50 | end 51 | end 52 | end 53 | -------------------------------------------------------------------------------- /spec/help_spec.rb: -------------------------------------------------------------------------------- 1 | module SchemaToScaffold 2 | RSpec.describe Help do 3 | describe ".message" do 4 | it "returns message for darwin platform" do 5 | allow(Help).to receive(:platform).and_return("darwin") 6 | expect(Help.message).to include("work/rails/my_app/") 7 | end 8 | 9 | it "returns message for linux platform" do 10 | allow(Help).to receive(:platform).and_return("linux") 11 | expect(Help.message).to include("work/rails/my_app/") 12 | end 13 | 14 | it "returns message for windows(win) platform" do 15 | allow(Help).to receive(:platform).and_return("win") 16 | expect(Help.message).to include("JohnDoe\\Documents") 17 | end 18 | 19 | it "returns message for windows (mingw) platform" do 20 | allow(Help).to receive(:platform).and_return("mingw") 21 | expect(Help.message).to include("JohnDoe\\Documents") 22 | end 23 | end 24 | end 25 | end -------------------------------------------------------------------------------- /spec/path_spec.rb: -------------------------------------------------------------------------------- 1 | module SchemaToScaffold 2 | RSpec.describe Path do 3 | 4 | describe "#choose" do 5 | it "prints error when given path is not an directory" do 6 | path = Path.new("path_spec.rb") 7 | expect { 8 | begin 9 | path.choose 10 | rescue SystemExit 11 | end 12 | }.to output(/Sorry path_spec\.rb is not a valid directory!/).to_stdout 13 | end 14 | 15 | it "prints message when given path is a directory but no schema is found" do 16 | path = Path.new(File.expand_path(File.dirname(__FILE__)) + '/support/no_schema') 17 | expect { 18 | begin 19 | path.choose 20 | rescue SystemExit 21 | end 22 | }.to output(/There is no/).to_stdout 23 | end 24 | 25 | context "needs user input" do 26 | let(:path) { Path.new(File.expand_path(File.dirname(__FILE__)) + '/support') } 27 | before do 28 | allow(STDIN).to receive(:gets) { "0" } 29 | end 30 | 31 | it "prints message when given path is a directory" do 32 | expect { 33 | path.choose 34 | }.to output(/Select a path to the target schema/).to_stdout 35 | end 36 | end 37 | 38 | 39 | end 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /spec/schema_spec.rb: -------------------------------------------------------------------------------- 1 | module SchemaToScaffold 2 | RSpec.describe Schema do 3 | 4 | let(:schema_data) { File.read(File.expand_path(File.dirname(__FILE__)) + '/support/schema.rb') } 5 | let(:schema) { Schema.new(schema_data) } 6 | 7 | describe ".parse" do 8 | it "parses given schema file" do 9 | expect(Schema.parse(schema_data)).to be_kind_of(Array) 10 | end 11 | end 12 | 13 | describe "#table_names" do 14 | it "returns table names" do 15 | expect(schema.table_names).to eq(["users"]) 16 | end 17 | end 18 | 19 | describe "#to_script" do 20 | it "is broken" do 21 | pending 22 | expect(schema.to_script).to eq("users") 23 | end 24 | end 25 | 26 | describe "#table" do 27 | it "returns parsed table by given table name as symbol" do 28 | expect(schema.table(:users)).to be_kind_of(SchemaToScaffold::Table) 29 | end 30 | 31 | it "returns parsed table by given table index" do 32 | expect(schema.table(0)).to be_kind_of(SchemaToScaffold::Table) 33 | end 34 | 35 | it "returns nil" do 36 | expect(schema.table([])).to be_nil 37 | end 38 | 39 | it "returns nil" do 40 | pending 41 | expect(schema.table("")).to be_nil 42 | end 43 | end 44 | end 45 | end 46 | -------------------------------------------------------------------------------- /spec/schema_to_scaffold_spec.rb: -------------------------------------------------------------------------------- 1 | RSpec.describe SchemaToScaffold do 2 | it "has a version number" do 3 | expect(SchemaToScaffold::VERSION).not_to be nil 4 | end 5 | end -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | # This file was generated by the `rspec --init` command. Conventionally, all 2 | # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`. 3 | # The generated `.rspec` file contains `--require spec_helper` which will cause 4 | # this file to always be loaded, without a need to explicitly require it in any 5 | # files. 6 | 7 | $LOAD_PATH.unshift File.expand_path("../../lib", __FILE__) 8 | 9 | require "simplecov" 10 | SimpleCov.start 'test_frameworks' 11 | 12 | require "schema_to_scaffold" 13 | require "byebug" 14 | 15 | # Given that it is always loaded, you are encouraged to keep this file as 16 | # light-weight as possible. Requiring heavyweight dependencies from this file 17 | # will add to the boot time of your test suite on EVERY test run, even for an 18 | # individual file that may not need all of that loaded. Instead, consider making 19 | # a separate helper file that requires the additional dependencies and performs 20 | # the additional setup, and require it from the spec files that actually need 21 | # it. 22 | # 23 | # The `.rspec` file also contains a few flags that are not defaults but that 24 | # users commonly want. 25 | # 26 | # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration 27 | RSpec.configure do |config| 28 | # rspec-expectations config goes here. You can use an alternate 29 | # assertion/expectation library such as wrong or the stdlib/minitest 30 | # assertions if you prefer. 31 | config.expect_with :rspec do |expectations| 32 | # This option will default to `true` in RSpec 4. It makes the `description` 33 | # and `failure_message` of custom matchers include text for helper methods 34 | # defined using `chain`, e.g.: 35 | # be_bigger_than(2).and_smaller_than(4).description 36 | # # => "be bigger than 2 and smaller than 4" 37 | # ...rather than: 38 | # # => "be bigger than 2" 39 | expectations.include_chain_clauses_in_custom_matcher_descriptions = true 40 | end 41 | 42 | # rspec-mocks config goes here. You can use an alternate test double 43 | # library (such as bogus or mocha) by changing the `mock_with` option here. 44 | config.mock_with :rspec do |mocks| 45 | # Prevents you from mocking or stubbing a method that does not exist on 46 | # a real object. This is generally recommended, and will default to 47 | # `true` in RSpec 4. 48 | mocks.verify_partial_doubles = true 49 | end 50 | 51 | # This option will default to `:apply_to_host_groups` in RSpec 4 (and will 52 | # have no way to turn it off -- the option exists only for backwards 53 | # compatibility in RSpec 3). It causes shared context metadata to be 54 | # inherited by the metadata hash of host groups and examples, rather than 55 | # triggering implicit auto-inclusion in groups with matching metadata. 56 | config.shared_context_metadata_behavior = :apply_to_host_groups 57 | 58 | # The settings below are suggested to provide a good initial experience 59 | # with RSpec, but feel free to customize to your heart's content. 60 | 61 | # This allows you to limit a spec run to individual examples or groups 62 | # you care about by tagging them with `:focus` metadata. When nothing 63 | # is tagged with `:focus`, all examples get run. RSpec also provides 64 | # aliases for `it`, `describe`, and `context` that include `:focus` 65 | # metadata: `fit`, `fdescribe` and `fcontext`, respectively. 66 | config.filter_run_when_matching :focus 67 | 68 | # Allows RSpec to persist some state between runs in order to support 69 | # the `--only-failures` and `--next-failure` CLI options. We recommend 70 | # you configure your source control system to ignore this file. 71 | config.example_status_persistence_file_path = "spec/examples.txt" 72 | 73 | # Limits the available syntax to the non-monkey patched syntax that is 74 | # recommended. For more details, see: 75 | # - http://rspec.info/blog/2012/06/rspecs-new-expectation-syntax/ 76 | # - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/ 77 | # - http://rspec.info/blog/2014/05/notable-changes-in-rspec-3/#zero-monkey-patching-mode 78 | config.disable_monkey_patching! 79 | 80 | # This setting enables warnings. It's recommended, but in some cases may 81 | # be too noisy due to issues in dependencies. 82 | config.warnings = true 83 | 84 | # Many RSpec users commonly either run the entire suite or an individual 85 | # file, and it's useful to allow more verbose output when running an 86 | # individual spec file. 87 | if config.files_to_run.one? 88 | # Use the documentation formatter for detailed output, 89 | # unless a formatter has already been configured 90 | # (e.g. via a command-line flag). 91 | config.default_formatter = 'doc' 92 | end 93 | 94 | # Print the 10 slowest examples and example groups at the 95 | # end of the spec run, to help surface which specs are running 96 | # particularly slow. 97 | config.profile_examples = 10 98 | 99 | # Run specs in random order to surface order dependencies. If you find an 100 | # order dependency and want to debug it, you can fix the order by providing 101 | # the seed, which is printed after each run. 102 | # --seed 1234 103 | config.order = :random 104 | 105 | # Seed global randomization in this process using the `--seed` CLI option. 106 | # Setting this allows you to use `--seed` to deterministically reproduce 107 | # test failures related to randomization by passing the same `--seed` value 108 | # as the one that triggered the failure. 109 | Kernel.srand config.seed 110 | 111 | end 112 | -------------------------------------------------------------------------------- /spec/support/empty_schema.rb: -------------------------------------------------------------------------------- 1 | # random schema https://github.com/RailsApps/rails-devise-roles/blob/master/db/schema.rb 2 | ActiveRecord::Schema.define(version: 20140828012915) do 3 | 4 | create_table :users, force: :cascade do |t| 5 | end 6 | 7 | end -------------------------------------------------------------------------------- /spec/support/no_schema/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frenesim/schema_to_scaffold/53667e1da92af3ec8055239f0bfbdaeaaeb08507/spec/support/no_schema/.keep -------------------------------------------------------------------------------- /spec/support/schema.rb: -------------------------------------------------------------------------------- 1 | # random schema https://github.com/RailsApps/rails-devise-roles/blob/master/db/schema.rb 2 | ActiveRecord::Schema.define(version: 20140828012915) do 3 | 4 | create_table "users", force: :cascade do |t| 5 | t.string "email", default: "", null: false 6 | t.string "encrypted_password", default: "", null: false 7 | t.string "reset_password_token" 8 | t.datetime "reset_password_sent_at" 9 | t.datetime "remember_created_at" 10 | t.integer "sign_in_count", default: 0, null: false 11 | t.datetime "current_sign_in_at" 12 | t.datetime "last_sign_in_at" 13 | t.string "current_sign_in_ip" 14 | t.string "last_sign_in_ip" 15 | t.datetime "created_at" 16 | t.datetime "updated_at" 17 | t.string "name" 18 | t.integer "role" 19 | end 20 | 21 | add_index "users", ["email"], name: "index_users_on_email", unique: true 22 | add_index "users", ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true 23 | 24 | end -------------------------------------------------------------------------------- /spec/support/table.rb: -------------------------------------------------------------------------------- 1 | create_table "users", force: :cascade do |t| 2 | t.string "email", default: "", null: false 3 | t.string "encrypted_password", default: "", null: false 4 | t.string "reset_password_token" 5 | t.datetime "reset_password_sent_at" 6 | t.datetime "remember_created_at" 7 | t.integer "sign_in_count", default: 0, null: false 8 | t.datetime "current_sign_in_at" 9 | t.datetime "last_sign_in_at" 10 | t.string "current_sign_in_ip" 11 | t.string "last_sign_in_ip" 12 | t.datetime "created_at" 13 | t.datetime "updated_at" 14 | t.string "name" 15 | t.integer "role" 16 | t.index ["basket_id"], name: "index_orders_on_basket_id", using: :btree 17 | end -------------------------------------------------------------------------------- /spec/table_spec.rb: -------------------------------------------------------------------------------- 1 | module SchemaToScaffold 2 | RSpec.describe Table do 3 | 4 | let(:table_data) { File.read(File.expand_path(File.dirname(__FILE__)) + '/support/table.rb') } 5 | let(:email_attribute) { Attribute.new("email", "string") } 6 | let(:password_attribute) { Attribute.new("password", "string") } 7 | 8 | describe "#to_script" do 9 | let(:table) { Table.new("users", [email_attribute, password_attribute]) } 10 | 11 | it "returns rails generate line" do 12 | expect(table.to_script("model", true)).to eq(["rails generate model User email:string password:string", "\n\n"]) 13 | end 14 | 15 | it "returns rails generate line without migrations" do 16 | expect(table.to_script("model", false)).to eq(["rails generate model User email:string password:string", " --no-migration", "\n\n"]) 17 | end 18 | 19 | it "raises an exception" do 20 | allow(email_attribute).to receive(:to_script).and_raise(StandardError, "something went wrong") 21 | expect { table.to_script("model", true) }.to output(/something went wrong/).to_stdout 22 | end 23 | end 24 | 25 | describe ".parse" do 26 | let(:table) { Table.parse(table_data) } 27 | it "parses given table data" do 28 | expect(table).to be_kind_of(SchemaToScaffold::Table) 29 | end 30 | end 31 | 32 | end 33 | end --------------------------------------------------------------------------------