gem_updater 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: 1b964766638c2f8ef6afc1b87b3d17ae68fec20c
4
+ data.tar.gz: 92e434d50c4d81fb9d95537afe89001fdd9536ea
5
+ SHA512:
6
+ metadata.gz: 04b7c13adf8c38658fb0a47e6df184373f826d94c5bc29f889bd86d85b942cf96506eb6d58d05b41fdf4a5f1d3f457fa2ff3d4cda224080697e5bf5939b6f92b
7
+ data.tar.gz: f01e1bc74d5618300f0728d0e2ed7bf43a8244d27beaedf12b34b4943bf061f3eada7f856ccbc31d15da5e37228d4ae493d95fe0165b31aa088f9f4a57ee32d7
data/bin/gem_update ADDED
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # Exit cleanly from an early interrupt
4
+ Signal.trap("INT") { exit 1 }
5
+
6
+ require 'gem_updater'
7
+
8
+ gems = GemUpdater::Updater.new
9
+ gems.update!
10
+
11
+ puts "\nHere are your changes:"
12
+ puts '------------------------'
13
+ gems.format_diff
@@ -0,0 +1,49 @@
1
+ require 'gem_updater/gem_file'
2
+ require 'gem_updater/ruby_gems_fetcher'
3
+ require 'gem_updater/source_page_parser'
4
+
5
+ module GemUpdater
6
+ class Updater
7
+
8
+ def initialize
9
+ @gemfile = GemUpdater::GemFile.new
10
+ end
11
+
12
+ # Update process.
13
+ # This will:
14
+ # 1. update gemfile
15
+ # 2. find changelogs for updated gems
16
+ def update!
17
+ @gemfile.update!
18
+
19
+ @gemfile.changes.each do |gem_name, details|
20
+ source_uri = GemUpdater::RubyGemsFetcher.new( gem_name ).source_uri
21
+ source_page = GemUpdater::SourcePageParser.new( url: source_uri, version: details[ :versions ][ :new ] )
22
+
23
+ @gemfile.changes[ gem_name ][ :changelog ] = source_page.changelog if source_page.changelog
24
+ end
25
+ end
26
+
27
+ # Format the diff to get human readable information
28
+ # on the gems that were updated.
29
+ def format_diff
30
+ @gemfile.changes.each do |gem, details|
31
+ puts ERB.new( template, nil, '<>' ).result( binding )
32
+ end
33
+ end
34
+
35
+ private
36
+
37
+ # Get the template for gem's diff.
38
+ # It can use a custom template.
39
+ #
40
+ # @return [ERB] the template
41
+ def template
42
+ @template ||= begin
43
+ File.read( "#{Dir.home}/.gem_updater_template.erb" )
44
+ rescue Errno::ENOENT
45
+ File.read( File.expand_path( '../../lib/gem_updater_template.erb', __FILE__ ) )
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,34 @@
1
+ require 'bundler/cli'
2
+
3
+ module GemUpdater
4
+
5
+ # GemFile is responsible for handling `Gemfile`
6
+ class GemFile
7
+ attr_accessor :changes
8
+
9
+ def initialize
10
+ @old_spec_set = Bundler.definition.specs
11
+ @changes = {}
12
+ end
13
+
14
+ # Run `bundle update` to update gems.
15
+ # Then get new spec set.
16
+ def update!
17
+ puts "Updating gems..."
18
+ Bundler::CLI.new.update
19
+ @new_spec_set = Bundler.definition.specs
20
+ compute_changes
21
+ end
22
+
23
+ # Compute the diffs between two `Gemfile.lock`.
24
+ #
25
+ # @return [Hash] gems for which there are differences.
26
+ def compute_changes
27
+ @old_spec_set.each do |gem_specs|
28
+ unless ( old_version = gem_specs.version ) == ( new_version = @new_spec_set[ gem_specs.name ].first.version )
29
+ @changes[ gem_specs.name ] = { versions: { old: old_version.to_s, new: new_version.to_s } }
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,34 @@
1
+ require 'json'
2
+ require 'nokogiri'
3
+ require 'open-uri'
4
+
5
+ module GemUpdater
6
+
7
+ # RubyGemsFetcher is a wrapper around rubygems API.
8
+ class RubyGemsFetcher
9
+
10
+ # @param gem_name [String] name of the gem
11
+ def initialize( gem_name )
12
+ @gem_name = gem_name
13
+ end
14
+
15
+ # Finds where code is hosted.
16
+ # Most likely in will be in 'source_code_uri' or 'homepage_uri'
17
+ #
18
+ # @return [String] url of gem source code
19
+ def source_uri
20
+ information[ "source_code_uri" ] || information[ "homepage_uri" ]
21
+ end
22
+
23
+ private
24
+
25
+ # Obtain information about a given gem
26
+ # from rubygems.org.
27
+ # See API: http://guides.rubygems.org/rubygems-org-api/#gem-methods
28
+ #
29
+ # @return [Hash] parse of json information
30
+ def information
31
+ @information ||= JSON.parse( open( "https://rubygems.org/api/v1/gems/#{@gem_name}.json" ).read )
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,131 @@
1
+ require 'nokogiri'
2
+ require 'open-uri'
3
+
4
+ module GemUpdater
5
+
6
+ # SourcePageParser is responsible for parsing a source page where
7
+ # the gem code is hosted.
8
+ class SourcePageParser
9
+
10
+ # @param url [String] url of page
11
+ # @param version [String] version of gem
12
+ def initialize( url: nil, version: nil )
13
+ @uri = correct_uri( url )
14
+ @version = version
15
+ end
16
+
17
+ # Get the changelog in an uri.
18
+ #
19
+ # @return [String, nil] URL of changelog
20
+ def changelog
21
+ @changelog ||= begin
22
+ puts "Looking for a changelog in #{@uri}"
23
+ doc = Nokogiri::HTML( open( @uri ) )
24
+
25
+ find_changelog( doc )
26
+
27
+ rescue OpenURI::HTTPError # Uri points to nothing
28
+ puts "Cannot find #{@uri}"
29
+ end
30
+ end
31
+
32
+ private
33
+
34
+ # Some gems have 'http://github.com' as URI which will redirect to https
35
+ # leading `open_uri` to crash.
36
+ #
37
+ # @param url [String] the url to parse
38
+ # @return [URI] valid URI
39
+ def correct_uri( url )
40
+ uri = URI( url )
41
+ uri.scheme = 'https' if uri.host == 'github.com' && uri.scheme == 'http'
42
+
43
+ uri
44
+ end
45
+
46
+ # Try to find where changelog might be.
47
+ #
48
+ # @param doc [Nokogiri::XML::Element] document of source page
49
+ def find_changelog( doc )
50
+ case @uri.host
51
+ when 'github.com'
52
+ GitHubParser.new( doc, @version ).changelog
53
+ end
54
+ end
55
+
56
+ # List possible names for a changelog
57
+ # since humans may have many many ways to call it.
58
+ #
59
+ # @return [Array] list of possible names
60
+ def changelog_names
61
+ %w( CHANGELOG Changelog ChangeLog changelog HISTORY History history )
62
+ end
63
+
64
+ # Some documents like the one written in markdown may contain
65
+ # a direct anchor to specific version.
66
+ #
67
+ # @param file_name [String] file name of changelog
68
+ # @return [Boolean] true if file may contain an anchor
69
+ def changelog_may_contain_anchor?( file_name )
70
+ %w( .md .rdoc ).include?( File.extname( file_name ) )
71
+ end
72
+
73
+ # GitHubParser is responsible for parsing source code
74
+ # hosted on github.com.
75
+ class GitHubParser < SourcePageParser
76
+ BASE_URL = 'https://github.com'
77
+
78
+ # @param doc [Nokogiri::XML::Element] document of source page
79
+ # @param version [String] version of gem
80
+ def initialize( doc, version )
81
+ @doc = doc
82
+ @version = version
83
+ end
84
+
85
+ # Finds url of changelog.
86
+ #
87
+ # @return [String] the URL of changelog
88
+ def changelog
89
+ url = find_changelog_link
90
+
91
+ if url
92
+ full_url = BASE_URL + url
93
+
94
+ if changelog_may_contain_anchor?( full_url )
95
+ anchor = find_anchor( full_url )
96
+ full_url += anchor if anchor
97
+ end
98
+
99
+ full_url
100
+ end
101
+ end
102
+
103
+ private
104
+
105
+ # Find which link corresponds to changelog.
106
+ #
107
+ # @return [String, nil] url of changelog
108
+ def find_changelog_link
109
+ changelog_names.find do |name|
110
+ node = @doc.at_css( %(table.files a[title^="#{name}"]) )
111
+ if node
112
+ break node.attr( 'href' )
113
+ end
114
+ end
115
+ end
116
+
117
+ # Looks into document to find it there is an anchor to new gem version.
118
+ #
119
+ # @param url [String] url of changelog
120
+ # @return [String, nil] anchor's href
121
+ def find_anchor( url )
122
+ changelog_page = Nokogiri::HTML( open( url ) )
123
+ anchor = changelog_page.css( %(a.anchor) ).find{ |element| element.attr( 'href' ).match( @version.gsub('.', '') ) }
124
+
125
+ if anchor
126
+ anchor.attr( 'href' )
127
+ end
128
+ end
129
+ end
130
+ end
131
+ end
@@ -0,0 +1,5 @@
1
+ * <%= gem %> <%= details[ :versions ][ :old ] %> → <%= details[ :versions ][ :new ] %>
2
+ <% if details[ :changelog ] %>
3
+ [changelog](<%= details[ :changelog ] %>)
4
+ <% end %>
5
+
metadata ADDED
@@ -0,0 +1,108 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: gem_updater
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Maxime Demolin
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-02-15 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.7'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.7'
27
+ - !ruby/object:Gem::Dependency
28
+ name: json
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.8'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.8'
41
+ - !ruby/object:Gem::Dependency
42
+ name: nokogiri
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.6'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.6'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '3.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '3.0'
69
+ description: It updates the gems of your Gemfile and fetches the links pointing to
70
+ where their changelogs are
71
+ email: akbarova.armia@gmail.com
72
+ executables:
73
+ - gem_update
74
+ extensions: []
75
+ extra_rdoc_files: []
76
+ files:
77
+ - bin/gem_update
78
+ - lib/gem_updater.rb
79
+ - lib/gem_updater/gem_file.rb
80
+ - lib/gem_updater/ruby_gems_fetcher.rb
81
+ - lib/gem_updater/source_page_parser.rb
82
+ - lib/gem_updater_template.erb
83
+ homepage: https://github.com/MaximeD/gem_updater
84
+ licenses:
85
+ - MIT
86
+ metadata: {}
87
+ post_install_message:
88
+ rdoc_options: []
89
+ require_paths:
90
+ - lib
91
+ required_ruby_version: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - ">="
94
+ - !ruby/object:Gem::Version
95
+ version: '0'
96
+ required_rubygems_version: !ruby/object:Gem::Requirement
97
+ requirements:
98
+ - - ">="
99
+ - !ruby/object:Gem::Version
100
+ version: '0'
101
+ requirements: []
102
+ rubyforge_project:
103
+ rubygems_version: 2.4.5
104
+ signing_key:
105
+ specification_version: 4
106
+ summary: Update your gems and find their changelogs
107
+ test_files: []
108
+ has_rdoc: