grundstein 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 4417958b338d6b06e0c0e6fa6a60785705341f27
4
+ data.tar.gz: 9ac05330efb49ec5e851fba82e59dd546b940abb
5
+ SHA512:
6
+ metadata.gz: 24f6b4cc484a02fc65b9cbbcd7782617cb14d1043c2ad8eb3fec07e808f916c5ee329a57a9a9aa0eef9b86d5fdeedb95427b5cb4979094cc5267495d1f0163f4
7
+ data.tar.gz: ccd5d10005905732a05324ac6ce8b6b608fe83388ebc062d37abf1c47adcfed5b50e22aa747c5fec6b7b47ce5bef5f662392f2af3752d6888613086a224c028e
data/.gitignore ADDED
@@ -0,0 +1,14 @@
1
+ /.vagrant
2
+ /.bundle
3
+ /.yardoc
4
+ /Gemfile.lock
5
+ /_yardoc/
6
+ /coverage/
7
+ /doc/
8
+ /pkg/
9
+ /spec/reports/
10
+ /tmp/
11
+ *.bundle
12
+ mkmf.log
13
+ doc/api
14
+ grundstein-*.gem
data/.rubocop.yml ADDED
@@ -0,0 +1,46 @@
1
+ # See a complete description of the checks (named "cops") here: https://github.com/bbatsov/rubocop/blob/master/config/default.yml
2
+ AllCops:
3
+ DisplayCopNames: true
4
+ DisplayStyleGuide: true
5
+ Exclude:
6
+ - 'generators/**/*'
7
+
8
+ Metrics/LineLength:
9
+ Enabled: false
10
+
11
+ Style/StringLiterals:
12
+ Enabled: false
13
+
14
+ Style/SpaceAroundEqualsInParameterDefault:
15
+ Enabled: false
16
+
17
+ Style/RedundantReturn:
18
+ Enabled: false
19
+
20
+ Style/BracesAroundHashParameters:
21
+ Enabled: false
22
+
23
+ Style/RedundantSelf:
24
+ Enabled: false
25
+
26
+ Style/SignalException:
27
+ Enabled: false
28
+
29
+ Style/EachWithObject:
30
+ Enabled: false
31
+
32
+ Style/RegexpLiteral:
33
+ Enabled: false
34
+
35
+ Metrics/AbcSize:
36
+ Enabled: false
37
+
38
+ Style/TrailingUnderscoreVariable:
39
+ Enabled: false
40
+
41
+ Style/NegatedWhile:
42
+ Enabled: false
43
+
44
+ Style/WordArray:
45
+ Enabled: false
46
+
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in grundstein.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2016 TODO: Write your name
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.
@@ -0,0 +1,179 @@
1
+ # Development
2
+
3
+ ## How generators work
4
+
5
+ Each generator has a directory in the `generators` folder. The generator's name is determined by the directory's name.
6
+ Directories starting with `.` or `#` are ignored. All others must have a `_generator.rb` file.
7
+ In there...
8
+
9
+ - ...must be a `def run` method.
10
+ - ...must be a `def spec` method which returns a hash with at least `{desc: '....'}`.
11
+ - ...may be a `def caveats` method which returns a string to be displayed after the generator has run.
12
+ - ...can be arbitrary other methods or declarations.
13
+
14
+ Example:
15
+
16
+ ```ruby
17
+ # file: generators/fancy/_generator.rb
18
+ def run
19
+ doc_existed = directory('doc') # ensure doc folder is there
20
+ template('newfile') # finds `newfile` in the generator folder and copies it to the project
21
+ template('doc/.keep') unless doc_existed # and that there is a .keep if needed
22
+ template('probably_existing_file', assume_append: true)
23
+
24
+ info("EXIST", "YEEY") if File.exists?(File.join(@project_path, 'yeey')) # use Ruby methods
25
+ answer = ask("You? ") # use HighLine methods
26
+ end
27
+
28
+ def caveats
29
+ res = <<CAVEATS
30
+ Please run `bundle install` in your project.
31
+ CAVEATS
32
+ return res
33
+ end
34
+
35
+ def spec
36
+ return { desc: 'does some fancy stuff' }
37
+ end
38
+ ```
39
+
40
+ ### Generator context
41
+
42
+ The generator script is guaranteed to include:
43
+
44
+ - Ruby's standard methods (standard library)
45
+ - [HighLine](https://github.com/JEG2/highline) gem (`require 'highline/import'` was run)
46
+ - [Thor](https://github.com/erikhuda/thor) gem loaded.
47
+ - The following helpers:
48
+
49
+ ```ruby
50
+ @generator_path # root path of the generator e.g. `.../generators/sdoc/`
51
+ @working_path # path of the current working directory (may be != to @project_path)
52
+ @project_path # root path of the project (with the .git in it)
53
+
54
+ # Ensures that the project has the directory given.
55
+ # This method returns if the directory already existed.
56
+ def directory(relative_project_path)
57
+ end
58
+
59
+ # Processes the template found at `relative_project_path` and copies it to the project.
60
+ # Relative_template_path determines the path of the template relative to the generator's root.
61
+ # If `destination_path` is nil, the destination path will be determined by appending `relative_template_path` to `@project_path`.
62
+ # If `destination_path` is given, it may be an absolute path or a relative path (to `@project_path`).
63
+ # In case the destination file already exists, the user will be queried what to do (append, overwrite, skip or print).
64
+ # If `assume_append` is true, the user is not queried and the template will be appended.
65
+ def template(relative_template_path, destination_path: nil, assume_append: false) # rubocop:disable Metrics/CyclomaticComplexity,
66
+ end
67
+
68
+ # Prints out a message.
69
+ def info(action, message)
70
+ end
71
+
72
+ # Sets a variable with the given `name` and `value` to be available for all future templates.
73
+ def template_context(name, value)
74
+ end
75
+ ```
76
+
77
+ Please see `lib/grundstein/generator/environment.rb` for more.
78
+
79
+ ## Templates
80
+
81
+ Grundstein uses [mustache](https://mustache.github.io/) as template engine.
82
+ Please see an extensive description [here](https://mustache.github.io/mustache.5.html).
83
+
84
+ There are a few variables guaranteed to be present:
85
+
86
+ - `existed?` and `new_file?` as boolean values determining if a new file has just been created.
87
+ - `project_path`, `generator_path` and `working_path`. Description, please see above
88
+ - You can also use `template_context(name, value)` in your generator's run method to add other variables.
89
+ (use a question mark at the end of the name for boolean values).
90
+
91
+ Example:
92
+
93
+ ```ruby
94
+ def run
95
+ template_context("rails?", agree("Do you intend to use Rails? ")) # note the two spaces at the end
96
+ template_context("name", ask("You name? "))
97
+ end
98
+ # ...
99
+ ```
100
+
101
+ ```ruby
102
+ # Author: {{name}}
103
+
104
+ {{#new_file?}}
105
+ source 'https://rubygems.org'
106
+ {{/new_file?}}
107
+
108
+ gem 'sdoc'
109
+ {{#rails?}}
110
+ gem 'rails'
111
+ {{/rails?}}
112
+ ```
113
+
114
+ ## Testing
115
+
116
+ ```bash
117
+ # you should test your generators in a new project
118
+ cd /tmp/
119
+ git init myproject
120
+ cd myproject/
121
+ RUBYLIB='/vagrant/lib:$RUBYLIB' bin/grundstein add rubocop
122
+ ```
123
+
124
+ ## API documentation
125
+
126
+ We are using the [sdoc](https://github.com/voloko/sdoc) generator which is based on [rdoc](https://github.com/rdoc/rdoc).
127
+ You may generate the documentation via `rake rdoc` and then find it in `doc/api`.
128
+
129
+ For documenting methods and classes we use [markdown](https://daringfireball.net/projects/markdown/). Just add a comment above a class, method or attribute.
130
+ If you are not familiar with markdown, please see this [cheat sheet](http://nestacms.com/docs/creating-content/markdown-cheat-sheet).
131
+ Here a quick example:
132
+
133
+ ```ruby
134
+ # This is a mountain in the landscape.
135
+ # It has seasons and simulates weather.
136
+ class Mountain
137
+ # The hight of the mountain [meter].
138
+ attr_accessor :height
139
+
140
+ # Constructor.
141
+ # The `height` is measured in meters.
142
+ # If snow is true, we can build ski lifts.
143
+ def initialize(height, snow=false)
144
+ end
145
+
146
+ # Changes the weather on the mountain and sends a letter to the weather god.
147
+ # `season` can be either `:summer`, `:spring`, `:fall` or `:winter`.
148
+ # The letter to the weather god will include the desired `min_temp` or `max_temp`.
149
+ #
150
+ # m = Mountain.new(1000) # this is some example code
151
+ # m.change_season(:summer)
152
+ def change_season(season, min_temp: -10, max_temp: 30)
153
+ end
154
+
155
+ # Whenever rain occures, the block will be executed.
156
+ # **WARNING**: Will be called from another thread.
157
+ #
158
+ # _yields_ temperature, rain_amount
159
+ def rain_handler(&block)
160
+ yield 1, 2
161
+ end
162
+ end
163
+ ```
164
+
165
+ ## Code Style
166
+
167
+ We are using this [Rubocop](https://github.com/bbatsov/rubocop) to enforce coding style.
168
+
169
+ ```bash
170
+ rake rubocop # Run before each commit/push
171
+ rake rubocop:auto_correct # auto-corrects issues where it can
172
+
173
+ rubocop --only Style/TrailingBlankLines --auto-correct # correct only a single offense type
174
+ ```
175
+
176
+ If you are a hundred percent sure you are doing the right thing, you can disable checking. Either
177
+
178
+ - disable them for a particular line/method/class with: `# rubocop:disable Metrics/LineLength, Style/StringLiterals`
179
+ - or disable rubocop for the full file: `# rubocop:disable all`
data/README.md ADDED
@@ -0,0 +1,47 @@
1
+ # Grundstein
2
+
3
+ [![GitHub license](https://img.shields.io/github/license/motine/grunstein.svg?style=flat-square)]()
4
+ [![Gem](https://img.shields.io/gem/v/grundstein.svg?style=flat-square)]()
5
+
6
+ I was annoyed to do the same work over and over.
7
+ Often, I found myself to create the same `Vagrantfile`, `.gitignore` or `Gemfile` for each new project.
8
+ Grundstein automates this step in a very simple way: We have (smart) templates which are copied to your repo.
9
+ These templates are (in Rails fashion) called generators.
10
+
11
+ **Examples**
12
+
13
+ - You can call something like `grundstein vagrant` and you get a Vagrantfile and a little note in your README.
14
+ - When you call `grundstein rubocop` you get a .rubocop file with some defaults and a few lines in your Rakefile.
15
+
16
+ **Principles**
17
+
18
+ - Don't be smart detecting stuff, ask! (some stuff might not have been created by the developer yet)
19
+ - Generate rather more useful stuff than too few: A user can delete quicker than adding new stuff.
20
+ - Show the user what is happening and what needs to be done next.
21
+ - If in doubt, append.
22
+
23
+ **Pro Tip**: Commit before running generators. Then review the changes via `git diff` or a [visual git tool](https://desktop.github.com/).
24
+
25
+ ## Installation
26
+
27
+ Please make sure you have `git >= 1.7` installed. Then run:
28
+
29
+ ```bash
30
+ gem install grundstein
31
+ ```
32
+
33
+ ## Usage
34
+
35
+ The project's root folder is determined based on the current working directory.
36
+ If there is no `.git` directory in the current directory, the script will try to move up until it finds a `.git`.
37
+ Hence, **the project must be a git repository**.
38
+
39
+ ```bash
40
+ grundstein list # see all generators
41
+ cd myproject
42
+ grundstein add rubocop
43
+ ```
44
+
45
+ ## Contribute & Development
46
+
47
+ Please see `README.Development.md`.
data/Rakefile ADDED
@@ -0,0 +1,20 @@
1
+ require "bundler/gem_tasks"
2
+ require "rdoc/task"
3
+ require 'sdoc'
4
+
5
+ begin
6
+ require 'rubocop/rake_task'
7
+ RuboCop::RakeTask.new
8
+ rescue LoadError => _
9
+ STDERR.puts "Rubocop rake tasks not added, because gem not available." # you can remove this note if you want
10
+ end
11
+
12
+ Rake::RDocTask.new do |rdoc| # options are documented here: http://ruby-doc.org/stdlib-2.0.0/libdoc/rdoc/rdoc/RDoc/Task.html
13
+ rdoc.rdoc_dir = 'doc/api'
14
+ rdoc.generator = 'sdoc'
15
+ rdoc.markup = 'markdown'
16
+ rdoc.main = 'README.md'
17
+ rdoc.rdoc_files.include("README.md", "bin/*", "lib/**/*.rb")
18
+ rdoc.template = 'rails' # change default from 'sdoc' to 'rails' style
19
+ # rdoc.options.push('--quiet') # see more options here: http://ruby-doc.org/stdlib-2.0.0/libdoc/rdoc/rdoc/RDoc/Options.html
20
+ end
data/Vagrantfile ADDED
@@ -0,0 +1,28 @@
1
+ # -*- mode: ruby -*-
2
+
3
+ NAME = 'grundstein'.freeze
4
+
5
+ DEPS = <<SCRIPT.freeze
6
+ dnf -y install git vim-enhanced
7
+ dnf -y install ruby ruby-devel rubygem-bundler
8
+ SCRIPT
9
+
10
+ BUNDLE = <<SCRIPT.freeze
11
+ pushd /vagrant
12
+ bundle install
13
+ SCRIPT
14
+
15
+ Vagrant.configure("2") do |config|
16
+ config.vm.hostname = NAME
17
+
18
+ config.vm.box = "fedora/23-cloud-base" # see below for virtual box
19
+ config.vm.provision :shell, inline: DEPS
20
+ config.vm.provision :shell, inline: BUNDLE, privileged: false
21
+
22
+ config.vm.provider "virtualbox" do |vb, override|
23
+ override.vm.synced_folder ".", "/vagrant", type: "virtualbox"
24
+ vb.name = NAME
25
+ vb.memory = 2048
26
+ vb.cpus = 2
27
+ end
28
+ end
data/bin/grundstein ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+ # -*- mode: ruby -*-
3
+
4
+ require 'grundstein'
5
+ Grundstein::Runner.start
File without changes
@@ -0,0 +1,52 @@
1
+ # See a complete description of the checks (named "cops") here: https://github.com/bbatsov/rubocop/blob/master/config/default.yml
2
+ AllCops:
3
+ DisplayCopNames: true
4
+ DisplayStyleGuide: true
5
+
6
+ {{#rails?}}
7
+ Rails:
8
+ Enabled: true
9
+
10
+ # Style/Documentation:
11
+ # Enabled: false
12
+ {{/rails?}}
13
+
14
+ # Metrics/LineLength:
15
+ # Enabled: false
16
+ #
17
+ # Style/StringLiterals:
18
+ # Enabled: false
19
+ #
20
+ # Style/SpaceAroundEqualsInParameterDefault:
21
+ # Enabled: false
22
+ #
23
+ # Style/RedundantReturn:
24
+ # Enabled: false
25
+ #
26
+ # Style/BracesAroundHashParameters:
27
+ # Enabled: false
28
+ #
29
+ # Style/RedundantSelf:
30
+ # Enabled: false
31
+ #
32
+ # Style/SignalException:
33
+ # Enabled: false
34
+ #
35
+ # Style/EachWithObject:
36
+ # Enabled: false
37
+ #
38
+ # Style/RegexpLiteral:
39
+ # Enabled: false
40
+ #
41
+ # Metrics/AbcSize:
42
+ # Enabled: false
43
+ #
44
+ # Style/TrailingUnderscoreVariable:
45
+ # Enabled: false
46
+ #
47
+ # Style/NegatedWhile:
48
+ # Enabled: false
49
+ #
50
+ # Style/WordArray:
51
+ # Enabled: false
52
+
@@ -0,0 +1,7 @@
1
+ {{#new_file?}}
2
+ source 'https://rubygems.org'
3
+ {{/new_file?}}
4
+
5
+ group :development do
6
+ gem 'rubocop', require: false
7
+ end
@@ -0,0 +1,15 @@
1
+ ## Code Style
2
+
3
+ We are using this [Rubocop](https://github.com/bbatsov/rubocop) to enforce coding style.
4
+
5
+ ```bash
6
+ rake rubocop # Run before each commit/push
7
+ rake rubocop:auto_correct # auto-corrects issues where it can
8
+
9
+ rubocop --only Style/TrailingBlankLines --auto-correct # correct only a single offense type
10
+ ```
11
+
12
+ If you are a hundred percent sure you are doing the right thing, you can disable checking. Either
13
+
14
+ - disable them for a particular line/method/class with: `# rubocop:disable Metrics/LineLength, Style/StringLiterals`
15
+ - or disable rubocop for the full file: `# rubocop:disable all`
@@ -0,0 +1,6 @@
1
+ begin
2
+ require 'rubocop/rake_task'
3
+ RuboCop::RakeTask.new
4
+ rescue LoadError => _
5
+ STDERR.puts "Rubocop rake tasks not added, because gem not available." # you can remove this note if you want
6
+ end
@@ -0,0 +1,27 @@
1
+ def run
2
+ template_context("rails?", agree("Do you intend to use Rails? "))
3
+ template('.rubocop.yml')
4
+ template('Gemfile', assume_append: true)
5
+ template('Rakefile', assume_append: true)
6
+ template('README.Development.md', assume_append: true)
7
+ end
8
+
9
+ def caveats
10
+ res = <<CAVEATS
11
+ Please run `bundle install` in your project.
12
+
13
+ You can use `rubocop --auto-gen-config` to automatically create `.rubocop_todo.yml`.
14
+ In your `.rubocop.yml` you can then add `inherit_from: .rubocop_todo.yml` to include it.
15
+ More info: https://github.com/bbatsov/rubocop#automatically-generated-configuration
16
+
17
+
18
+ If you are using a CI, you may want to include something like `bundle exec rubocop`.
19
+ CAVEATS
20
+ return res
21
+ end
22
+
23
+ def spec
24
+ return {
25
+ desc: 'rubocop style checker'
26
+ }
27
+ end
@@ -0,0 +1 @@
1
+ doc/api
@@ -0,0 +1,5 @@
1
+ {{#new_file?}}
2
+ source 'https://rubygems.org'
3
+ {{/new_file?}}
4
+
5
+ gem 'sdoc', '~> 0.4.1'
@@ -0,0 +1,40 @@
1
+ ## API documentation
2
+
3
+ We are using the [sdoc](https://github.com/voloko/sdoc) generator which is based on [rdoc](https://github.com/rdoc/rdoc).
4
+ You may generate the documentation via `rake rdoc` and then find it in `doc/api`.
5
+
6
+ For documenting methods and classes we use [markdown](https://daringfireball.net/projects/markdown/). Just add a comment above a class, method or attribute.
7
+ If you are not familiar with markdown, please see this [cheat sheet](http://nestacms.com/docs/creating-content/markdown-cheat-sheet).
8
+ Here a quick example:
9
+
10
+ ```ruby
11
+ # This is a mountain in the landscape.
12
+ # It has seasons and simulates weather.
13
+ class Mountain
14
+ # The hight of the mountain [meter].
15
+ attr_accessor :height
16
+
17
+ # Constructor.
18
+ # The `height` is measured in meters.
19
+ # If snow is true, we can build ski lifts.
20
+ def initialize(height, snow=false)
21
+ end
22
+
23
+ # Changes the weather on the mountain and sends a letter to the weather god.
24
+ # `season` can be either `:summer`, `:spring`, `:fall` or `:winter`.
25
+ # The letter to the weather god will include the desired `min_temp` or `max_temp`.
26
+ #
27
+ # m = Mountain.new(1000) # this is some example code
28
+ # m.change_season(:summer)
29
+ def change_season(season, min_temp: -10, max_temp: 30)
30
+ end
31
+
32
+ # Whenever rain occures, the block will be executed.
33
+ # **WARNING**: Will be called from another thread.
34
+ #
35
+ # _yields_ temperature, rain_amount
36
+ def rain_handler(&block)
37
+ yield 1, 2
38
+ end
39
+ end
40
+ ```
@@ -0,0 +1,3 @@
1
+ {{#new_file?}}
2
+ Run `grundstein readme` to create a `README.md`.
3
+ {{/new_file?}}
@@ -0,0 +1,12 @@
1
+ require "rdoc/task"
2
+ require 'sdoc'
3
+
4
+ Rake::RDocTask.new do |rdoc| # options are documented here: http://ruby-doc.org/stdlib-2.0.0/libdoc/rdoc/rdoc/RDoc/Task.html
5
+ rdoc.rdoc_dir = 'doc/api'
6
+ rdoc.generator = 'sdoc'
7
+ rdoc.markup = 'markdown'
8
+ rdoc.main = 'README.md'
9
+ rdoc.rdoc_files.include("README.md", "bin/*", "lib/**/*.rb")
10
+ rdoc.template = 'rails' # change default from 'sdoc' to 'rails' style
11
+ # rdoc.options.push('--quiet') # see more options here: http://ruby-doc.org/stdlib-2.0.0/libdoc/rdoc/rdoc/RDoc/Options.html
12
+ end
@@ -0,0 +1,25 @@
1
+ def run
2
+ doc_existed = directory('doc') # ensure doc folder is there
3
+ template('doc/.keep') unless doc_existed # and that there is a .keep if needed
4
+ template('.gitignore', assume_append: true)
5
+ template('Gemfile', assume_append: true)
6
+ template('Rakefile', assume_append: true)
7
+ template('README.Development.md', assume_append: true)
8
+ template('README.md', assume_append: true)
9
+ end
10
+
11
+ def caveats
12
+ res = <<CAVEATS
13
+ Please run `bundle install` in your project.
14
+
15
+ Rdoc is very free-form.
16
+ If you prefer a stricter way for documenting you code, please take a look at yardoc.org.
17
+ Especially in larger projects, a stricter style may be better suited.
18
+
19
+ CAVEATS
20
+ return res
21
+ end
22
+
23
+ def spec
24
+ return { desc: 'rdoc rake task using sdoc template' }
25
+ end
File without changes
@@ -0,0 +1,29 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'grundstein/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'grundstein'
8
+ spec.version = Grundstein::VERSION
9
+ spec.authors = ['Tom Rothe']
10
+ spec.email = ['info@tomrothe.de']
11
+ spec.summary = 'Automates adding common files to your project.'
12
+ spec.description = 'Grundstein automates adding common files by we adding (smart) templates. E.g. add a Vagrantfile, a .gitignore, or a sinatra app.'
13
+ spec.homepage = 'https://github.com/motine/grundstein'
14
+ spec.license = 'MIT'
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_runtime_dependency 'thor', '~> 0.19.1'
22
+ spec.add_runtime_dependency 'highline', '~> 1.7'
23
+ spec.add_runtime_dependency 'mustache', '~> 1.0'
24
+ spec.add_runtime_dependency 'git', '~> 1.3'
25
+ spec.add_development_dependency 'bundler', '~> 1.7'
26
+ spec.add_development_dependency 'rake', '~> 10.0'
27
+ spec.add_development_dependency 'rubocop', '~> 0.37.2'
28
+ spec.add_development_dependency 'sdoc', '~> 0.4.1'
29
+ end
data/lib/grundstein.rb ADDED
@@ -0,0 +1,11 @@
1
+ require_relative 'grundstein/version'
2
+
3
+ module Grundstein # rubocop:disable Style/Documentation
4
+ def self.lib_path
5
+ File.expand_path('..', __FILE__)
6
+ end
7
+ end
8
+
9
+ require_relative 'grundstein/refinements'
10
+ require_relative 'grundstein/generator'
11
+ require_relative 'grundstein/runner'
@@ -0,0 +1,9 @@
1
+ require_relative 'generator/errors'
2
+ require_relative 'generator/repository'
3
+ require_relative 'generator/environment'
4
+ require_relative 'generator/loader'
5
+
6
+ module Grundstein
7
+ module Generator # rubocop:disable Style/Documentation
8
+ end
9
+ end
@@ -0,0 +1,104 @@
1
+ require 'mustache'
2
+ require 'fileutils'
3
+
4
+ module Grundstein
5
+ module Generator
6
+ # This class is used to create the envrionment for the generator file.
7
+ # This class is instanciated by the Generator::Loader.
8
+ # Then `extend_from_file` is called. This will then load all methods available in that file into this object.
9
+ # From there on the Loader can interact with the generator (e.g. call `run`)
10
+ class Environment
11
+ using Grundstein::Refinements::ColoredStrings
12
+
13
+ def initialize
14
+ @template_vars = {}
15
+ end
16
+
17
+ # Takes a kwargs and sets instance variables in the environment and makes them available as template variables.
18
+ # Called by Generator::Loader.
19
+ def set_context(**vars) # rubocop:disable Style/AccessorMethodName
20
+ vars.each do |var, value|
21
+ self.instance_variable_set("@#{var}".to_sym, value)
22
+ @template_vars[var] = value
23
+ end
24
+ end
25
+
26
+ # Loads the script from the given path and adds the methods to this class.
27
+ # Called by Generator::Loader.
28
+ def extend_from_file(path)
29
+ self.instance_eval(File.read(path))
30
+ end
31
+
32
+ # This may be overridden by the `_generator.rb`.
33
+ def caveats
34
+ return ''
35
+ end
36
+
37
+ # Prints out a message.
38
+ def info(action, message)
39
+ puts " #{action.ljust(10).c_info} #{message}"
40
+ end
41
+
42
+ # Sets a variable with the given `name` and `value` to be available for all future templates.
43
+ def template_context(name, value)
44
+ @template_vars[name] = value
45
+ end
46
+
47
+ # Ensures that the project has the directory given.
48
+ # This method returns if the directory already existed.
49
+ def directory(relative_project_path)
50
+ path = File.expand_path(relative_project_path, @project_path)
51
+ exists = Dir.exist?(path)
52
+ if exists
53
+ info("EXIST", path)
54
+ else
55
+ info("CREATE", path)
56
+ FileUtils.mkdir_p(path)
57
+ end
58
+ return exists
59
+ end
60
+
61
+ # Convenience method for the generator
62
+ # Processes the template found at `relative_project_path` and copies it to the project.
63
+ # Relative_template_path determines the path of the template relative to the generator's root
64
+ # If `destination_path` is nil, the destination path will be determined by appending `relative_template_path` to `@project_path`.
65
+ # If `destination_path` is given, it may be an absolute path or a relative path (to `@project_path`).
66
+ # In case the destination file already exists, the user will be queried what to do (append, overwrite, skip or print).
67
+ # If `assume_append` is true, the user is not queried and the template will be appended.
68
+ def template(relative_template_path, destination_path: nil, assume_append: false) # rubocop:disable Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
69
+ template_path = File.join(@generator_path, relative_template_path)
70
+ raise GeneratorRunError, "Could not find template '#{relative_template_path}'." unless File.exist?(template_path)
71
+
72
+ destination_path = File.expand_path(relative_template_path, @project_path) if destination_path.nil?
73
+ exists = File.exist?(destination_path)
74
+
75
+ rendered_template = Mustache.render(File.read(template_path), @template_vars.merge(new_file?: !exists))
76
+
77
+ append = true
78
+ if exists && !assume_append
79
+ choose do |menu|
80
+ menu.prompt = "#{destination_path.c_warning} already exists. "
81
+ menu.layout = :one_line
82
+ menu.choice(:append) {}
83
+ menu.choice(:overwrite) { append = false }
84
+ menu.choice(:skip) do
85
+ info("SKIP", destination_path)
86
+ return
87
+ end
88
+ menu.choice(:print) do
89
+ puts ">>>" * 20 + destination_path
90
+ puts rendered_template
91
+ puts "<<<" * 20 + destination_path
92
+ return
93
+ end
94
+ end
95
+ puts
96
+ end
97
+ File.open(destination_path, append ? 'a' : 'w') do |f|
98
+ f.puts(rendered_template)
99
+ end
100
+ info((exists && append) ? "APPEND" : "CREATE", destination_path)
101
+ end
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,12 @@
1
+ module Grundstein
2
+ module Generator
3
+ class GeneratorError < StandardError
4
+ end
5
+ class GeneratorNotFoundError < GeneratorError
6
+ end
7
+ class GeneratorMalformedError < GeneratorError
8
+ end
9
+ class GeneratorRunError < GeneratorError
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,89 @@
1
+ module Grundstein
2
+ module Generator
3
+ # This class encapsulates the logic to load a generator.
4
+ # It sets up a Generator::Envrionment and interacts with it.
5
+ # It is also responsible to determine paths.
6
+ class Loader
7
+ using Grundstein::Refinements::ColoredStrings
8
+
9
+ SCRIPT_NAME = '_generator.rb'.freeze
10
+ DIR_EXPECTED_IN_PROJECT_ROOT = '.git'.freeze
11
+
12
+ # Loads the generator.
13
+ def initialize(generator_name)
14
+ @generator_name = generator_name
15
+ @env = load_environment
16
+ @env.set_context(generator_path: generator_path, working_path: working_path, project_path: project_root_path)
17
+ end
18
+
19
+ # Executes the generator's run method.
20
+ def run # rubocop:disable Metrics/MethodLength
21
+ raise GeneratorMalformedError, "Generator script '#{@generator_name}' does not have a 'run' method." unless @env.respond_to?(:run)
22
+ begin
23
+ puts "Running #{@generator_name.c_gen}"
24
+ puts "Working path: #{working_path}"
25
+ puts "Project path: #{project_root_path}"
26
+ puts
27
+ @env.run
28
+ puts
29
+ puts @env.caveats.c_warning
30
+ rescue => e
31
+ raise GeneratorRunError, "[#{@generator_name}] #{e.message}"
32
+ end
33
+ end
34
+
35
+ def name
36
+ return @generator_name
37
+ end
38
+
39
+ def info
40
+ raise GeneratorMalformedError, "Generator script '#{@generator_name}' does not have an 'info' method." unless @env.respond_to?(:spec)
41
+ spec = @env.spec
42
+ raise GeneratorMalformedError, "Generator script '#{@generator_name}' does include :desc in the 'info' result." unless spec.is_a?(Hash) && spec[:desc].is_a?(String)
43
+ return spec
44
+ end
45
+
46
+ # short hand for info[:desc]
47
+ def desc
48
+ return info[:desc]
49
+ end
50
+
51
+ protected
52
+
53
+ # Creates a Generator::Environment for the generator_name given in the constructor.
54
+ def load_environment
55
+ env = Grundstein::Generator::Environment.new
56
+ path = self.generator_script_path
57
+ raise GeneratorNotFoundError, "Generator named '#{@generator_name}' could not be found." unless Dir.exist?(File.dirname(path))
58
+ raise GeneratorNotFoundError, "Generator named '#{@generator_name}' has no #{SCRIPT_NAME} script." unless File.exist?(path)
59
+ env.extend_from_file(path)
60
+ return env
61
+ end
62
+
63
+ def working_path
64
+ return Dir.pwd
65
+ end
66
+
67
+ def generator_path
68
+ return Generator::Repository.instance.generator_path(@generator_name)
69
+ end
70
+
71
+ def generator_script_path
72
+ return File.join(self.generator_path, SCRIPT_NAME)
73
+ end
74
+
75
+ def project_root_path
76
+ return @project_root_path unless @project_root_path.nil?
77
+ dir = Dir.pwd
78
+ loop do
79
+ raise GeneratorRunError, "Could not determine project path (no .git found here or above)." if dir == '/'
80
+ if Dir.exist?(File.join(dir, DIR_EXPECTED_IN_PROJECT_ROOT))
81
+ @project_root_path = dir
82
+ return dir
83
+ end
84
+ dir = File.dirname(dir)
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,82 @@
1
+ require 'git'
2
+
3
+ module Grundstein
4
+ module Generator
5
+ # Manages the generator repository. Implemented as a singleton (see `instance` method).
6
+ # It uses git to keep the repository up to date.
7
+ # It will check the repo out to '~/.grundstein'.
8
+ # It also creates a file in this directory named 'last_update' where the timestamp of the last update is kept.
9
+ # If this time stamp is too long ago, it will update the repo.
10
+ #
11
+ # Use this class by using the instance method: `Generator::Repository.instance`
12
+ class Repository
13
+ OUTDATED_THRESHOLD = 1 # day(s)
14
+
15
+ using Grundstein::Refinements::ColoredStrings
16
+
17
+ def self.instance
18
+ @instance ||= Repository.new(File.expand_path("~/.grundstein"))
19
+ return @instance
20
+ end
21
+
22
+ # Iteratates through all generators and yields the block with the |name, desc|.
23
+ def generators
24
+ result = []
25
+ Dir.foreach(generators_path) do |dir|
26
+ next if dir.start_with?('.', '#')
27
+ result << dir
28
+ end
29
+ return result
30
+ end
31
+
32
+ def generators_path
33
+ return File.join(@path, 'generators')
34
+ end
35
+
36
+ def generator_path(name)
37
+ return File.join(generators_path, name)
38
+ end
39
+
40
+ protected
41
+
42
+ # The constructor will check if the repository is there and create it if necessary.
43
+ # If the repo is outdated, it will update it.
44
+ def initialize(path)
45
+ @path = path
46
+ @git = Git.open(@path) # , :log => Logger.new(STDOUT))
47
+ update if outdated?
48
+ rescue ArgumentError => _
49
+ setup
50
+ retry
51
+ end
52
+
53
+ def outdated?
54
+ return DateTime.parse(File.read(last_update_file_path)) < (DateTime.now - OUTDATED_THRESHOLD)
55
+ rescue => _
56
+ return true
57
+ end
58
+
59
+ def update
60
+ puts "Updating generator repository..."
61
+ @git.pull
62
+ File.write(last_update_file_path, DateTime.now.to_s)
63
+ end
64
+
65
+ def setup
66
+ puts "Initializing generator repository...".c_warning
67
+ @git = Git.init(@path)
68
+ @git.add_remote('origin', 'https://github.com/motine/grundstein.git')
69
+
70
+ # TODO: make sure git > 1.7
71
+ # let's only checkout the generators
72
+ @git.config('core.sparsecheckout', true)
73
+ File.write(File.join(@git.repo.path, 'info', 'sparse-checkout'), 'generators')
74
+ update
75
+ end
76
+
77
+ def last_update_file_path
78
+ return File.join(@path, 'last_update')
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1 @@
1
+ require_relative 'refinements/colored_strings'
@@ -0,0 +1,26 @@
1
+ require 'highline'
2
+
3
+ module Grundstein
4
+ module Refinements
5
+ # Add namespaced refinement for the color scheme (we are not using HighLine's stuff like `ColorScheme` or `HighLine.colorize_strings`).
6
+ module ColoredStrings
7
+ refine String do
8
+ def c_gen
9
+ HighLine.color(self, :blue, :bold)
10
+ end
11
+
12
+ def c_info
13
+ HighLine.color(self, :green, :bold)
14
+ end
15
+
16
+ def c_warning
17
+ HighLine.color(self, :yellow, :bold)
18
+ end
19
+
20
+ def c_error
21
+ HighLine.color(self, :red, :bold)
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,29 @@
1
+ require 'thor'
2
+ require 'highline/import'
3
+
4
+ module Grundstein
5
+ # Entry point for the command line interface.
6
+ class Runner < Thor
7
+ using Grundstein::Refinements::ColoredStrings
8
+
9
+ desc :list, 'Lists all available generators.'
10
+ def list
11
+ Generator::Repository.instance.generators.each do |name|
12
+ gen = Generator::Loader.new(name)
13
+ puts " #{gen.name.ljust(20).c_gen} #{gen.desc}"
14
+ end
15
+ rescue Generator::GeneratorError => e
16
+ puts "ERROR: #{e.to_s.c_error}"
17
+ end
18
+
19
+ desc 'add NAME', 'Runs the given generator in the current directory.'
20
+ def add(generator_name)
21
+ generator = Generator::Loader.new(generator_name)
22
+ generator.run
23
+ rescue Generator::GeneratorError => e
24
+ puts "ERROR: #{e.to_s.c_error}"
25
+ # TODO: ask stuff (https://github.com/JEG2/highline)
26
+ # e.g.: gitignore will ask you if your intend to use Vagrant or Ruby or C...
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,3 @@
1
+ module Grundstein
2
+ VERSION = "0.0.1".freeze
3
+ end
metadata ADDED
@@ -0,0 +1,191 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: grundstein
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Tom Rothe
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-03-11 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: thor
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 0.19.1
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 0.19.1
27
+ - !ruby/object:Gem::Dependency
28
+ name: highline
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.7'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.7'
41
+ - !ruby/object:Gem::Dependency
42
+ name: mustache
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: git
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '1.3'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '1.3'
69
+ - !ruby/object:Gem::Dependency
70
+ name: bundler
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '1.7'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '1.7'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rake
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '10.0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '10.0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rubocop
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: 0.37.2
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: 0.37.2
111
+ - !ruby/object:Gem::Dependency
112
+ name: sdoc
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: 0.4.1
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: 0.4.1
125
+ description: Grundstein automates adding common files by we adding (smart) templates.
126
+ E.g. add a Vagrantfile, a .gitignore, or a sinatra app.
127
+ email:
128
+ - info@tomrothe.de
129
+ executables:
130
+ - grundstein
131
+ extensions: []
132
+ extra_rdoc_files: []
133
+ files:
134
+ - ".gitignore"
135
+ - ".rubocop.yml"
136
+ - Gemfile
137
+ - LICENSE.txt
138
+ - README.Development.md
139
+ - README.md
140
+ - Rakefile
141
+ - Vagrantfile
142
+ - bin/grundstein
143
+ - generators/#vagrant/ask_for_ubuntu_or_fedora
144
+ - generators/rubocop/.rubocop.yml
145
+ - generators/rubocop/Gemfile
146
+ - generators/rubocop/README.Development.md
147
+ - generators/rubocop/Rakefile
148
+ - generators/rubocop/_generator.rb
149
+ - generators/sdoc/.gitignore
150
+ - generators/sdoc/Gemfile
151
+ - generators/sdoc/README.Development.md
152
+ - generators/sdoc/README.md
153
+ - generators/sdoc/Rakefile
154
+ - generators/sdoc/_generator.rb
155
+ - generators/sdoc/doc/.keep
156
+ - grundstein.gemspec
157
+ - lib/grundstein.rb
158
+ - lib/grundstein/generator.rb
159
+ - lib/grundstein/generator/environment.rb
160
+ - lib/grundstein/generator/errors.rb
161
+ - lib/grundstein/generator/loader.rb
162
+ - lib/grundstein/generator/repository.rb
163
+ - lib/grundstein/refinements.rb
164
+ - lib/grundstein/refinements/colored_strings.rb
165
+ - lib/grundstein/runner.rb
166
+ - lib/grundstein/version.rb
167
+ homepage: https://github.com/motine/grundstein
168
+ licenses:
169
+ - MIT
170
+ metadata: {}
171
+ post_install_message:
172
+ rdoc_options: []
173
+ require_paths:
174
+ - lib
175
+ required_ruby_version: !ruby/object:Gem::Requirement
176
+ requirements:
177
+ - - ">="
178
+ - !ruby/object:Gem::Version
179
+ version: '0'
180
+ required_rubygems_version: !ruby/object:Gem::Requirement
181
+ requirements:
182
+ - - ">="
183
+ - !ruby/object:Gem::Version
184
+ version: '0'
185
+ requirements: []
186
+ rubyforge_project:
187
+ rubygems_version: 2.4.8
188
+ signing_key:
189
+ specification_version: 4
190
+ summary: Automates adding common files to your project.
191
+ test_files: []