├── .gitignore
├── CHANGELOG.md
├── LICENSE.md
├── Manifest.txt
├── NOTES.md
├── README.md
├── Rakefile
├── bin
├── qk
└── quik
├── lib
├── quik.rb
└── quik
│ ├── builder.rb
│ ├── catalog.rb
│ ├── cli
│ ├── main.rb
│ └── opts.rb
│ ├── colors.rb
│ ├── config.rb
│ ├── merger.rb
│ ├── package.rb
│ ├── version.rb
│ └── wizard.rb
└── test
├── data
└── gem-starter-template
│ ├── .gitignore
│ ├── Manifest.txt
│ ├── README.md
│ └── lib
│ ├── $filename$.rb
│ └── __filename__
│ ├── $filename$.rb
│ ├── __filename__
│ └── test.rb
│ └── version.rb
├── helper.rb
├── test_colors.rb
├── test_config.rb
├── test_merger.rb
├── test_package.rb
└── test_wizard.rb
/.gitignore:
--------------------------------------------------------------------------------
1 | # ignore generated folders
2 | pkg/
3 | doc/
4 |
5 | ## temp folders
6 | tmp/
7 | temp/
8 |
9 |
10 | # ignore jekyll generated output
11 | site/_site/
12 |
13 | ###
14 | # for testing downloading and unzipping packages ignore
15 |
16 | o/
17 | *.zip
18 | gem.rb
19 | gemtest.rb
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ### 0.0.1 / 2015-08-17
2 |
3 | * Everything is new. First release
4 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | CC0 1.0 Universal
2 |
3 | Statement of Purpose
4 |
5 | The laws of most jurisdictions throughout the world automatically confer
6 | exclusive Copyright and Related Rights (defined below) upon the creator and
7 | subsequent owner(s) (each and all, an "owner") of an original work of
8 | authorship and/or a database (each, a "Work").
9 |
10 | Certain owners wish to permanently relinquish those rights to a Work for the
11 | purpose of contributing to a commons of creative, cultural and scientific
12 | works ("Commons") that the public can reliably and without fear of later
13 | claims of infringement build upon, modify, incorporate in other works, reuse
14 | and redistribute as freely as possible in any form whatsoever and for any
15 | purposes, including without limitation commercial purposes. These owners may
16 | contribute to the Commons to promote the ideal of a free culture and the
17 | further production of creative, cultural and scientific works, or to gain
18 | reputation or greater distribution for their Work in part through the use and
19 | efforts of others.
20 |
21 | For these and/or other purposes and motivations, and without any expectation
22 | of additional consideration or compensation, the person associating CC0 with a
23 | Work (the "Affirmer"), to the extent that he or she is an owner of Copyright
24 | and Related Rights in the Work, voluntarily elects to apply CC0 to the Work
25 | and publicly distribute the Work under its terms, with knowledge of his or her
26 | Copyright and Related Rights in the Work and the meaning and intended legal
27 | effect of CC0 on those rights.
28 |
29 | 1. Copyright and Related Rights. A Work made available under CC0 may be
30 | protected by copyright and related or neighboring rights ("Copyright and
31 | Related Rights"). Copyright and Related Rights include, but are not limited
32 | to, the following:
33 |
34 | i. the right to reproduce, adapt, distribute, perform, display, communicate,
35 | and translate a Work;
36 |
37 | ii. moral rights retained by the original author(s) and/or performer(s);
38 |
39 | iii. publicity and privacy rights pertaining to a person's image or likeness
40 | depicted in a Work;
41 |
42 | iv. rights protecting against unfair competition in regards to a Work,
43 | subject to the limitations in paragraph 4(a), below;
44 |
45 | v. rights protecting the extraction, dissemination, use and reuse of data in
46 | a Work;
47 |
48 | vi. database rights (such as those arising under Directive 96/9/EC of the
49 | European Parliament and of the Council of 11 March 1996 on the legal
50 | protection of databases, and under any national implementation thereof,
51 | including any amended or successor version of such directive); and
52 |
53 | vii. other similar, equivalent or corresponding rights throughout the world
54 | based on applicable law or treaty, and any national implementations thereof.
55 |
56 | 2. Waiver. To the greatest extent permitted by, but not in contravention of,
57 | applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and
58 | unconditionally waives, abandons, and surrenders all of Affirmer's Copyright
59 | and Related Rights and associated claims and causes of action, whether now
60 | known or unknown (including existing as well as future claims and causes of
61 | action), in the Work (i) in all territories worldwide, (ii) for the maximum
62 | duration provided by applicable law or treaty (including future time
63 | extensions), (iii) in any current or future medium and for any number of
64 | copies, and (iv) for any purpose whatsoever, including without limitation
65 | commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes
66 | the Waiver for the benefit of each member of the public at large and to the
67 | detriment of Affirmer's heirs and successors, fully intending that such Waiver
68 | shall not be subject to revocation, rescission, cancellation, termination, or
69 | any other legal or equitable action to disrupt the quiet enjoyment of the Work
70 | by the public as contemplated by Affirmer's express Statement of Purpose.
71 |
72 | 3. Public License Fallback. Should any part of the Waiver for any reason be
73 | judged legally invalid or ineffective under applicable law, then the Waiver
74 | shall be preserved to the maximum extent permitted taking into account
75 | Affirmer's express Statement of Purpose. In addition, to the extent the Waiver
76 | is so judged Affirmer hereby grants to each affected person a royalty-free,
77 | non transferable, non sublicensable, non exclusive, irrevocable and
78 | unconditional license to exercise Affirmer's Copyright and Related Rights in
79 | the Work (i) in all territories worldwide, (ii) for the maximum duration
80 | provided by applicable law or treaty (including future time extensions), (iii)
81 | in any current or future medium and for any number of copies, and (iv) for any
82 | purpose whatsoever, including without limitation commercial, advertising or
83 | promotional purposes (the "License"). The License shall be deemed effective as
84 | of the date CC0 was applied by Affirmer to the Work. Should any part of the
85 | License for any reason be judged legally invalid or ineffective under
86 | applicable law, such partial invalidity or ineffectiveness shall not
87 | invalidate the remainder of the License, and in such case Affirmer hereby
88 | affirms that he or she will not (i) exercise any of his or her remaining
89 | Copyright and Related Rights in the Work or (ii) assert any associated claims
90 | and causes of action with respect to the Work, in either case contrary to
91 | Affirmer's express Statement of Purpose.
92 |
93 | 4. Limitations and Disclaimers.
94 |
95 | a. No trademark or patent rights held by Affirmer are waived, abandoned,
96 | surrendered, licensed or otherwise affected by this document.
97 |
98 | b. Affirmer offers the Work as-is and makes no representations or warranties
99 | of any kind concerning the Work, express, implied, statutory or otherwise,
100 | including without limitation warranties of title, merchantability, fitness
101 | for a particular purpose, non infringement, or the absence of latent or
102 | other defects, accuracy, or the present or absence of errors, whether or not
103 | discoverable, all to the greatest extent permissible under applicable law.
104 |
105 | c. Affirmer disclaims responsibility for clearing rights of other persons
106 | that may apply to the Work or any use thereof, including without limitation
107 | any person's Copyright and Related Rights in the Work. Further, Affirmer
108 | disclaims responsibility for obtaining any necessary consents, permissions
109 | or other rights required for any use of the Work.
110 |
111 | d. Affirmer understands and acknowledges that Creative Commons is not a
112 | party to this document and has no duty or obligation with respect to this
113 | CC0 or use of the Work.
114 |
115 | For more information, please see
116 |
--------------------------------------------------------------------------------
/Manifest.txt:
--------------------------------------------------------------------------------
1 | CHANGELOG.md
2 | LICENSE.md
3 | Manifest.txt
4 | README.md
5 | Rakefile
6 | bin/qk
7 | bin/quik
8 | lib/quik.rb
9 | lib/quik/builder.rb
10 | lib/quik/catalog.rb
11 | lib/quik/cli/main.rb
12 | lib/quik/cli/opts.rb
13 | lib/quik/colors.rb
14 | lib/quik/config.rb
15 | lib/quik/merger.rb
16 | lib/quik/package.rb
17 | lib/quik/version.rb
18 | lib/quik/wizard.rb
19 | test/data/gem-starter-template/Manifest.txt
20 | test/data/gem-starter-template/README.md
21 | test/data/gem-starter-template/lib/$filename$.rb
22 | test/data/gem-starter-template/lib/__filename__/$filename$.rb
23 | test/data/gem-starter-template/lib/__filename__/__filename__/test.rb
24 | test/data/gem-starter-template/lib/__filename__/version.rb
25 | test/helper.rb
26 | test/test_colors.rb
27 | test/test_config.rb
28 | test/test_merger.rb
29 | test/test_package.rb
30 | test/test_wizard.rb
31 |
--------------------------------------------------------------------------------
/NOTES.md:
--------------------------------------------------------------------------------
1 | # Notes
2 |
3 | ## Todos
4 |
5 | - [ ] move todos over here :-)
6 |
7 | ## Quick Starter Gems / Libraries
8 |
9 | - - use ERB and Thor for template generation and scripting
10 | -
11 |
12 |
13 |
14 | ## Ideas
15 |
16 | - [ ] add remove .zip file after download and unpack
17 |
18 | by @StartZeroGnu (see https://github.com/quikstart/quik/issues/2)
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # qk/quik - ruby quick starter template script wizard .:. the missing code generator
2 |
3 | * home :: [github.com/quikstart/quik](https://github.com/quikstart/quik)
4 | * bugs :: [github.com/quikstart/quik/issues](https://github.com/quikstart/quik/issues)
5 | * gem :: [rubygems.org/gems/quik](https://rubygems.org/gems/quik)
6 | * rdoc :: [rubydoc.info/gems/quik](http://rubydoc.info/gems/quik)
7 |
8 |
9 | ## Usage
10 |
11 | The quick gem includes a command line tool that lets you
12 | run quick starter template scripts. Try:
13 |
14 | ```
15 | $ quik --help # or
16 | $ qk -h
17 | ```
18 |
19 | Resulting in:
20 |
21 | ```
22 | NAME
23 | qk/quik - ruby quick starter template script wizard .:. the missing code generator
24 |
25 | SYNOPSIS
26 | quik [global options] command [command options] [arguments...]
27 |
28 | VERSION
29 | 1.0.0
30 |
31 | GLOBAL OPTIONS
32 | --help - Show this message
33 | --test, --dry_run - (Debug) Dry run; run script in simulation for testing
34 | --verbose - (Debug) Show debug messages
35 | --version - Display the program version
36 |
37 | COMMANDS
38 | list, ls, l - List ruby quick starter scripts
39 | new, n - Run ruby quick starter script
40 |
41 | help - Shows a list of commands or help for one command
42 | test - (Debug) Test command suite
43 | ```
44 |
45 |
46 | ### Commands
47 |
48 | [List Wizards](#list-wizard-command---list-ls-l) •
49 | [New Wizard](#new-wizard-command---new-n)
50 |
51 |
52 | #### List Wizards Command - `list`, `ls`, `l`
53 |
54 | Use:
55 |
56 | ```
57 | $ quik list # or
58 | $ quik ls # or
59 | $ quik l # or
60 | $ qk l
61 | ```
62 |
63 | Resulting in:
64 |
65 | ```
66 | 1..gem .:. Gem Quick Starter Template
67 | 2..gem-hoe .:. Gem Quick Starter Template (Hoe Classic Edition)
68 | 3..sinatra .:. Sinatra Quick Starter Template
69 | ...
70 | ```
71 |
72 |
73 | #### New Wizard Command - `new`, `n`
74 |
75 | To run a quick starter template wizard script
76 | to download and install (unzip/unpack) a template archive and configure
77 | the code ready-to-use. Try:
78 |
79 |
80 | ```
81 | $ quik new gem # or
82 | $ quik n gem # or
83 | $ qk n gem
84 | ```
85 |
86 | This will download the [`quik.rb`](https://github.com/quikstart/gem-starter-template/blob/master/quik.rb) wizard script from the
87 | gem starter template repo
88 | and run through all steps e.g.:
89 |
90 | ```
91 | Welcome, to the gem quick starter script.
92 |
93 | Q: What's your gem's name? [hola]: hello
94 | Q: What's your gem's module? [Hola]: Hello
95 |
96 | Thanks! Ready-to-go. Stand back.
97 |
98 | Downloading Gem Starter Template...
99 | Setting up Gem Starter Template...
100 | ...
101 | Done.
102 | ```
103 |
104 | That's it. Now the gem starter code is ready in the `hello`
105 | folder.
106 |
107 |
108 | **More Quick Starter Wizard Scripts**
109 |
110 | For more ruby quick starter scripts, see the [Quik Scripts](https://github.com/quikstart/scripts)
111 | catalog / directory.
112 |
113 |
114 | ## Install
115 |
116 | Just install the gem:
117 |
118 | $ gem install quik
119 |
120 |
121 | ## License
122 |
123 | The `quik` scripts are dedicated to the public domain.
124 | Use it as you please with no restrictions whatsoever.
125 |
126 | ## Questions? Comments?
127 |
128 | Send them along to the ruby-talk mailing list.
129 | Thanks!
130 |
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
1 | require 'hoe'
2 | require './lib/quik/version.rb'
3 |
4 | Hoe.spec 'quik' do
5 |
6 | self.version = Quik::VERSION
7 |
8 | self.summary = 'qk/quik - ruby quick starter template script wizard .:. the missing code generator'
9 | self.description = summary
10 |
11 | self.urls = { home: 'https://github.com/quikstart/quik' }
12 |
13 | self.author = 'Gerald Bauer'
14 | self.email = 'ruby-talk@ruby-lang.org'
15 |
16 | # switch extension to .markdown for gihub formatting
17 | self.readme_file = 'README.md'
18 | self.history_file = 'CHANGELOG.md'
19 |
20 | self.extra_deps = [
21 | ['logutils'],
22 | ['fetcher'],
23 | ['rubyzip'],
24 | ['gli'],
25 | ]
26 |
27 | self.licenses = ['Public Domain']
28 |
29 | self.spec_extras = {
30 | required_ruby_version: '>= 2.2.2'
31 | }
32 |
33 | end
34 |
--------------------------------------------------------------------------------
/bin/qk:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | require 'quik'
4 |
5 | Quik.main
6 |
--------------------------------------------------------------------------------
/bin/quik:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | require 'quik'
4 |
5 | Quik.main
6 |
--------------------------------------------------------------------------------
/lib/quik.rb:
--------------------------------------------------------------------------------
1 | # encoding: utf-8
2 |
3 | # stdlibs
4 | require 'pp'
5 | require 'fileutils'
6 | require 'yaml'
7 | require 'json'
8 | require 'uri'
9 | require 'time'
10 | require 'date'
11 |
12 |
13 | # 3rd party libs/gems
14 | require 'fetcher'
15 |
16 | ## more 3rd party gems
17 | require 'zip' # use $ gem install rubyzip
18 | require 'gli'
19 |
20 |
21 | # our own code
22 | require 'quik/version' # let version always go first
23 |
24 |
25 | module Quik
26 | ### builtin default urls
27 | QUIK_SCRIPTS_URL = "https://github.com/quikstart/scripts/raw/master/scripts.yml"
28 | end
29 |
30 |
31 |
32 | require 'quik/catalog'
33 | require 'quik/package'
34 | require 'quik/merger'
35 | require 'quik/config'
36 | require 'quik/wizard'
37 | require 'quik/builder'
38 | require 'quik/colors'
39 |
40 |
41 | require 'quik/cli/opts'
42 | require 'quik/cli/main'
43 |
44 |
45 | module Quik
46 | def self.main( args=ARGV )
47 | exit Tool.new.run( args )
48 | end
49 | end # module Quik
50 |
51 |
52 |
53 |
54 | ## say hello
55 | puts Quik.banner if defined?($RUBYLIBS_DEBUG) && $RUBYLIBS_DEBUG
56 |
--------------------------------------------------------------------------------
/lib/quik/builder.rb:
--------------------------------------------------------------------------------
1 | #encoding: utf-8
2 |
3 | module Quik
4 |
5 | class Builder
6 |
7 | def self.load_file( path, opts={} )
8 | code = File.open( path, 'r:utf-8' ) { |f| f.read }
9 | self.load( code, opts )
10 | end
11 |
12 | def self.load( code, opts={} )
13 | builder = Builder.new( opts )
14 | builder.instance_eval( code )
15 | builder
16 | end
17 |
18 |
19 | include Wizard ## mixin helpers for say, ask, yes?, no?, select, etc.
20 |
21 | def initialize( opts={} )
22 | puts "==> starting new Quik script with options #{opts.inspect}; lets go"
23 |
24 | @test = opts[:dry_run] || opts[:test] || false
25 | @output_dir = opts[:o] || '.'
26 | @pak = nil ## for now reference last template pack (in future support many - why?? why not??)
27 | end
28 |
29 | ## "global" builder options
30 | def test?() @test; end ## dry_run option (defaults to false)
31 | def output_dir() @output_dir; end ## ouptput (root) dir (defaults to . e.g. working folder)
32 |
33 |
34 |
35 | def use( name, opts={} ) ## short alias for install_template
36 | install_template( name, opts )
37 | end
38 |
39 | def install_template( name, opts= {} )
40 | puts "==> handle install_template >#{name}< with options #{opts.inspect}"
41 |
42 | ## note for now assume name is key
43 | ## e.g. always downcase (e.g. Gem => gem)
44 | key = name.downcase
45 |
46 | if test?
47 | # do nothing; dry run
48 | else
49 | @pak = Package.new( key )
50 |
51 | puts "GET #{@pak.remote_zip_url}".bold.green ## output network access in green bold
52 | @pak.download
53 | ## pak.unzip( "#{@output_dir}/#{key}" )
54 | end
55 | end
56 |
57 |
58 | def config( opts={} )
59 | puts "==> handle config block"
60 | c = OpenConfig.new
61 | yield( c )
62 | ## pp c
63 | h = c.to_h
64 |
65 | pp h
66 |
67 | ##
68 | # check for name
69 | ## required for output
70 | ## if no name
71 | ## use ./ working folder for output!!!
72 |
73 | name = h['name']
74 |
75 | if name.nil? || name.empty?
76 | fail 'sorry; for now name is required for output folder'
77 | end
78 |
79 | if test?
80 | ## do nothing; dry run
81 | else
82 | if @pak
83 | unzip_dir = "./#{name}"
84 | ## step 1: unzip templates
85 | @pak.unzip( unzip_dir )
86 | ## step 2: merge templates (using hash/config settings)
87 | m = Quik::Merger.new
88 | m.merge( unzip_dir, h )
89 | end
90 | end
91 | end # method config
92 |
93 |
94 | end # class Builder
95 |
96 | end # module Quik
97 |
--------------------------------------------------------------------------------
/lib/quik/catalog.rb:
--------------------------------------------------------------------------------
1 | # encoding: utf-8
2 |
3 | module Quik
4 |
5 | class Catalog
6 |
7 | def self.from_url( src )
8 | worker = Fetcher::Worker.new
9 | text = worker.read_utf8!( src )
10 | new( text )
11 | end
12 |
13 | def self.from_file( path )
14 | ## read in themes catalog
15 | text = File.open( path, 'r:utf-8' ) { |f| f.read }
16 | new( text )
17 | end
18 |
19 | # def self.from_string( text )
20 | # self.new( text )
21 | # end
22 |
23 |
24 | def initialize( text )
25 | recs = YAML.load( text )
26 | ## note: convert array to hash (lookup by name)
27 | ## todo: check for missing name property? - why? why not?
28 | @scripts = recs.reduce( {} ) { |h,rec| h[rec['name']] = rec; h }
29 | end
30 |
31 |
32 | def find( name )
33 | @scripts[ name ]
34 | end
35 |
36 | def list( filter=nil )
37 | ## pp filter
38 |
39 | @scripts.each_with_index do |(_,rec),i|
40 | name = rec['name']
41 | summary = rec['summary']
42 | script = rec['script']
43 |
44 | if script && script.index( '//github.com/')
45 | ## "beautify" / shorten github links - remove raw/master/
46 | script = script.gsub('raw/master/', '' )
47 | end
48 |
49 | ## pp h
50 |
51 | line = ''
52 | line << " %3d" % (i+1)
53 | line << "..%-10s" % name
54 | line << " .:. #{summary}"
55 | line << " @ (#{script})"
56 |
57 | if filter
58 | ## note: ignore case (upper/lower/downcase) for now
59 | ## filter on name/title and
60 | ## tags for now
61 | if name.downcase.index(filter.downcase) != nil || ## note: 0 is match found on index 0; only nil is not found
62 | summary.downcase.index(filter.downcase) != nil
63 | puts line
64 | end
65 | else
66 | puts line
67 | end
68 | end
69 | end # method filter
70 |
71 | end # class Catalog
72 |
73 | end # module Quik
74 |
--------------------------------------------------------------------------------
/lib/quik/cli/main.rb:
--------------------------------------------------------------------------------
1 | ################
2 | ## NOTE: wrap gli config into a class
3 | ## see github.com/davetron5000/gli/issues/153
4 |
5 | module Quik
6 |
7 | class Tool
8 | def initialize
9 | LogUtils::Logger.root.level = :info # set logging level to info
10 | end
11 |
12 | def run( args )
13 | puts Quik.banner
14 | Toolii.run( args )
15 | end
16 | end
17 |
18 |
19 | ## NOTE: gli added function are class methods (thus, wrap class Toolii in Tool for now)
20 |
21 | class Toolii
22 | extend GLI::App
23 |
24 | def self.logger=(value) @@logger=value; end
25 | def self.logger() @@logger; end
26 |
27 | ## todo: find a better name e.g. change to settings? config? safe_opts? why? why not?
28 | def self.opts=(value) @@opts = value; end
29 | def self.opts() @@opts; end
30 |
31 |
32 | logger = LogUtils::Logger.root
33 | opts = Opts.new
34 |
35 |
36 | program_desc 'ruby quick starter template script wizard .:. the missing code generator'
37 | version VERSION
38 |
39 |
40 | ## desc 'Use only local (offline) cached data; no (online) remote network access'
41 | ## switch [:l, :local], negatable: false
42 |
43 | desc '(Debug) Show debug messages'
44 | switch [:verbose], negatable: false ## todo: use -w for short form? check ruby interpreter if in use too?
45 |
46 | desc '(Debug) Dry run; run script in simulation for testing'
47 | switch [:test, :dry_run], negatable: false
48 |
49 |
50 |
51 | def self.fetch_catalog
52 | ## scripts_dir = "#{Quik.root}/test/data"
53 | ## catalog = Catalog.new( "#{scripts_dir}/scripts.yml" )
54 |
55 | url = QUIK_SCRIPTS_URL
56 |
57 | puts "GET #{url}".bold.green ## output network access in green bold
58 |
59 | catalog = Catalog.from_url( url )
60 | catalog
61 | end
62 |
63 |
64 | def self.fetch_script( name_or_path )
65 |
66 | ## first try local version in working folder
67 |
68 | text = ''
69 |
70 | extname = File.extname( name_or_path )
71 |
72 |
73 | ## try local first
74 | ## todo/fix: check in ~/.quik/ dir too?
75 |
76 | path = if extname == '.rb' && File.exist?( name_or_path )
77 | name_or_path
78 | elsif extname == '' && File.exist?( "#{name_or_path}.rb" )
79 | "#{name_or_path}.rb"
80 | elsif extname == '' && File.exist?( "~/.quik/#{name_or_path}.rb" )
81 | "~/.quik/#{name_or_path}.rb" ## todo/check - if (~) shortcut works in windows -too?
82 | else
83 | nil
84 | end
85 |
86 | if path
87 | text = File.open( path, 'r:utf-8' ) { |f| f.read }
88 | else ## try fetch remote script
89 |
90 | url = nil
91 |
92 | if name_or_path.index( '/' ).nil? ## assume short-cut name if no (/) in name
93 | ## do a lookup from catalog!!!
94 | catalog = fetch_catalog
95 | rec = catalog.find( name_or_path )
96 | url = rec['script'] if rec
97 |
98 | if url.nil?
99 | puts "!! ERROR - sorry no wizard script found / available for name >#{name_or_path}<"
100 | exit 1
101 | end
102 | else
103 | ## assume custom github url shortcut
104 | url = "https://github.com/#{name_or_path}/raw/master/quik.rb"
105 | end
106 |
107 | ## assume utf8 text encoding for now
108 | puts "GET #{url}".bold.green ## output network access in green bold
109 |
110 | worker = Fetcher::Worker.new
111 | text = worker.read_utf8!( url )
112 | end
113 |
114 | text
115 | end
116 |
117 |
118 |
119 | desc "List ruby quick starter scripts"
120 | arg_name 'QUERY' # optional search query/filter
121 | command [:list,:ls,:l] do |c|
122 |
123 | c.action do |g,o,args|
124 | ## read in scripts diretory
125 | catalog = fetch_catalog
126 | catalog.list( args[0] ) ## note: pass in filter e.g. args[0]; may be nil (no filter)
127 | puts 'Done.'
128 | end # action
129 | end # command list
130 |
131 |
132 | desc "Run ruby quick starter script"
133 | arg_name 'NAME' # required theme name
134 | command [:new,:n] do |c|
135 |
136 | c.action do |g,o,args|
137 |
138 | name = args[0] || 'gem'
139 |
140 | script = fetch_script( name )
141 | if opts.test?
142 | puts "dry (test) run:"
143 | Builder.load( script, test: true )
144 | else
145 | Builder.load( script )
146 | end
147 |
148 | puts 'Done.'
149 | end # action
150 | end # command setup
151 |
152 |
153 |
154 | desc '(Debug) Test command suite'
155 | command :test do |c|
156 | c.action do |g,o,args|
157 |
158 | puts "hello from test command"
159 | puts "args (#{args.class.name}):"
160 | pp args
161 | puts "o (#{o.class.name}):"
162 | pp o
163 | puts "g (#{g.class.name}):"
164 | pp g
165 |
166 | LogUtils::Logger.root.debug 'test debug msg'
167 | LogUtils::Logger.root.info 'test info msg'
168 | LogUtils::Logger.root.warn 'test warn msg'
169 |
170 | puts 'Done.'
171 | end
172 | end
173 |
174 |
175 |
176 | pre do |g,c,o,args|
177 | opts.merge_gli_options!( g )
178 | opts.merge_gli_options!( o )
179 |
180 | puts Quik.banner
181 |
182 | if opts.verbose?
183 | LogUtils::Logger.root.level = :debug
184 | end
185 |
186 | logger.debug "Executing #{c.name}"
187 | true
188 | end
189 |
190 | post do |global,c,o,args|
191 | logger.debug "Executed #{c.name}"
192 | true
193 | end
194 |
195 |
196 | on_error do |e|
197 | puts
198 | puts "*** error: #{e.message}"
199 |
200 | if opts.verbose?
201 | puts e.backtrace
202 | end
203 |
204 | false # skip default error handling
205 | end
206 |
207 |
208 | ### exit run(ARGV) ## note: use Toolii.run( ARGV ) outside of class
209 |
210 | end # class Toolii
211 |
212 | end # module Quik
213 |
214 |
--------------------------------------------------------------------------------
/lib/quik/cli/opts.rb:
--------------------------------------------------------------------------------
1 | # encoding: utf-8
2 |
3 | module Quik
4 |
5 | class Opts
6 |
7 | def merge_gli_options!( options = {} )
8 | @test = true if options[:test] == true
9 | @verbose = true if options[:verbose] == true
10 | end
11 |
12 |
13 | def verbose=(boolean) # add: alias for debug ??
14 | @verbose = boolean
15 | end
16 |
17 | def verbose?
18 | return false if @verbose.nil? # default verbose/debug flag is false
19 | @verbose == true
20 | end
21 |
22 | def test=(boolean)
23 | @test = boolean
24 | end
25 |
26 | def test?
27 | return false if @test.nil? # default test/dry-run flag is false
28 | @test == true
29 | end
30 |
31 | end # class Opts
32 |
33 | end # module Quik
34 |
--------------------------------------------------------------------------------
/lib/quik/colors.rb:
--------------------------------------------------------------------------------
1 | # encoding: utf-8
2 |
3 | ### todo: move to textutils for (re)use - why? why not??
4 | ### todo add some references to alternative color gems
5 | ##
6 | ## e.g. https://github.com/fazibear/colorize
7 | ## http://github.com/ssoroka/ansi
8 | ## http://flori.github.com/term-ansicolor/
9 | ## http://github.com/sickill/rainbow
10 | ## https://github.com/janlelis/paint
11 |
12 |
13 | ## move to core_ext.rb - why? why not??
14 |
15 | ###
16 | ## fix: use css style
17 | ## e.g. color( :red )
18 | ## color( :blue )
19 | ## background_color( :red )
20 | ## font_style( :bold ) -- why? why not??
21 |
22 | class String
23 |
24 | def self.use_colors?
25 | @@use_color ||= true
26 | ## todo/fix: check for windows on load (set to false;otherwise true)
27 | @@use_color
28 | end
29 |
30 | def self.use_colors=(value)
31 | @@use_color = value
32 | end
33 |
34 |
35 | def red() colorize(31); end
36 | def green() colorize(32); end
37 | def yellow() colorize(33); end ## note: more brown-ish (if not used w/ bold/bright mode) ??
38 | def blue() colorize(34); end
39 | def magenta() colorize(35); end ## pink ??
40 | def cyan() colorize(36); end ## light blue ??
41 |
42 | def bold() colorize(1); end ## just use 0 clear for now; instead of BOLD_OFF (22) code; use bright as an alias??
43 |
44 | private
45 | def colorize(code)
46 | if self.class.use_colors?
47 | "\e[#{code}m#{self}\e[0m"
48 | else
49 | self
50 | end
51 | end
52 |
53 |
54 | ## todo - add modes e.g. bold/underscore/etc.
55 | =begin
56 | class String
57 | def black; "\e[30m#{self}\e[0m" end
58 | def red; "\e[31m#{self}\e[0m" end
59 | def green; "\e[32m#{self}\e[0m" end
60 | def brown; "\e[33m#{self}\e[0m" end
61 | def blue; "\e[34m#{self}\e[0m" end
62 | def magenta; "\e[35m#{self}\e[0m" end
63 | def cyan; "\e[36m#{self}\e[0m" end
64 | def gray; "\e[37m#{self}\e[0m" end
65 |
66 | def on_black; "\e[40m#{self}\e[0m" end
67 | def on_red; "\e[41m#{self}\e[0m" end
68 | def on_green; "\e[42m#{self}\e[0m" end
69 | def on_brown; "\e[43m#{self}\e[0m" end
70 | def on_blue; "\e[44m#{self}\e[0m" end
71 | def on_magenta; "\e[45m#{self}\e[0m" end
72 | def on_cyan; "\e[46m#{self}\e[0m" end
73 | def on_gray; "\e[47m#{self}\e[0m" end
74 |
75 | def bold; "\e[1m#{self}\e[21m" end
76 | def italic; "\e[3m#{self}\e[23m" end
77 | def underline; "\e[4m#{self}\e[24m" end
78 | def blink; "\e[5m#{self}\e[25m" end
79 | def reverse_color; "\e[7m#{self}\e[27m" end
80 |
81 | use Ansi constants - why? why not? e.g.:
82 | //foreground color
83 | public static final String BLACK_TEXT() { return "\033[30m";}
84 | public static final String RED_TEXT() { return "\033[31m";}
85 | public static final String GREEN_TEXT() { return "\033[32m";}
86 | public static final String BROWN_TEXT() { return "\033[33m";}
87 | public static final String BLUE_TEXT() { return "\033[34m";}
88 | public static final String MAGENTA_TEXT() { return "\033[35m";}
89 | public static final String CYAN_TEXT() { return "\033[36m";}
90 | public static final String GRAY_TEXT() { return "\033[37m";}
91 |
92 | //background color
93 | public static final String BLACK_BACK() { return "\033[40m";}
94 | public static final String RED_BACK() { return "\033[41m";}
95 | public static final String GREEN_BACK() { return "\033[42m";}
96 | public static final String BROWN_BACK() { return "\033[43m";}
97 | public static final String BLUE_BACK() { return "\033[44m";}
98 | public static final String MAGENTA_BACK() { return "\033[45m";}
99 | public static final String CYAN_BACK() { return "\033[46m";}
100 | public static final String WHITE_BACK() { return "\033[47m";}
101 |
102 | //ANSI control chars
103 | public static final String RESET_COLORS() { return "\033[0m";}
104 | public static final String BOLD_ON() { return "\033[1m";}
105 | public static final String BLINK_ON() { return "\033[5m";}
106 | public static final String REVERSE_ON() { return "\033[7m";}
107 | public static final String BOLD_OFF() { return "\033[22m";}
108 | public static final String BLINK_OFF() { return "\033[25m";}
109 | public static final String REVERSE_OFF() { return "\033[27m";}
110 | end
111 |
112 | Code Effect
113 | 0 Turn off all attributes
114 | 1 Set bright mode
115 | 4 Set underline mode
116 | 5 Set blink mode
117 | 7 Exchange foreground and background colors
118 | 8 Hide text (foreground color would be the same as background)
119 | 30 Black text
120 | 31 Red text
121 | 32 Green text
122 | 33 Yellow text
123 | 34 Blue text
124 | 35 Magenta text
125 | 36 Cyan text
126 | 37 White text
127 | 39 Default text color
128 | 40 Black background
129 | 41 Red background
130 | 42 Green background
131 | 43 Yellow background
132 | 44 Blue background
133 | 45 Magenta background
134 | 46 Cyan background
135 | 47 White background
136 | 49 Default background color
137 |
138 | note:
139 | puts "\e[31m" # set format (red foreground)
140 | puts "\e[0" # clear format
141 | puts "green-#{"red".red}-green".green # will be green-red-normal, because of \e[0
142 | e.g. for now colors can NOT get nested
143 | plus if you bold/italic/etc. use it before the color e.g.
144 | bold.red etc.
145 | =end
146 |
147 |
148 | end # class String
149 |
--------------------------------------------------------------------------------
/lib/quik/config.rb:
--------------------------------------------------------------------------------
1 | #encoding: utf-8
2 |
3 | module Quik
4 |
5 | ##
6 | # used for config block
7 | # lets you access props (even nested) that don't yet exist
8 | # and all props get stored in a hash
9 | #
10 | # e.g
11 | # c = OpenConfig.new
12 | # c.title = 'title'
13 | # c.author.name = 'name'
14 |
15 | # c.quik.last_updated = Time.now
16 | # c.quik.title = 'title'
17 | # c.quik.name = 'name'
18 | # c.quik.theme = 'theme'
19 |
20 | class OpenConfig
21 |
22 | def initialize
23 | @h = {}
24 | end
25 |
26 | def to_h
27 | h = {}
28 | @h.each do |k,v|
29 | if v.is_a? OpenConfig
30 | h[ k ] = v.to_h
31 | else
32 | h[ k ] = v ## just pass along as is
33 | end
34 | end
35 | h
36 | end
37 |
38 | def method_missing( m, *args, &block)
39 | if m.to_s =~ /^(.*)=$/ ## setter
40 | puts "config lookup (setter) >#{m}< #{m.class.name}, #{args.inspect}"
41 | key = m[0..-2].to_s ## cut off trailing =
42 | @h[ key ] = args[0].to_s # note: assume first arg is value for setter
43 | # note: for now all values are strings (always use to_s)
44 | else ## assume getter
45 | ## fix: add check for args?? must be 0 for getters??
46 | ## use else super to delegate non-getters??
47 | puts "config lookup (getter) >#{m}< #{m.class.name}"
48 | key = m.to_s
49 | value = @h[ key ]
50 | if value.nil?
51 | puts " config add (nested) hash"
52 | value = @h[ key ] = OpenConfig.new
53 | end
54 | value
55 | end
56 | end # method_missing
57 |
58 | end # class OpenConfig
59 |
60 | end # module Quik
61 |
--------------------------------------------------------------------------------
/lib/quik/merger.rb:
--------------------------------------------------------------------------------
1 | # encoding: utf-8
2 |
3 | module Quik
4 |
5 | class Merger
6 |
7 |
8 |
9 | def find_files( root_dir )
10 | tree = []
11 |
12 | files = Dir.entries( root_dir )
13 | files = files.sort
14 | puts "#{root_dir}:"
15 | pp files
16 |
17 | files.each do |file|
18 | if File.directory?( "#{root_dir}/#{file}" )
19 | ## note: skip directory if it starts with dot e.g. . or .. or .git etc.
20 | if file.start_with?( '.' )
21 | puts "skipping directory >#{file}< (#{root_dir})"
22 | next
23 | end
24 | subtree = find_files( "#{root_dir}/#{file}" )
25 | tree << [ file, subtree ]
26 | else
27 | ## just a "regular" file
28 | tree << file
29 | end
30 | end
31 | tree
32 | end
33 |
34 |
35 | def merge_filenames( root_dir, hash, opts={} ) ## flags e.g. noop, verbose, etc. see FileUtils
36 | tree = find_files( root_dir )
37 | pp tree
38 |
39 | flags = { ## always use verbose mode for now
40 | verbose: true
41 | }
42 |
43 | ## todo/check: move opts={} to initialize e.g. Merger.new( opts={}) why? why not??
44 |
45 | ## check for noop e.g. test mode/dry run
46 | flags[ :noop ] = true if opts[:test] || opts[:dry_run] || opts[:noop]
47 |
48 | puts "walk tree:"
49 | merge_filenames_worker_step1( root_dir, hash, tree, flags )
50 | merge_filenames_worker_step2( root_dir, hash, tree, flags )
51 | end
52 |
53 | def merge_filenames_worker_step1( root_dir, hash, tree, flags )
54 | ## note: step 1 move all files
55 | ## step 2 move all dirs
56 |
57 | tree.each do |node|
58 | if node.is_a? Array ## assume it's a directory
59 | merge_filenames_worker_step1( "#{root_dir}/#{node[0]}", hash, node[1], flags )
60 | else ## assume it's a file
61 | old_name = node
62 | new_name = merge_path( old_name, hash )
63 | if old_name == new_name
64 | puts " keep file #{node} (#{root_dir})"
65 | else
66 | puts " *** move file #{old_name} => #{new_name} (#{root_dir})"
67 | src_path = "#{root_dir}/#{old_name}"
68 | dest_path = "#{root_dir}/#{new_name}"
69 | ## note: make sure subpath exists (e.g. replacement might include new (sub)dirs too)
70 | FileUtils.mkdir_p( File.dirname( dest_path ), flags ) unless Dir.exist?( File.dirname( dest_path ))
71 | FileUtils.mv( src_path, dest_path, flags )
72 | end
73 | end
74 | end
75 | end
76 |
77 | def merge_filenames_worker_step2( root_dir, hash, tree, flags )
78 | ## note: step 1 move all files
79 | ## step 2 move all dirs
80 |
81 | tree.each do |node|
82 | if node.is_a? Array ## assume it's a directory
83 | merge_filenames_worker_step2( "#{root_dir}/#{node[0]}", hash, node[1], flags )
84 | old_name = node[0]
85 | new_name = merge_path( old_name, hash )
86 | if old_name == new_name
87 | puts " keep dir #{node[0]} (#{root_dir})"
88 | else
89 | puts " *** move dir #{old_name} => #{new_name} (#{root_dir})"
90 | src_path = "#{root_dir}/#{old_name}"
91 | dest_path = "#{root_dir}/#{new_name}"
92 | ## note: make sure subpath exists (e.g. replacement might include new (sub)dirs too)
93 | FileUtils.mkdir_p( File.dirname( dest_path ), flags ) unless Dir.exist?( File.dirname( dest_path ))
94 | FileUtils.mv( src_path, dest_path, flags )
95 | end
96 | end
97 | end
98 | end
99 |
100 |
101 |
102 | def merge_files( root_dir, hash, opts={} )
103 | ## note: rescan files (after renames)
104 | tree = find_files( root_dir )
105 | pp tree
106 |
107 | puts "walk tree:"
108 | merge_files_worker( root_dir, '', hash, tree, opts )
109 | end
110 |
111 | def merge_files_worker( root_dir, relative_dir, hash, tree, opts )
112 | tree.each do |node|
113 | if node.is_a? Array ## assume it's a directory
114 | if relative_dir.empty? # e.g. just add w/o leading slash e.g. 'lib' and not '/lib'
115 | new_relative_dir = node[0].dup # note: create a new string; just in case
116 | else
117 | new_relative_dir = "#{relative_dir}/#{node[0]}"
118 | end
119 | merge_files_worker( root_dir, new_relative_dir, hash, node[1], opts )
120 | else ## assume it's a file
121 | if relative_dir.empty?
122 | relative_path = node
123 | else
124 | relative_path = "#{relative_dir}/#{node}"
125 | end
126 |
127 | src_path = "#{root_dir}/#{relative_path}"
128 |
129 | ## note: for now assume always assume text files/utf8
130 | old_text = File.open( src_path, 'r:utf-8' ) { |f| f.read }
131 | new_text = merge_text( old_text, hash )
132 |
133 | if opts[:o]
134 | dest_root = opts[:o]
135 | dest_path = "#{dest_root}/#{relative_path}"
136 | ## make sure dest_path exists
137 | dest_dir = File.dirname( dest_path )
138 | FileUtils.mkdir_p( dest_dir ) unless Dir.exist?( dest_dir )
139 | else
140 | dest_root = root_dir
141 | dest_path = "#{dest_root}/#{relative_path}"
142 | end
143 |
144 | if old_text == new_text
145 | if opts[:o] ## for testing copy file 1:1
146 | puts " copy file 1:1 #{node} (#{relative_dir}) in (#{dest_root})"
147 | FileUtils.cp( src_path, dest_path, verbose: true )
148 | else
149 | puts " skip file #{node} (#{relative_dir})"
150 | end
151 | else
152 | puts " *** update file #{node} (#{relative_dir}) in (#{dest_root})"
153 | File.open( dest_path, 'w:utf-8' ) do |f|
154 | f.write( new_text )
155 | end
156 | end
157 | end
158 | end
159 | end
160 |
161 |
162 |
163 | def merge_path( path, hash )
164 | ## e.g. allow
165 | ## __filename__ or
166 | ## $filename$ for now
167 | ## note: allow underline too e.g $file_name$ etc.
168 | path.gsub( /(__|\$)([a-z_]+)\1/i ) do |_|
169 | key = $2.to_s
170 | value = hash[ key ]
171 | puts " [path] replacing #{key} w/ >#{value}< in (#{path})"
172 | value
173 | end
174 | end
175 |
176 | def merge_text( text, hash )
177 | ## e.g. allow
178 | ## $filename$ for now only in text
179 | ## note: must include leading and trailing word boundry (/B)
180 | ## e.g. hello$test$ will not match only "free-standing $test
181 | ## or in quote e.g. "$test$"
182 | ## e.g. no letters or digits allowed before or after $ to match
183 | ## note: allow underline too e.g. $test_klass$ etc.
184 |
185 | ## pp text
186 |
187 | text.gsub( /\B\$([a-z_]+)\$\B/i ) do |_|
188 | key = $1.to_s
189 | value = hash[ key ]
190 | puts " [text] replacing #{key} w/ >#{value}<"
191 | value
192 | end
193 | end
194 |
195 |
196 |
197 | def merge( root_dir, hash, opts={} )
198 | puts " merge #{root_dir}, #{hash.inspect}"
199 |
200 | merge_filenames( root_dir, hash, opts )
201 | merge_files( root_dir, hash, opts )
202 | end
203 |
204 | end # class Merger
205 |
206 | end # module Quik
207 |
208 |
--------------------------------------------------------------------------------
/lib/quik/package.rb:
--------------------------------------------------------------------------------
1 | # encoding: utf-8
2 |
3 | module Quik
4 |
5 | class Package
6 |
7 | def initialize( key ) ## e.g. quikstart/gems
8 | if key.index( '/' ).nil? ## if just 'gems' etc; assumbe quikstart "standard" github org
9 | @key = "quikstart/#{key}"
10 | else
11 | @key = key # use as is e.g. seattlerb/hoe etc.
12 | end
13 | end
14 |
15 |
16 | def remote_zip_url # remote zip url
17 | "https://github.com/#{@key}/archive/master.zip"
18 | end
19 |
20 | def local_zip_name
21 | ## note: change / to --I-- for "flat" structure (e.g. no dirs)
22 | @key.sub( '/', '--I--' ) # note: will NOT include/return .zip extension
23 | end
24 |
25 | def local_zip_dir
26 | "." ## use ./tmp or ./dl or ~/.quik/cache - why?? why not??
27 | end
28 |
29 | def local_zip_path # local zip path
30 | "#{local_zip_dir}/#{local_zip_name}.zip"
31 | end
32 |
33 |
34 |
35 | def download
36 | src = remote_zip_url
37 | dest_zip = local_zip_path
38 |
39 | ## make sure dest folder exists
40 | FileUtils.mkdir_p( local_zip_dir ) unless Dir.exist?( local_zip_dir )
41 | fetch_archive( src, dest_zip )
42 | end
43 |
44 | def unzip( unzip_dir )
45 | src = local_zip_path
46 | dest_unzip = unzip_dir ## local_unzip_dir
47 |
48 | ## check if folders exists? if not create folder in path
49 | FileUtils.mkdir_p( dest_unzip ) unless Dir.exist?( dest_unzip )
50 | unzip_archive( src, dest_unzip )
51 | end
52 |
53 |
54 | def merge( root_dir, hash )
55 | ## replace/merge variable in files
56 | Dir.chdir( "#{root_dir}" ) do
57 | files = Dir[ '**/*' ] ## get all files
58 | pp files
59 | end
60 | end
61 |
62 |
63 | private
64 | def fetch_archive( src, dest )
65 | ## step 1 - fetch archive
66 | worker = Fetcher::Worker.new
67 | worker.copy( src, dest )
68 | ### fix: add src.sha5
69 | ### inside folder
70 | ### lets us check if current HEAD version is in place across datafiles etc.
71 | ## - try HTTP HEAD ?? to check?
72 | end
73 |
74 | def unzip_archive( src, dest, opts={} )
75 | ### todo/fix: rename or remove root folder -- use opts { root: false or something??}
76 | # e.g
77 | # !/starter-gh-pages/_layouts/ becomes
78 | # !/_layouts/ etc.
79 |
80 | ##
81 | ## note:
82 | ## skip all files in "root" folder
83 | ## only unzip files in /template(s)/ folder
84 |
85 | Zip::File.open( src ) do |zipfile|
86 | zipfile.each do |file|
87 | if file.directory?
88 | puts " skip directory zip entry - #{file.name}"
89 | else
90 | ### fix: only cut-off if master or gh-pages ???
91 | ## check if others include root folder?
92 | name = file.name[ file.name.index('/')+1..-1] ## cut-off root/first path entry
93 |
94 | ## note: name must start w/ template/ or templates/
95 | ## otherwise gets skipped as "top level" docu
96 | if name =~ /^template(s)?\//
97 | name = name[ name.index('/')+1..-1] ## cut-off first path entry (e.g. template(s)/)
98 |
99 | path = File.join( dest, name)
100 | puts " unzip file zip entry - #{file.name} to #{path}"
101 | FileUtils.mkdir_p( File.dirname( path) )
102 | ## todo/fix: check - always overwrite if file exists - why? why not??
103 | zipfile.extract(file, path) unless File.exist?(path)
104 | else
105 | puts " skip top-level docu file entry - #{file.name}"
106 | end
107 | end
108 | end
109 | end
110 | end # method unzip_theme
111 |
112 |
113 | end # class Package
114 |
115 | end # module Quik
116 |
117 |
--------------------------------------------------------------------------------
/lib/quik/version.rb:
--------------------------------------------------------------------------------
1 | # encoding: utf-8
2 |
3 | module Quik
4 |
5 | MAJOR = 1 ## todo: namespace inside version or something - why? why not??
6 | MINOR = 0
7 | PATCH = 0
8 | VERSION = [MAJOR,MINOR,PATCH].join('.')
9 |
10 | def self.version
11 | VERSION
12 | end
13 |
14 | # version string for generator meta tag (includes ruby version)
15 | def self.banner
16 | "quik/#{VERSION} on Ruby #{RUBY_VERSION} (#{RUBY_RELEASE_DATE}) [#{RUBY_PLATFORM}] in (#{root})"
17 | end
18 |
19 | def self.root
20 | File.expand_path( File.dirname(File.dirname(File.dirname(__FILE__))) )
21 | end
22 |
23 | end # module Quik
24 |
25 |
--------------------------------------------------------------------------------
/lib/quik/wizard.rb:
--------------------------------------------------------------------------------
1 | # encoding: utf-8
2 |
3 |
4 | ## note:
5 | ## use global $QUIK_WIZARD_STDIN
6 | ## lets you redirect stdin for testing e.g $QUIK_WIZARD_STDIN = StringIO.new( 'test/n' )
7 | $QUIK_WIZARD_IN = $stdin
8 |
9 |
10 | module Quik
11 |
12 | module Wizard ## use a different name e.g. WizardHelpers, FormHelpers, InputHelper, etc - why, why not??
13 |
14 | def getstr ## use getstr to avoid conflict w/ gets (use better name? read_string, readline (already exists too), etc.?)
15 | ## note: gets will include trailing newline (user hits return to enter data)
16 | ## - use strip for now (remove leading and traling whitspaces) - might later just use chomp ? (jsut removes newlines)
17 | $QUIK_WIZARD_IN.gets.strip
18 | end
19 |
20 | def say( text )
21 | puts text
22 | end
23 |
24 |
25 | YES_REGEX = /y|yes|on|t|true/i ## support YAML true values - double check (YAML does NOT support t/f)
26 | NO_REGEX = /n|no|off|f|false/i
27 |
28 | def yes?( question ) ## defaults to yes - why, why not??
29 | ## todo: strip trailing question mark (?) if present (gets auto-included)
30 | print( "Q: #{question} (y/n)? [y]: " )
31 | str = getstr
32 | if str.empty? || str =~ YES_REGEX
33 | true
34 | elsif str =~ NO_REGEX
35 | false
36 | else ## warn: unknown value??
37 | true
38 | end
39 | end
40 |
41 | def no?( question ) ## defaults to yes - why, why not??
42 | ## todo: strip trailing question mark (?) if present (gets auto-included)
43 | print( "Q: #{question} (y/n)? [n]: " )
44 | str = getstr
45 | if str.empty? || str =~ NO_REGEX
46 | true
47 | elsif str =~ YES_REGEX
48 | false
49 | else ## warn: unknown value??
50 | true
51 | end
52 | end
53 |
54 |
55 | def ask( question, default=nil )
56 | ## todo: strip trailing question mark (?) if present (gets auto-included)
57 | if default
58 | print( "Q: #{question}? [#{default}]: " )
59 | else
60 | print( "Q: #{question}?: " )
61 | end
62 |
63 | str = getstr
64 | if default && str.empty? ## todo: best way to check for empty string?
65 | str = default
66 | end
67 | str
68 | end
69 |
70 |
71 | def select( title, options )
72 | puts( "Q: #{title}: " )
73 | options.each_with_index do |opt,i|
74 | puts " #{i+1} - #{opt}"
75 | end
76 | print( " Your choice (1-#{options.size})? [1]: " )
77 | str = getstr
78 | if str.empty? ## todo: best way to check for empty string?
79 | num = 0 ## default to first option for now
80 | else
81 | num = str.to_i ## note: defaults to 0 if cannot convert?
82 | num -= 1 if num > 0 ## note: "convert" from 1-based to 0-based for ary; if invalid entry; default to 0
83 | end
84 | options[ num ]
85 | end
86 |
87 | end # module Wizard
88 | end # module Quik
89 |
--------------------------------------------------------------------------------
/test/data/gem-starter-template/.gitignore:
--------------------------------------------------------------------------------
1 | *.gem
2 | *.rbc
3 | /.config
4 | /coverage/
5 | /InstalledFiles
6 | /pkg/
7 | /spec/reports/
8 | /test/tmp/
9 | /test/version_tmp/
10 | /tmp/
11 |
12 | ## Specific to RubyMotion:
13 | .dat*
14 | .repl_history
15 | build/
16 |
17 | ## Documentation cache and generated files:
18 | /.yardoc/
19 | /_yardoc/
20 | /doc/
21 | /rdoc/
22 |
23 | ## Environment normalisation:
24 | /.bundle/
25 | /vendor/bundle
26 | /lib/bundler/man/
27 |
28 | # for a library or gem, you might want to ignore these files since the code is
29 | # intended to run in multiple environments; otherwise, check them in:
30 | # Gemfile.lock
31 | # .ruby-version
32 | # .ruby-gemset
33 |
34 | # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
35 | .rvmrc
36 |
--------------------------------------------------------------------------------
/test/data/gem-starter-template/Manifest.txt:
--------------------------------------------------------------------------------
1 | ---
2 | # Gem Starter Template
3 | ---
4 | Manifest.txt
5 | HISTORY.md
6 | README.md
7 | Rakefile
8 | lib/{{filename}}.rb
9 | lib/{{filename}}/version.rb
10 |
--------------------------------------------------------------------------------
/test/data/gem-starter-template/README.md:
--------------------------------------------------------------------------------
1 | # gem-starter-template
2 |
3 | gem starter template
4 |
5 |
6 | ## Settings / Config
7 |
8 | ~~~
9 | {{ filename }} used for lib/{{ filename }}.rb
10 | {{ project }} used for gem name
11 | {{ klass }} used for class name (use class - why? why not?)
12 | ~~~
13 |
14 | filename encode with --filename-- for variables e.g. lib/--filename--.rb -- why? why not?
15 |
16 |
17 | use three jekyll conventions:
18 |
19 | - folders w/ underscore get ignored
20 | - files w/ front matter e.g. ---...--- get (pre)processed
21 | - files w/o front matter get copied 1:1
22 |
23 |
--------------------------------------------------------------------------------
/test/data/gem-starter-template/lib/$filename$.rb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/quikstart/quik/b041fd23d670499010bc07f29254d50272431df0/test/data/gem-starter-template/lib/$filename$.rb
--------------------------------------------------------------------------------
/test/data/gem-starter-template/lib/__filename__/$filename$.rb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/quikstart/quik/b041fd23d670499010bc07f29254d50272431df0/test/data/gem-starter-template/lib/__filename__/$filename$.rb
--------------------------------------------------------------------------------
/test/data/gem-starter-template/lib/__filename__/__filename__/test.rb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/quikstart/quik/b041fd23d670499010bc07f29254d50272431df0/test/data/gem-starter-template/lib/__filename__/__filename__/test.rb
--------------------------------------------------------------------------------
/test/data/gem-starter-template/lib/__filename__/version.rb:
--------------------------------------------------------------------------------
1 |
2 |
3 | module $klass$
4 | VERSION = $version$
5 | VERSION2 = "$version$"
6 | VERSION3 = '$version$'
7 | VERSION4 = $test
8 | VERSION5 = test$test$hello
9 | VERSION6 = test$test$
10 | VERSION7 = $$
11 | VERSION8 = $1$2
12 | end
13 |
--------------------------------------------------------------------------------
/test/helper.rb:
--------------------------------------------------------------------------------
1 | require 'minitest/autorun'
2 |
3 |
4 | class EchoIO
5 | def initialize( buf )
6 | @io = StringIO.new( buf )
7 | end
8 |
9 | def gets
10 | str = @io.gets
11 | puts "|>#{str.chomp}<|" ## remove newline (w/ chomp) in debug/echo output
12 | str
13 | end
14 | end
15 |
16 |
17 | ## our own code
18 | require 'quik'
19 |
--------------------------------------------------------------------------------
/test/test_colors.rb:
--------------------------------------------------------------------------------
1 | # encoding: utf-8
2 |
3 | ###
4 | # to run use
5 | # ruby -I ./lib -I ./test test/test_colors.rb
6 |
7 |
8 | require 'helper'
9 |
10 |
11 | class TestColors < MiniTest::Test
12 |
13 | def test_colors
14 |
15 | puts "this text is blue".blue
16 | puts "this text is red".red
17 | puts "this text is green".green + "this text is yellow".yellow
18 | puts "this text is magenta".magenta
19 | puts "this text is cyan".cyan
20 |
21 | puts "this text is blue".bold.blue
22 | puts "this text is red".bold.red
23 | puts "this text is green".bold.green + "this text is yellow".bold.yellow
24 |
25 | pp String.use_colors?
26 |
27 | assert true ## if we get here; everything is ok
28 | end
29 |
30 | def test_codes
31 | assert_equal "\e[31mred\e[0m", "red".red
32 | assert_equal "\e[34m\e[31mblue\e[0m\e[0m", "blue".red.blue
33 | ## better to generate "\e[34m;[31m" -- why? why not? e.g. one starting escpape and codes delimited by ; ??
34 | assert_equal "\e[1mway bold\e[0m", "way bold".bold
35 | assert_equal "\e[36m\e[1mcyan bold\e[0m\e[0m", "cyan bold".bold.cyan
36 | end
37 |
38 | end # class TestColors
39 |
40 |
--------------------------------------------------------------------------------
/test/test_config.rb:
--------------------------------------------------------------------------------
1 | # encoding: utf-8
2 |
3 | ###
4 | # to run use
5 | # ruby -I ./lib -I ./test test/test_config.rb
6 |
7 |
8 | require 'helper'
9 |
10 |
11 | class TestConfig < MiniTest::Test
12 |
13 | def test_config
14 |
15 | c = Quik::OpenConfig.new
16 | c.title = 'title'
17 | c.author.name = 'name'
18 |
19 | c.mrhyde.last_updated = Time.now
20 | c.mrhyde.title = 'title'
21 | c.mrhyde.name = 'name'
22 | c.mrhyde.theme = 'theme'
23 | c.mrhyde.meta.info = 'test nested nested value'
24 |
25 | ## test self reference in value assignment e.g.
26 | c.test.title = c.title
27 | c.test.name = c.author.name
28 |
29 | pp c.to_h
30 |
31 | assert true ## if we get here; everything is ok
32 | end
33 |
34 |
35 | end # class TestConfig
36 |
--------------------------------------------------------------------------------
/test/test_merger.rb:
--------------------------------------------------------------------------------
1 | # encoding: utf-8
2 |
3 | ###
4 | # to run use
5 | # ruby -I ./lib -I ./test test/test_merger.rb
6 |
7 |
8 | require 'helper'
9 |
10 |
11 | class TestMerger < MiniTest::Test
12 |
13 | def test_gem_starter_template
14 |
15 | config = { 'filename' => 'hola',
16 | 'version' => '0.0.1',
17 | 'klass' => 'Hola',
18 | }
19 |
20 | m = Quik::Merger.new
21 |
22 | ## m.merge_filenames( "#{Quik.root}/test/data/gem-starter-template", config, test: true )
23 | ## m.merge_filenames( "./o/gem", config )
24 |
25 | ## note: set output path (for testing; not in place)
26 | ## m.merge_files( "#{Quik.root}/test/data/gem-starter-template", config, test: true, o: "./o/#{Time.now.to_i}" )
27 |
28 | ## note: set output path (for testing; not in place)
29 | m.merge( "#{Quik.root}/test/data/gem-starter-template", config, test: true, o: "./o/#{Time.now.to_i}" )
30 | ## m.merge( "./o/gem", config )
31 |
32 | assert true ## if we get here; everything is ok
33 | end
34 |
35 | end # class TestMerger
36 |
--------------------------------------------------------------------------------
/test/test_package.rb:
--------------------------------------------------------------------------------
1 | # encoding: utf-8
2 |
3 | ###
4 | # to run use
5 | # ruby -I ./lib -I ./test test/test_package.rb
6 |
7 |
8 | require 'helper'
9 |
10 |
11 | class TestPackage < MiniTest::Test
12 |
13 | def test_gem_starter_template
14 |
15 | pak = Quik::Package.new( 'gem-starter-template' )
16 | pak.download
17 | pak.unzip( './o/gem' )
18 |
19 | assert true ## if we get here; everything is ok
20 | end
21 |
22 | end # class TestPackage
23 |
--------------------------------------------------------------------------------
/test/test_wizard.rb:
--------------------------------------------------------------------------------
1 | # encoding: utf-8
2 |
3 | ###
4 | # to run use
5 | # ruby -I ./lib -I ./test test/test_wizard.rb
6 |
7 |
8 | require 'helper'
9 |
10 |
11 | class TestWizard < MiniTest::Test
12 |
13 | include Quik::Wizard ## lets you use ask etc.
14 |
15 | def test_ask
16 |
17 | $QUIK_WIZARD_IN = EchoIO.new( <