changi 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 0d7fbfb1689d7effa8bed7d6ca6ca66073b6f31f
4
+ data.tar.gz: c436fe107d78b2479e66f578e0a92ba783eb3344
5
+ SHA512:
6
+ metadata.gz: c4e10adc77fde7170e214922b6a9845f17a0af8d4b039bb53944076e424287e126c4236ca66480da5ebf0b46f9f5534a4b1135f86929808c36cbd71ec886da23
7
+ data.tar.gz: 0e8c75ba30d82a81ff03acfd492db5f8f3cd4abb75628ce3f85a15450722b748f0ab7c9d58201d09ed26d75b5fe626b4889ab49bcd859e9e8e4a17c6b6274ebf
@@ -0,0 +1,29 @@
1
+ module Changi
2
+ class Changelog
3
+ def initialize config
4
+ @config = config
5
+ end
6
+
7
+ def new_entry
8
+ entry_set.new_entry
9
+ end
10
+
11
+ def render demo: false
12
+ release = demo ? Release.demo : Release.build
13
+ renderer = Renderer.new @config, entry_set, release
14
+ renderer.render
15
+ end
16
+
17
+ def update
18
+ updater = @config.updater.new
19
+ updater.update @config.changelog_path, render
20
+ entry_set.destroy_all
21
+ end
22
+
23
+ private
24
+
25
+ def entry_set
26
+ @entry_set ||= EntrySet.new @config
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,34 @@
1
+ module Changi
2
+ class Configuration
3
+ attr_accessor :changelog_path
4
+ attr_accessor :entries_path
5
+ attr_accessor :default_categories
6
+ attr_accessor :updater
7
+ attr_accessor :changelog_template
8
+
9
+ def self.default
10
+ new.tap do |config|
11
+ config.changelog_path = File.join Dir.pwd, 'changelog.md'
12
+ config.entries_path = File.join Dir.pwd, 'changelog'
13
+ config.default_categories = []
14
+ config.updater = Updater::PrependUpdater
15
+ config.changelog_template = <<-eod
16
+ # <%= release.version %>, <%= Time.now.strftime('%Y-%m-%d') %><%= release.notes and ", \#{release.notes}" %>
17
+ <% entry_set.entries_by_category.each do |category, entries| %>
18
+ ## <%= category %>
19
+
20
+ <%=
21
+ entries.map do |e|
22
+ if e.text.is_a?(Array)
23
+ e.text.map.with_index { |l, i| i == 0 ? "* \#{l}" : " \#{l}" }.join("\n")
24
+ else
25
+ "* \#{e.text.gsub("\n", ' ')}"
26
+ end
27
+ end.join("\n")
28
+ %>
29
+ <% end %>
30
+ eod
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,48 @@
1
+ require 'yaml'
2
+
3
+ module Changi
4
+ class Entry
5
+ include FlexibleAttributes
6
+
7
+ class << self
8
+ def build entry_set, yaml_path
9
+ new(entry_set, yaml_path).tap &:read_in_attributes
10
+ end
11
+
12
+ def load entry_set, yaml_path
13
+ new(entry_set, yaml_path).tap &:read_in_yaml
14
+ end
15
+ end
16
+
17
+ define_attribute :category, reader: Reader::CategoryReader
18
+ define_attribute :text, reader: Reader::MultilineReader, opts: { required: true }
19
+
20
+ attr_reader :entry_set
21
+
22
+ def initialize entry_set, path
23
+ @entry_set = entry_set
24
+ @path = path
25
+ end
26
+
27
+ def read_in_yaml
28
+ data = YAML.load_file @path
29
+ self.class.attribute_names.each { |name| send "#{name}=".to_sym, data[name] }
30
+ end
31
+
32
+ def save
33
+ File.open(@path, 'w') { |fd| fd.puts to_yaml }
34
+ end
35
+
36
+ def destroy
37
+ unless system "git rm '#{@path}' >/dev/null 2>&1"
38
+ FileUtils.rm_f @path
39
+ end
40
+ end
41
+
42
+ private
43
+
44
+ def to_yaml
45
+ self.class.attribute_names.map { |name| [name, send(name.to_sym)] }.to_h.to_yaml
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,50 @@
1
+ require 'fileutils'
2
+
3
+ module Changi
4
+ class EntrySet
5
+ attr_reader :config
6
+
7
+ def initialize config
8
+ @config = config
9
+ FileUtils.mkdir_p @config.entries_path
10
+ end
11
+
12
+ def new_entry
13
+ entries << Entry.build(self, make_entry_path).tap(&:save)
14
+ end
15
+
16
+ def entries
17
+ @entries ||= yamls.map { |yml| Entry.load self, yml }
18
+ end
19
+
20
+ def entries_by_category
21
+ entries.group_by &:category
22
+ end
23
+
24
+ def previous_categories
25
+ entries.map &:category
26
+ end
27
+
28
+ def destroy_all
29
+ entries.each &:destroy
30
+ end
31
+
32
+ private
33
+
34
+ def yamls
35
+ Dir["#{@config.entries_path}/*yml"]
36
+ end
37
+
38
+ def make_entry_path
39
+ File.join @config.entries_path, make_entry_name
40
+ end
41
+
42
+ def make_entry_name
43
+ if git_branch = `git rev-parse --abbrev-ref HEAD 2>/dev/null`.strip
44
+ "#{Time.now.strftime('%y%m%d%H%M%S')}_from_#{git_branch}.yml"
45
+ else
46
+ "#{Time.now.strftime('%y%m%d%H%M%S')}.yml"
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,33 @@
1
+ module Changi
2
+ module FlexibleAttributes
3
+ module ClassMethods
4
+ def attributes
5
+ @attributes ||= []
6
+ end
7
+
8
+ def attribute_names
9
+ attributes.map { |x| x[:name] }
10
+ end
11
+
12
+ def define_attribute name, reader: Reader::StringReader, opts: {}, &block
13
+ attr_accessor name.to_sym
14
+
15
+ attributes << {
16
+ name: name,
17
+ reader: (block_given? ? block : ->(*args) { reader.new.read *args }),
18
+ opts: opts
19
+ }
20
+ end
21
+ end
22
+
23
+ def self.included base
24
+ base.extend ClassMethods
25
+ end
26
+
27
+ def read_in_attributes
28
+ self.class.attributes.each do |data|
29
+ send "#{data[:name]}=".to_sym, data[:reader].call(data, self)
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,24 @@
1
+ module Changi
2
+ module Reader
3
+ class CategoryReader < StringReader
4
+ def read attribute, owner
5
+ fail 'CategoryReader can only be used for Entry attributes.' unless owner.is_a? Entry
6
+
7
+ cli.say "#{attribute[:name].capitalize}:\n"
8
+ cli.choose do |menu|
9
+ menu.choices *categories(owner.entry_set)
10
+ menu.choice 'Other (create new)' do
11
+ cli.ask "Please insert #{attribute[:name]}:"
12
+ end
13
+ menu.choice('Abort') { exit }
14
+ end
15
+ end
16
+
17
+ private
18
+
19
+ def categories entry_set
20
+ (entry_set.previous_categories + entry_set.config.default_categories).uniq
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,63 @@
1
+ require 'tempfile'
2
+
3
+ module Changi
4
+ module Reader
5
+ class MultilineReader
6
+ def read attribute, owner
7
+ tmpfile = Tempfile.new 'changi'
8
+
9
+ intro tmpfile, attribute
10
+
11
+ unless system "#{editor} '#{tmpfile.path.strip}'"
12
+ abort 'editor returned with non-zero exit status, abort'
13
+ end
14
+
15
+ read_and_strip(tmpfile).tap do |data|
16
+ if attribute[:opts][:required] && data.empty?
17
+ abort "required #{attribute[:name]} attribute empty, abort"
18
+ end
19
+ end
20
+ ensure
21
+ tmpfile.close
22
+ tmpfile.unlink
23
+ end
24
+
25
+ private
26
+
27
+ def intro tmpfile, attribute
28
+ tmpfile.puts
29
+ tmpfile.puts
30
+ tmpfile.puts "# Please enter #{attribute[:name]} above."
31
+ tmpfile.puts "# Lines starting with # will be ignored."
32
+ tmpfile.puts "# Empty tmpfile will abort the process." if attribute[:opts][:required]
33
+ tmpfile.sync
34
+ tmpfile.close
35
+ end
36
+
37
+ def read_and_strip tmpfile
38
+ tmpfile.open
39
+ tmpfile.read.strip.split("\n").reject { |x| x =~ /^\s*#/ }
40
+ .drop_while(&:empty?).reverse.drop_while(&:empty?).reverse
41
+ end
42
+
43
+ def editor
44
+ editor_tests.lazy.map(&:call).find { |e| !(e.nil? || e.empty?) }.tap do |e|
45
+ fail 'could not detect editor' unless e
46
+ end
47
+ end
48
+
49
+ def editor_tests
50
+ [
51
+ -> { ENV['EDITOR'] },
52
+ -> { `git config core.editor`.strip },
53
+ -> { editor_exists?('nano') && 'nano' },
54
+ -> { editor_exists?('vim') && 'vim' }
55
+ ]
56
+ end
57
+
58
+ def editor_exists? app
59
+ system "command -v #{app} >/dev/null 2>&1"
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,21 @@
1
+ require 'highline'
2
+
3
+ module Changi
4
+ module Reader
5
+ class StringReader
6
+ def read attribute, owner
7
+ cli.ask("#{attribute[:name]}: ").tap do |x|
8
+ if attribute[:opts][:required] && [nil, ''].include?(x)
9
+ abort "required #{attribute[:name]} attribute empty, abort"
10
+ end
11
+ end
12
+ end
13
+
14
+ protected
15
+
16
+ def cli
17
+ @cli ||= HighLine.new
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,20 @@
1
+ module Changi
2
+ class Release
3
+ include FlexibleAttributes
4
+
5
+ class << self
6
+ def build
7
+ new.tap &:read_in_attributes
8
+ end
9
+
10
+ def demo
11
+ new.tap do |inst|
12
+ attribute_names.each { |name| inst.send "#{name}=".to_sym, "<#{name}>" }
13
+ end
14
+ end
15
+ end
16
+
17
+ define_attribute :version
18
+ define_attribute :notes
19
+ end
20
+ end
@@ -0,0 +1,18 @@
1
+ require 'erb'
2
+
3
+ module Changi
4
+ class Renderer
5
+ attr_reader :entry_set
6
+ attr_reader :release
7
+
8
+ def initialize config, entry_set, release
9
+ @config = config
10
+ @entry_set = entry_set
11
+ @release = release
12
+ end
13
+
14
+ def render
15
+ ERB.new(@config.changelog_template).result binding
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,20 @@
1
+ require 'changi'
2
+
3
+ namespace :changi do
4
+ desc 'Create a new changelog entry'
5
+ task :new do
6
+ Changi.changelog.new_entry
7
+ end
8
+
9
+ desc 'Show changelog for next release'
10
+ task :diff do
11
+ puts Changi.changelog.render demo: true
12
+ end
13
+
14
+ desc 'Update project changelog for release'
15
+ task :update do
16
+ Changi.changelog.update
17
+ puts 'Changelog updated, and changelog directory cleared.'
18
+ puts 'Check if there were manual changes to changelog.md and merge them before committing.'
19
+ end
20
+ end
@@ -0,0 +1,15 @@
1
+ module Changi
2
+ module Updater
3
+ class PrependUpdater
4
+ def update changelog_path, release_data
5
+ previous = File.read changelog_path if File.exists? changelog_path
6
+
7
+ File.open changelog_path, 'w' do |fd|
8
+ fd.puts release_data
9
+ fd.puts
10
+ fd.puts previous if previous
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
data/lib/changi.rb ADDED
@@ -0,0 +1,27 @@
1
+ require 'changi/reader/string_reader'
2
+ require 'changi/reader/multiline_reader'
3
+ require 'changi/reader/category_reader'
4
+ require 'changi/updater/prepend_updater'
5
+ require 'changi/flexible_attributes'
6
+ require 'changi/changelog'
7
+ require 'changi/configuration'
8
+ require 'changi/entry'
9
+ require 'changi/entry_set'
10
+ require 'changi/release'
11
+ require 'changi/renderer'
12
+
13
+ module Changi
14
+ class << self
15
+ def configuration
16
+ @configuration ||= Configuration.default
17
+ end
18
+
19
+ def configure
20
+ yield configuration
21
+ end
22
+
23
+ def changelog
24
+ @changelog ||= Changelog.new configuration
25
+ end
26
+ end
27
+ end
metadata ADDED
@@ -0,0 +1,86 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: changi
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.1
5
+ platform: ruby
6
+ authors:
7
+ - Mobisol GmbH
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-05-13 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rake
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '10.4'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '10.4'
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
+ description: Manages a set of changelog entries for you and combines them to a changelog
42
+ file when you release.
43
+ email: dev@plugintheworld.com
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - lib/changi.rb
49
+ - lib/changi/changelog.rb
50
+ - lib/changi/configuration.rb
51
+ - lib/changi/entry.rb
52
+ - lib/changi/entry_set.rb
53
+ - lib/changi/flexible_attributes.rb
54
+ - lib/changi/reader/category_reader.rb
55
+ - lib/changi/reader/multiline_reader.rb
56
+ - lib/changi/reader/string_reader.rb
57
+ - lib/changi/release.rb
58
+ - lib/changi/renderer.rb
59
+ - lib/changi/tasks.rb
60
+ - lib/changi/updater/prepend_updater.rb
61
+ homepage: https://github.org/plugintheworld/changi
62
+ licenses:
63
+ - MIT
64
+ metadata: {}
65
+ post_install_message:
66
+ rdoc_options: []
67
+ require_paths:
68
+ - lib
69
+ required_ruby_version: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ version: '0'
74
+ required_rubygems_version: !ruby/object:Gem::Requirement
75
+ requirements:
76
+ - - ">="
77
+ - !ruby/object:Gem::Version
78
+ version: '0'
79
+ requirements: []
80
+ rubyforge_project:
81
+ rubygems_version: 2.4.5.1
82
+ signing_key:
83
+ specification_version: 4
84
+ summary: Tiny library of changelog management rake tasks.
85
+ test_files: []
86
+ has_rdoc: