Skip to content
Browse files

[WIP] Modernize

  • Loading branch information...
1 parent ca48079 commit 2e12d8db0da02d9a3523f6cf535ce2700ac18b2c @fabiopelosin fabiopelosin committed
View
10 .gitmodules
@@ -1,7 +1,3 @@
-
-[submodule "vendor/Xcodeproj"]
- path = vendor/Xcodeproj
- url = git://github.com/CocoaPods/Xcodeproj.git
-[submodule "vendor/CocoaPods"]
- path = vendor/CocoaPods
- url = git://github.com/CocoaPods/CocoaPods.git
+[submodule "spec/fixtures/master_repo"]
+ path = spec/fixtures/master_repo
+ url = https://github.com/CocoaPods/Specs.git
View
16 Gemfile
@@ -1,17 +1,17 @@
-source :rubygems
+source 'https://rubygems.org'
-#gem 'cocoapods', '>= 0.6.0.rc5'
+gem 'cocoapods-core', :git => 'https://github.com/CocoaPods/Core.git'
gem 'json'
gem 'colored'
gem 'octokit'
-gem 'active_support'
+gem 'activesupport'
+gem 'redcarpet'
gem 'sinatra'
gem 'sinatra-cache'
gem 'haml'
gem 'twitter'
gem 'exceptio-ruby'
-gem 'rake'
group :development do
gem 'thin'
@@ -22,3 +22,11 @@ group :development do
gem 'rack-test'
gem 'mocha'
end
+
+group :specs do
+ gem "mocha"
+ gem "bacon"
+ gem "mocha-on-bacon"
+ gem "rake"
+ gem 'coveralls', :require => false
+end
View
115 Gemfile.lock
@@ -1,56 +1,88 @@
+GIT
+ remote: https://github.com/CocoaPods/Core.git
+ revision: f34dda7b9700fcfdf3f7a0c2adc1e5c700b29253
+ specs:
+ cocoapods-core (0.17.0.rc6)
+ activesupport (~> 3.2.6)
+ faraday (~> 0.8.1)
+ octokit (~> 1.7)
+ rake (~> 10.0.0)
+
GEM
- remote: http://rubygems.org/
+ remote: https://rubygems.org/
specs:
- active_support (3.0.0)
- activesupport (= 3.0.0)
- activesupport (3.0.0)
- addressable (2.2.8)
- awesome_print (1.0.2)
- backports (2.6.1)
+ activesupport (3.2.13)
+ i18n (= 0.6.1)
+ multi_json (~> 1.0)
+ addressable (2.3.3)
+ awesome_print (1.1.0)
+ backports (3.1.1)
+ bacon (1.2.0)
colored (1.2)
- daemons (1.1.8)
- eventmachine (0.12.10)
+ colorize (0.5.8)
+ coveralls (0.6.3)
+ colorize
+ multi_json (~> 1.3)
+ rest-client
+ simplecov (>= 0.7)
+ thor
+ daemons (1.1.9)
+ eventmachine (1.0.3)
exceptio-ruby (0.1.5)
httparty (~> 0.8.1)
- faraday (0.8.1)
+ faraday (0.8.7)
multipart-post (~> 1.1)
- faraday_middleware (0.8.8)
+ faraday_middleware (0.9.0)
faraday (>= 0.7.4, < 0.9)
- foreman (0.47.0)
+ foreman (0.62.0)
thor (>= 0.13.6)
- haml (3.1.6)
- hashie (1.2.0)
+ haml (4.0.1)
+ tilt
+ hashie (2.0.3)
httparty (0.8.3)
multi_json (~> 1.0)
multi_xml
- json (1.7.3)
+ i18n (0.6.1)
+ json (1.7.7)
metaclass (0.0.1)
- mocha (0.11.4)
+ mime-types (1.21)
+ mocha (0.13.3)
metaclass (~> 0.0.1)
- multi_json (1.3.6)
- multi_xml (0.5.1)
- multipart-post (1.1.5)
- octokit (1.7.0)
+ mocha-on-bacon (0.2.2)
+ mocha (>= 0.13.0)
+ multi_json (1.7.2)
+ multi_xml (0.5.3)
+ multipart-post (1.2.0)
+ netrc (0.7.7)
+ octokit (1.24.0)
addressable (~> 2.2)
faraday (~> 0.8)
- faraday_middleware (~> 0.8)
- hashie (~> 1.2)
+ faraday_middleware (~> 0.9)
+ hashie (~> 2.0)
multi_json (~> 1.3)
- rack (1.4.1)
- rack-protection (1.2.0)
+ netrc (~> 0.7.7)
+ rack (1.5.2)
+ rack-protection (1.5.0)
rack
- rack-test (0.6.1)
+ rack-test (0.6.2)
rack (>= 1.0)
- rake (0.9.2.2)
- simple_oauth (0.1.8)
- sinatra (1.3.2)
- rack (~> 1.3, >= 1.3.6)
- rack-protection (~> 1.2)
+ rake (10.0.4)
+ redcarpet (2.2.2)
+ rest-client (1.6.7)
+ mime-types (>= 1.16)
+ simple_oauth (0.2.0)
+ simplecov (0.7.1)
+ multi_json (~> 1.0)
+ simplecov-html (~> 0.7.1)
+ simplecov-html (0.7.1)
+ sinatra (1.3.6)
+ rack (~> 1.4)
+ rack-protection (~> 1.3)
tilt (~> 1.3, >= 1.3.3)
sinatra-cache (0.3.7)
sinatra (>= 1.1.0)
sinatra-outputbuffer (>= 0.1.0)
- sinatra-contrib (1.3.1)
+ sinatra-contrib (1.3.2)
backports (>= 2.0)
eventmachine
rack-protection
@@ -59,32 +91,37 @@ GEM
tilt (~> 1.3)
sinatra-outputbuffer (0.1.0)
sinatra (>= 1.0.a)
- thin (1.3.1)
+ thin (1.5.1)
daemons (>= 1.0.9)
eventmachine (>= 0.12.6)
rack (>= 1.0.0)
- thor (0.15.4)
- tilt (1.3.3)
- twitter (3.0.1)
- faraday (~> 0.8)
- multi_json (~> 1.3)
- simple_oauth (~> 0.1.6)
+ thor (0.17.0)
+ tilt (1.3.6)
+ twitter (4.6.2)
+ faraday (~> 0.8, < 0.10)
+ multi_json (~> 1.0)
+ simple_oauth (~> 0.2)
PLATFORMS
ruby
DEPENDENCIES
- active_support
+ activesupport
awesome_print
+ bacon
+ cocoapods-core!
colored
+ coveralls
exceptio-ruby
foreman
haml
json
mocha
+ mocha-on-bacon
octokit
rack-test
rake
+ redcarpet
sinatra
sinatra-cache
sinatra-contrib
View
54 Rakefile
@@ -1,53 +1,23 @@
-require 'rake/testtask'
-def rvm_ruby_dir
- @rvm_ruby_dir ||= File.expand_path('../..', `which ruby`.strip)
-end
+task :default => 'spec:all'
-namespace :travis do
- task :install_opencflite_debs do
- sh "mkdir -p debs"
- Dir.chdir("debs") do
- sh "wget http://archive.ubuntu.com/ubuntu/pool/main/i/icu/libicu44_4.4.2-2ubuntu0.11.04.1_i386.deb" unless File.exist?("libicu44_4.4.2-2ubuntu0.11.04.1_i386.deb")
- base_url = "https://github.com/downloads/CocoaPods/OpenCFLite"
- %w{ opencflite1_248-1_i386.deb opencflite-dev_248-1_i386.deb }.each do |deb|
- sh "wget #{File.join(base_url, deb)}" unless File.exist?(deb)
- end
- sh "sudo dpkg -i *.deb"
- end
- end
+#--------------------------------------#
- task :fix_rvm_include_dir do
- unless File.exist?(File.join(rvm_ruby_dir, 'include'))
- # Make Ruby headers available, RVM seems to do not create a include dir on 1.8.7, but it does on 1.9.3.
- sh "mkdir '#{rvm_ruby_dir}/include'"
- sh "ln -s '#{rvm_ruby_dir}/lib/ruby/1.8/i686-linux' '#{rvm_ruby_dir}/include/ruby'"
- end
+namespace :spec do
+ desc "Run all the specs"
+ task :all do
+ sh "bundle exec bacon #{specs('**')}"
end
- task :setup => [:install_opencflite_debs, :fix_rvm_include_dir] do
- sh "CFLAGS='-I#{rvm_ruby_dir}/include' bundle install"
+ desc "Run the unit specs"
+ task :unit do
+ sh "bundle exec bacon #{specs('unit/**')}"
end
end
-Rake::TestTask.new do |t|
- t.pattern = "test/**/*.rb"
+def specs(dir)
+ FileList["spec/#{dir}/*_spec.rb"].shuffle.join(' ')
end
-Rake::TestTask.new("test:app") do |t|
- t.pattern = "test/app_test.rb"
-end
-
-Rake::TestTask.new("test:repo") do |t|
- t.pattern = "test/repo_test.rb"
-end
-
-Rake::TestTask.new("test:rss") do |t|
- t.pattern = "test/rss_test.rb"
-end
-
-Rake::TestTask.new("test:twitter") do |t|
- t.pattern = "test/twitter_test.rb"
-end
+#--------------------------------------#
-task :default => :test
View
191 app.rb
@@ -4,95 +4,144 @@
require 'json'
require 'exceptio-ruby'
require 'colored'
+require 'cocoapods-core'
-class CocoaPodsNotifier < Sinatra::Application
-end
+APP_ROOT = Pathname.new(File.expand_path('../', __FILE__))
+$:.unshift (APP_ROOT + 'lib').to_s
-ROOT = File.expand_path('../', __FILE__)
+require 'cocoapods_notifier'
-$:.unshift File.join(ROOT, 'lib')
-$:.unshift File.join(ROOT, 'vendor/Xcodeproj/lib')
-$:.unshift File.join(ROOT, 'vendor/CocoaPods/lib')
-require 'cocoapods'
-require 'repo'
-require 'rss'
-require 'twitter_notifier'
+module CocoaPodsNotifier
-class CocoaPodsNotifier
- RSS_FILE = File.join(ROOT, 'public/new-pods.rss')
+ class CocoaPodsNotifierApp < Sinatra::Application
- configure do
- set :root, ROOT
- register Sinatra::Cache
- set :cache_output_dir, File.join(ROOT, 'public')
- set :cache_enabled, true
+ # Setup
+ #---------------------------------------------------------------------------#
- set :haml, :format => :html5
- ExceptIO::Client.configure "cocoapods-feeds-cocoapods-org", ENV['EXCEPTIO_KEY'] if ENV['RACK_ENV'] == 'production'
- end
+ # The path of the RSS file. Served statically as it is located in the public
+ # folder.
+ #
+ RSS_FILE = File.join(APP_ROOT, 'public/new-pods.rss')
- configure :development do
- require 'awesome_print'
- require 'sinatra/reloader'
- register Sinatra::Reloader
- end
+ # Configurations.
+ #
+ configure do
+ set :root, APP_ROOT.to_s
+ set :haml, :format => :html5
- def self.init
- is_new = Repo.new.setup
- update
- end
+ register Sinatra::Cache
+ set :cache_output_dir, File.join(APP_ROOT, 'public')
+ set :cache_enabled, true
- def self.update
- repo = Repo.new.tap { |r| r.update }
+ if ENV['RACK_ENV'] == 'production'
+ ExceptIO::Client.configure "cocoapods-feeds-cocoapods-org", ENV['EXCEPTIO_KEY']
+ end
+ end
- feed = RSS.new(repo.pods, repo.creation_dates).feed
- File.open(RSS_FILE, 'w') { |f| f.write(feed) }
- puts '-> RSS feed created'.cyan unless $silent
+ # Development configurations.
+ #
+ configure :development do
+ require 'awesome_print'
+ require 'sinatra/reloader'
+ register Sinatra::Reloader
+ end
- repo.new_pods.each { |pod| Twitter.tweet(pod) }
- puts "-> Tweeted #{repo.new_pods.count} pods".cyan unless $silent
- rescue Exception => e
- puts "[!] update failed: #{e}".red
- puts e.backtrace.join("\n")
- ExceptIO::Client.log e, ENV['RACK_ENV']
- end
+ require "twitter"
+ ::Twitter.configure do |config|
+ config.consumer_key = ENV['CONSUMER_KEY']
+ config.consumer_secret = ENV['CONSUMER_SECRET']
+ config.oauth_token = ENV['OAUTH_TOKEN']
+ config.oauth_token_secret = ENV['OAUTH_TOKEN_SECRET']
+ end
+
+ # Repo Actions
+ #-------------------------------------------------------------------------#
+
+ def master_repo
+ @master_repo ||= Repo.new(APP_ROOT + 'tmp/.cocoapods/master', APP_ROOT + 'caches/statistics.yml')
+ end
+
+ # Clones the master repo from the remote and generates the feeds and
+ # sends the tweets for Pods of the last commit.
+ #
+ # @note If the tweets for the Pods of the last commit where already sent
+ # twitter will reject them.
+ #
+ def self.init
+ master_repo.setup_if_needed
+ update
+ end
+
+ #
+ #
+ def self.update
+ master_repo.update
- get '/' do
- begin
- repo = Repo.new
- pods = repo.pods
- @creation_dates = repo.creation_dates
- @pods_count = pods.length
- @new_pods = RSS.new(pods, @creation_dates).feed_pods
- @pods_tweets = {}
- @new_pods.each { |pod| @pods_tweets[pod.name] = Twitter.status(pod) }
- haml :index
+ feed = RSS.new(master_repo.pods, master_repo.creation_dates).feed
+ File.open(RSS_FILE, 'w') { |f| f.write(feed) }
+ puts '-> RSS feed created'.cyan unless $silent
+
+ master_repo.new_pods.each { |pod| Twitter.tweet(pod) }
+ puts "-> Tweeted #{master_repo.new_pods.count} pods".cyan unless $silent
rescue Exception => e
- puts "[!] get / failed: #{e}".red
+ puts "[!] update failed: #{e}".red
puts e.backtrace.join("\n")
ExceptIO::Client.log e, ENV['RACK_ENV']
- status 500
end
- end
- post "/#{ENV['HOOK_PATH']}" do
- begin
- start_time = Time.now
- payload = JSON.parse(params[:payload])
- if payload['ref'] == "refs/heads/master"
- self.class.update
- cache_expire('/index')
- status 201
- body "REINDEXED - #{(Time.now - start_time).to_i} seconds"
- else
- status 200
- body "NO UPDATES - #{(Time.now - start_time).to_i} seconds"
+ # Routes
+ #-------------------------------------------------------------------------#
+
+ # The home page. Shows a link to the feed and the preview of the tweets
+ # for the last 30 pods.
+ #
+ get '/' do
+ begin
+ pods = master_repo.pods
+ @creation_dates = master_repo.creation_dates
+ @pods_count = pods.length
+ @new_pods = RSS.new(pods, @creation_dates).pods_for_feed
+ @pods_tweets = {}
+ @new_pods.each { |pod| @pods_tweets[pod.name] = Twitter.new(nil).tweet_preview(pod) }
+ haml :index
+
+ rescue Exception => e
+ puts "[!] get / failed: #{e}".red
+ puts e.backtrace.join("\n")
+ ExceptIO::Client.log e, ENV['RACK_ENV']
+ status 500
end
- rescue Exception => e
- puts "[!] get /HOOK_PATH failed: #{e}".red
- puts e.backtrace.join("\n")
- ExceptIO::Client.log e, ENV['RACK_ENV']
- status 500
end
+
+ # The secret (TM) hook used by GitHub to trigger an update of the repo.
+ # It the process the RSS feed is recreated and the tweets for the Pods
+ # are sent.
+ #
+ post "/#{ENV['HOOK_PATH']}" do
+ begin
+ start_time = Time.now
+ payload = JSON.parse(params[:payload])
+
+ if payload['ref'] == "refs/heads/master"
+ self.class.update
+ cache_expire('/index')
+ status 201
+ body "REINDEXED - #{(Time.now - start_time).to_i} seconds"
+ else
+ status 200
+ body "NO UPDATES - #{(Time.now - start_time).to_i} seconds"
+ end
+
+ rescue Exception => e
+ puts "[!] get /HOOK_PATH failed: #{e}".red
+ puts e.backtrace.join("\n")
+ ExceptIO::Client.log e, ENV['RACK_ENV']
+ status 500
+ end
+ end
+
+ #---------------------------------------------------------------------------#
+
end
+
end
View
3,699 caches/statistics.yml
3,699 additions, 0 deletions not shown because the diff is too large. Please use a local Git client to view these changes.
View
4 config.ru
@@ -1,5 +1,5 @@
require File.expand_path '../app', __FILE__
$stdout.sync = true
-CocoaPodsNotifier.init
-run CocoaPodsNotifier
+# CocoaPodsNotifier::CocoaPodsNotifierApp.init
+run CocoaPodsNotifier::CocoaPodsNotifierApp
View
10 lib/cocoapods_notifier.rb
@@ -0,0 +1,10 @@
+module CocoaPodsNotifier
+
+ require 'cocoapods-core'
+ Pod = ::Pod
+
+ require 'cocoapods_notifier/repo'
+ require 'cocoapods_notifier/rss'
+ require 'cocoapods_notifier/twitter'
+
+end
View
149 lib/cocoapods_notifier/repo.rb
@@ -0,0 +1,149 @@
+module CocoaPodsNotifier
+
+ # Interface for the CocoaPods master repo.
+ #
+ class Repo
+
+ # @return [Pathname] The path where the master repo is stored.
+ #
+ attr_reader :master_repo_dir
+
+ # @return [Pod::Specification::Set::Statistics] The client to use for
+ # accessing the statistics of the Pods.
+ #
+ attr_reader :statistics_client
+
+ # @param [Pathname] master_repo_dir @see master_repo_dir
+ #
+ # @param [Pathname] statistics_cache_file
+ # The cache file to use for the statistics client. A cache file is
+ # used because computing the creation dates from the git meta-data
+ # is computationally expensive.
+ #
+ def initialize(master_repo_dir, statistics_cache_file = nil)
+ @master_repo_dir = master_repo_dir
+ @statistics_client = Pod::Specification::Set::Statistics.new
+ @statistics_client.cache_expiration = 0
+ @statistics_client.cache_file = statistics_cache_file
+ end
+
+ # @return [String]
+ #
+ def master_repo_url
+ "https://github.com/CocoaPods/Specs.git"
+ end
+
+ attr_accessor :silent
+
+ public
+
+ # Pods
+ #-------------------------------------------------------------------------#
+
+ # @return [Array<Pod::Specification::Set>]
+ #
+ def sets
+ Pod::Source.new(master_repo_dir).pod_sets.sort_by { |set| set.name.downcase }
+ end
+
+ # @return [Array<Pod::Specification::Set::Presenter>] the list of all
+ # the Pods available in the repo sorted by name.
+ #
+ # @note The Presenter is used because it takes care of massaging the
+ # specifications across the CocoaPods clients for a consistent
+ # experience.
+ #
+ def pods
+ sets.map { |set| Pod::Specification::Set::Presenter.new(set) }.sort_by(&:name)
+ end
+
+ # @return [Array<String>] The names of the pods.
+ #
+ def pod_names
+ pods.map(&:name)
+ end
+
+ # @return [Pod::Specification::Set::Presenter]
+ #
+ def pod_named(name)
+ pods.find { |pod| pod.name == name }
+ end
+
+ # @return [Hash{String => Time}] The date in which the first podspec of
+ # each Pod appeared for the first time in the master repo.
+ #
+ def creation_dates
+ statistics_client.creation_dates(sets)
+ end
+
+ public
+
+ # Initialization & Update
+ #-------------------------------------------------------------------------#
+
+ # @return [void]
+ #
+ def setup_if_needed
+ return if (master_repo_dir).exist?
+ title('Cloning Specs Repo')
+ master_repo_dir.dirname.mkpath
+ git("clone '#{master_repo_url}' '#{master_repo_dir}'")
+ end
+
+ # Updates the master repo against its git remote.
+ #
+ # @raise If the `git pull` command fails.
+ #
+ # @return [void]
+ #
+ def update
+ old_pod_names = pod_names
+ title('Updating Specs Repo')
+ git('pull', master_repo_dir)
+ @new_pod_names = pod_names - old_pod_names
+ end
+
+ # @return [Array<String>] The names of the new pods after the update.
+ #
+ attr_reader :new_pod_names
+
+ private
+
+ # Private Helpers
+ #-------------------------------------------------------------------------#
+
+ # Executes the given Git command.
+ #
+ # @param [String] command
+ # The command to execute.
+ #
+ # @param [String, Pathname] dir
+ # The directory in which to execute the command.
+ #
+ # @return [Bool] Whether the command was executed successfully.
+ #
+ def git(command, dir = Dir.pwd)
+ Dir.chdir(dir) do
+ output = `git #{command}`
+ unless $?.exitstatus.zero?
+ puts "git #{command} (in #{dir})"
+ puts output
+ raise 'Git command failed'
+ end
+ end
+ end
+
+ # Prints the given title.
+ #
+ # @return [void]
+ #
+ def title(string)
+ unless silent
+ puts "-> \033[0;36m#{string}\e[0m"
+ end
+ end
+
+ #-------------------------------------------------------------------------#
+
+ end
+end
View
89 lib/cocoapods_notifier/rss.rb
@@ -0,0 +1,89 @@
+require 'rss/maker'
+
+module CocoaPodsNotifier
+
+ # Creates the RSS feed.
+ #
+ class RSS
+
+ # @return [Array<Pod::Specification::Set::Presenter>] The list of all the
+ # Pods available in the master repo.
+ #
+ attr_reader :pods
+
+ # @return [Hash{String => Time}] The date in which the first podspec of
+ # each Pod appeared for the first time in the master repo.
+ #
+ attr_reader :creation_dates
+
+ # @param [Array<Pod::Specification::Set::Presenter>] @see pods
+ # @param [Hash{String => Time}] @see creation_dates
+ #
+ def initialize (pods, creation_dates)
+ @pods = pods
+ @creation_dates = creation_dates
+ end
+
+ # @return [String] The contents of the RSS feed.
+ #
+ def feed
+ rss = ::RSS::Maker.make('2.0') do |m|
+ m.channel.title = "CocoaPods"
+ m.channel.link = "http://www.cocoapods.org"
+ m.channel.description = "CocoaPods new pods feed"
+ m.channel.language = "en"
+ m.channel.lastBuildDate = Time.now
+ m.items.do_sort = true
+
+ pods_for_feed.each { |pod| configure_rss_item(m.items.new_item, pod) }
+ end
+ rss.to_s
+ end
+
+ # @return [Array<Pod::Specification::Set::Presenter>] The list of the 30
+ # most recent pods sorted from newest to oldest.
+ #
+ def pods_for_feed
+ pods.sort_by { |pod| creation_dates[pod.name] }.reverse[0..29]
+ end
+
+ private
+
+ # Private Helpers
+ #-------------------------------------------------------------------------#
+
+ # Configures a new RSS item with the given Pod.
+ #
+ # @param [Pod::Specification::Set::Presenter] The pod corresponding to the
+ # item.
+ #
+ # @return [void]
+ #
+ def configure_rss_item(item, pod)
+ item.title = pod.name
+ item.link = pod.homepage
+ item.pubDate = @creation_dates[pod.name]
+ item.description = rss_item_description(pod)
+ end
+
+ # Returns the HTML description of the RSS item corresponding to the given
+ # Pod.
+ #
+ # @param [Pod::Specification::Set::Presenter] The pod to describe.
+ #
+ # @return [String] the description for the RSS item.
+ #
+ def rss_item_description(pod)
+ s = "<p>#{pod.description.gsub(/\n/, "<br>")}</p>"
+ s << "<p>Authored by #{pod.authors}.</p>"
+ s << "<p>[ Available at: <a href=\"#{pod.source_url}\">#{pod.source_url}</a> ]</p>"
+ s << "<ul>"
+ s << "<li>Latest version: #{pod.version}</li>"
+ s << "<li>Platform: #{pod.platform}</li>"
+ s << "<li>License: #{pod.license}</li>" if pod.license
+ s << "<li>Stargazers: #{pod.github_watchers}</li>" if pod.github_watchers
+ s << "<li>Forks: #{pod.github_forks}</li>" if pod.github_forks
+ s << "</ul>"
+ end
+ end
+end
View
107 lib/cocoapods_notifier/twitter.rb
@@ -0,0 +1,107 @@
+# encoding: UTF-8
+
+module CocoaPodsNotifier
+
+ # Posts tweets about Pods.
+ #
+ class Twitter
+
+ attr_accessor :twitter_client
+
+ # @param [#update] The client to use for the update
+ #
+ def initialize(twitter_client)
+ @twitter_client = twitter_client
+ end
+
+ # @param [] pod
+ #
+ # @return [void]
+ #
+ def tweet(pod)
+ status = message_for_pod(pod.name, pod.summary, pod.homepage)
+ twitter_client.update(status)
+ end
+
+ def tweet_preview(pod)
+ message_for_pod(pod.name, pod.summary, pod.homepage)
+ end
+
+ private
+
+ # Private Helpers
+ #-------------------------------------------------------------------------#
+
+ # Returns the body for the tweet of the given Pod taking into account
+ # to truncate the summary.
+ #
+ # @note The summary is a required attribute of a Specification.
+ #
+ # @param [] pod
+ #
+ # @return [String] The body of the tweet.
+ #
+ def message_for_pod(pod_name, pod_summary, pod_homepage)
+ message = "[#{pod_name}] #{pod_summary}"
+ if message.length > message_max_length
+ message = truncate_message(message, message_max_length, ELLIPSIS_STRING)
+ end
+ message << LINK_SEPARATOR_STRING
+ message << pod_homepage
+ message
+ end
+
+ # Truncates the given message to the given length using the given ellipsis
+ # string. Trailing whitespace, comas and punctuation is removed.
+ #
+ # @param [String] message
+ # The message to truncate.
+ #
+ # @param [Fixnum] length
+ # The length to which truncate the message, including the ellipsis
+ # string length.
+ #
+ # @param [String] ellipsis_string
+ # The ellipsis string to append after the truncated message.
+ #
+ # @return [String] The truncated message.
+ #
+ def truncate_message(message, length, ellipsis_string)
+ chars = message.scan(/./mu)
+ max_lenght_with_ellipsis = length - ellipsis_string.length - 1
+ allowed_chars = chars[0..max_lenght_with_ellipsis]
+ allowed_chars.join.gsub(/ ?\.?,?$/,'') + ellipsis_string
+ end
+
+ # @return [Fixnum] The maximum length of the message for the tweet.
+ #
+ def message_max_length
+ MESSAGE_MAX_LENGTH - LINK_MAX_LENGTH - LINK_SEPARATOR_STRING.length
+ end
+
+ private
+
+ # Constants
+ #-------------------------------------------------------------------------#
+
+ # @return [Fixnum] The maximum length of the message.
+ #
+ MESSAGE_MAX_LENGTH = 140
+
+ # @return [Fixnum] The maximum length of a link. Twitter shortens http urls
+ # to 20 characters and https ones to 21.
+ #
+ LINK_MAX_LENGTH = 21
+
+ # @return [String] The string to use for the ellipsis.
+ #
+ LINK_SEPARATOR_STRING = ' '
+
+ # @return [String] The string to use for the ellipsis.
+ #
+ ELLIPSIS_STRING = ''
+
+ #-------------------------------------------------------------------------#
+
+ end
+end
View
51 lib/repo.rb
@@ -1,51 +0,0 @@
-class CocoaPodsNotifier
- class Repo
- attr_reader :new_pods
-
- def initialize
- @repos_dir = Pathname.new(File.join(ROOT, 'tmp/.cocoapods', __FILE__))
- Pod::Config.instance.repos_dir = @repos_dir
- Pod::Specification::Statistics.instance.cache_expiration = Time.mktime(2012,1,1).to_i
- end
-
- def master_dir
- @repos_dir + 'master'
- end
-
- def setup
- return if (master_dir).exist?
- @repos_dir.mkpath
- puts '-> Cloning Specs Repo'.cyan unless $silent
- Dir.chdir(@repos_dir) { `git clone '#{ENV['SPECS_URL']}' master` }
- raise 'Git failed to clone the master repo' unless $?.exitstatus == 0
- end
-
- def git_pull
- puts '-> Updating Specs Repo'.cyan unless $silent
- Dir.chdir(master_dir) { `git pull` }
- raise 'Git failed to pull the master repo' unless $?.exitstatus == 0
- end
-
- def update
- old_pods = pods
- git_pull
- @new_pods = pods - old_pods
- end
-
- def sets
- Pod::Source.all_sets.sort_by { |set| set.name.downcase }
- end
-
- def pods
- sets.map { |set| Pod::UI::UIPod.new(set) }.sort_by(&:name)
- end
-
- def pod_named(name)
- pods.find { |pod| pod.name == name }
- end
-
- def creation_dates
- Pod::Specification::Statistics.instance.creation_dates(sets)
- end
- end
-end
View
48 lib/rss.rb
@@ -1,48 +0,0 @@
-require 'rss/maker'
-
-class CocoaPodsNotifier
- class RSS
- def initialize (pods, creation_dates)
- @pods = pods
- @creation_dates = creation_dates
- end
-
- def feed_pods
- @pods.sort_by { |pod| @creation_dates[pod.name] }.reverse[0..29]
- end
-
- def feed
- rss = ::RSS::Maker.make('2.0') do |m|
- m.channel.title = "CocoaPods"
- m.channel.link = "http://www.cocoapods.org"
- m.channel.description = "CocoaPods new added pods feed"
- m.channel.language = "en"
- m.channel.lastBuildDate = Time.now
- m.items.do_sort = true
-
- feed_pods.each { |pod| configure_item(m.items.new_item, pod) }
- end
- rss.to_s
- end
-
- def configure_item(item, pod)
- item.title = pod.name
- item.link = pod.homepage
- item.pubDate = @creation_dates[pod.name]
- item.description = item_description(pod)
- end
-
- def item_description(pod)
- s = "<p>#{pod.description.gsub(/\n/, "<br>")}</p>"
- s << "<p>Authored by #{pod.authors}.</p>"
- s << "<p>[ Available at: <a href=\"#{pod.source_url}\">#{pod.source_url}</a> ]</p>"
- s << "<ul>"
- s << "<li>Latest version: #{pod.version}</li>"
- s << "<li>Platform: #{pod.platform}</li>"
- s << "<li>License: #{pod.license}</li>" if pod.license
- s << "<li>Watchers: #{pod.github_watchers}</li>" if pod.github_watchers
- s << "<li>Forks: #{pod.github_forks}</li>" if pod.github_forks
- s << "</ul>"
- end
- end
-end
View
33 lib/twitter_notifier.rb
@@ -1,33 +0,0 @@
-# encoding: UTF-8
-require "twitter"
-
-Twitter.configure do |config|
- config.consumer_key = ENV['CONSUMER_KEY']
- config.consumer_secret = ENV['CONSUMER_SECRET']
- config.oauth_token = ENV['OAUTH_TOKEN']
- config.oauth_token_secret = ENV['OAUTH_TOKEN_SECRET']
-end
-
-class CocoaPodsNotifier
- class Twitter
- # Twitter shortens urls to 20 characters
- # for http and 21 for https
- #
- MAX_LENGTH = 140 - 1 - 21
-
- def self.tweet(pod)
- status = status(pod) << " #{pod.homepage}"
- ::Twitter.update(status)
- end
-
- def self.status(pod)
- text = "[#{pod.name}] #{pod.summary}"
- text << '.' unless text =~ /\.$/
- chars = text.scan(/./mu)
- if chars.length >= MAX_LENGTH
- text = chars[0..MAX_LENGTH-2].join.gsub(/( )?\.?,?$/,'') + ''
- end
- text
- end
- end
-end
View
45 spec/app_spec.rb
@@ -0,0 +1,45 @@
+require File.expand_path('../spec_helper', __FILE__)
+
+ENV['SPECS_URL'] = 'git://github.com/CocoaPods/Specs.git'
+ENV['RACK_ENV'] = 'test'
+HOOK_PATH = ENV['HOOK_PATH'] = 'secret'
+$silent = true
+
+require 'rack/test'
+require File.expand_path('../../app', __FILE__)
+
+describe 'The CocoaPods Notifier App' do
+ extend Rack::Test::Methods
+
+ def app
+ CocoaPodsNotifier::CocoaPodsNotifierApp
+ end
+
+ before do
+ FileUtils.rm_f app::RSS_FILE
+ Twitter.stubs(:tweet)
+ end
+
+ it "returns a preview of the tweets" do
+ get '/'
+ last_response.should.be.ok
+ # last_response.body.should.include
+ end
+
+ it "generates the RSS file" do
+ post "/#{HOOK_PATH}", :payload => { 'ref' => 'refs/heads/master' }.to_json
+ last_response.status.should == 201
+ assert File.exist?(app::RSS_FILE)
+ end
+
+ it "generates the RSS file only for the master branch" do
+ post "/#{HOOK_PATH}", :payload => { 'ref' => 'refs/heads/other-branch' }.to_json
+ last_response.status.should == 200
+ File.exist?(app::RSS_FILE).should.be.false
+ end
+
+ it "generates the RSS file only for the master branch" do
+ post "/#{HOOK_PATH}", :payload => { 'ref' => 'refs/heads/master' }.to_json
+ last_response.status.should == 201
+ end
+end
1 spec/fixtures/master_repo
@@ -0,0 +1 @@
+Subproject commit 0dd253c94eee4e692eb8976ba5fe7d271b52f55a
View
17 spec/spec_helper.rb
@@ -0,0 +1,17 @@
+require 'pathname'
+ROOT = Pathname.new(File.expand_path('../../', __FILE__))
+$:.unshift((ROOT + 'lib').to_s)
+$:.unshift((ROOT + 'spec').to_s)
+
+require 'bundler/setup'
+require 'bacon'
+require 'mocha-on-bacon'
+require 'spec_helper/bacon'
+require 'cocoapods_notifier'
+
+def fixutre_repo
+ CocoaPodsNotifier::Repo.new(ROOT + 'spec/fixtures/master_repo')
+end
+
+# CocoaPodsNotifier::Repo.new.setup
+
View
197 spec/spec_helper/bacon.rb
@@ -0,0 +1,197 @@
+module Bacon
+ summary_at_exit
+
+ @needs_first_put = true
+
+ def self.color(color, string)
+ case color
+ when :red
+ "\e[31m#{string}\e[0m"
+ when :green
+ "\e[32m#{string}\e[0m"
+ when :yellow
+ "\e[33m#{string}\e[0m"
+ else
+ # Support for Conque
+ "\e[0m#{string}\e[0m"
+ end
+ end
+
+ #---------------------------------------------------------------------------#
+
+ # Overrides the SpecDoxzRtput to provide colored output by default
+ #
+ # Based on https://github.com/zen-cms/Zen-Core and subsequently modified
+ # which is available under the MIT License. Thanks YorickPeterse!
+ #
+ module SpecDoxOutput
+
+ def handle_specification(name)
+ if @needs_first_put
+ @needs_first_put = false
+ puts
+ end
+ @specs_depth = @specs_depth || 0
+ puts spaces + name
+ @specs_depth += 1
+
+ yield
+
+ @specs_depth -= 1
+ puts if @specs_depth.zero?
+ end
+
+ #:nodoc:
+ def handle_requirement(description, disabled = false)
+ error = yield
+
+ if !error.empty?
+ puts Bacon.color(:red, "#{spaces}- #{description} [FAILED]")
+ elsif disabled
+ puts Bacon.color(:yellow, "#{spaces}- #{description} [DISABLED]")
+ else
+ puts Bacon.color(:green, "#{spaces}- #{description}")
+ end
+ end
+
+ #:nodoc:
+ def handle_summary
+ print ErrorLog if Backtraces
+ unless Counter[:disabled].zero?
+ puts Bacon.color(:yellow, "#{Counter[:disabled]} disabled specifications\n")
+ end
+ puts "%d specifications (%d requirements), %d failures, %d errors" %
+ Counter.values_at(:specifications, :requirements, :failed, :errors)
+ end
+
+ #:nodoc:
+ def spaces
+ return ' ' * @specs_depth
+ end
+ end
+
+ #---------------------------------------------------------------------------#
+
+ # Overrides the TestUnitOutput to provide colored result output.
+ #
+ module TestUnitOutput
+
+ # Represents the specifications as `:`.
+ #
+ def handle_specification(name)
+ indicator = Bacon.color(nil, ':')
+ print indicator
+ @indicators||=''
+ @indicators << indicator
+ yield
+ end
+
+ # Represents the requirements as:
+ #
+ # - [.] successful
+ # - [E] error
+ # - [F] failure
+ # - [_] skipped
+ #
+ # After the first failure or error all the other requirements are skipped.
+ #
+ def handle_requirement(description, disabled = false)
+ if @first_error
+ indicator = Bacon.color(nil, '_')
+ else
+ error = yield
+ if !error.empty?
+ @first_error = true
+ m = error[0..0]
+ c = (m == "E" ? :red : :yellow)
+ indicator = Bacon.color(c, m)
+ elsif disabled
+ indicator = "D"
+ else
+ indicator = Bacon.color(nil, '.')
+ end
+ end
+ print indicator
+ @indicators||=''
+ @indicators << indicator
+ end
+
+ def handle_summary
+ first_error = ''
+ error_count = 0
+ ErrorLog.lines.each do |s|
+ error_count += 1 if s.include?('Error:') || s.include?('Informative')
+ first_error << s if error_count <= 1
+ end
+ first_error = first_error.gsub(Dir.pwd + '/', '')
+ first_error = first_error.gsub(/lib\//, Bacon.color(:yellow, 'lib') + '/')
+ first_error = first_error.gsub(/:([0-9]+):/, ':' + Bacon.color(:yellow, '\1') + ':')
+ puts "\n#{first_error}" if Backtraces
+ unless Counter[:disabled].zero?
+ puts Bacon.color(:yellow, "#{Counter[:disabled]} disabled specifications")
+ end
+ result = "%d specifications (%d requirements), %d failures, %d errors" %
+ Counter.values_at(:specifications, :requirements, :failed, :errors)
+ if Counter[:failed].zero?
+ puts Bacon.color(:green, result)
+ else
+ puts Bacon.color(:red, result)
+ end
+ end
+
+ end
+
+ #---------------------------------------------------------------------------#
+
+ module FilterBacktraces
+ def handle_summary
+ ErrorLog.replace(ErrorLog.split("\n").reject do |line|
+ line =~ %r{(gems/mocha|spec_helper|ruby_noexec_wrapper)}
+ end.join("\n").lstrip << "\n\n")
+ super
+ end
+ end
+
+ #---------------------------------------------------------------------------#
+
+ extend FilterBacktraces
+
+ class Context
+
+ # Add support for disabled specs
+ #
+ def xit(description, &block)
+ Counter[:disabled] += 1
+ Bacon.handle_requirement(description, true) {[]}
+ end
+
+ # Add support for running only focused specs
+ #
+ # @note The implementation is a hack because bacon evaluates Context#it
+ # immediately. Therefore this method is intended to be **temporary**.
+ #
+ # @example
+ #
+ # module BaconFocusedMode; end
+ #
+ # describe "A Context" do
+ # it "will *not* runt" do; end
+ # fit "will runt" do; end
+ # end
+ #
+ #
+ def fit(description, &block)
+ origina_it(description, &block)
+ end
+
+ # Add support for focused specs
+ #
+ alias :origina_it :it
+ def it(description, &block)
+ unless defined?(::BaconFocusedMode)
+ origina_it(description, &block)
+ end
+ end
+ end
+end
+
View
97 spec/unit/repo_spec.rb
@@ -0,0 +1,97 @@
+require File.expand_path('../../spec_helper', __FILE__)
+
+describe CocoaPodsNotifier::Repo do
+
+ describe "In general" do
+
+ before do
+ @sut = CocoaPodsNotifier::Repo.new(ROOT + 'spec/fixtures/master_repo')
+ end
+
+ it "returns the directory of the master repo" do
+ @sut.master_repo_dir.class.should == Pathname
+ @sut.master_repo_dir.to_s.should.end_with('spec/fixtures/master_repo')
+ end
+
+ end
+
+ #---------------------------------------------------------------------------#
+
+ describe "Pods" do
+
+ before do
+ repos_dir = ROOT + 'spec/fixtures/master_repo'
+ statistics_cache_file = ROOT + 'caches/statistics.yml'
+ @sut = CocoaPodsNotifier::Repo.new(repos_dir, statistics_cache_file)
+ end
+
+ it "returns the list of the available sets" do
+ sets = @sut.sets
+ sets.map(&:class).uniq.should == [Pod::Specification::Set]
+ sets.map(&:name).should.include('AFNetworking')
+ end
+
+ it "returns the list of the Pod presenters" do
+ pods = @sut.pods
+ pods.map(&:class).uniq.should == [Pod::Specification::Set::Presenter]
+ pods.map(&:name).should.include('AFNetworking')
+ end
+
+ it "returns the presenter for the Pod with the given name" do
+ pod = @sut.pod_named('AFNetworking')
+ pod.name.should == 'AFNetworking'
+ pod.summary.should == 'A delightful iOS and OS X networking framework.'
+ end
+
+ it "returns the creation dates of the Pods" do
+ @sut.creation_dates['AFNetworking'].should == Time.new(2011,9,18, 21,2,31, '+02:00')
+ end
+
+ end
+
+ #---------------------------------------------------------------------------#
+
+ describe "Initialization & Update" do
+
+ before do
+ @repo_dir = ROOT + 'tmp/spec_master_repo'
+ @sut = CocoaPodsNotifier::Repo.new(@repo_dir)
+ @sut.silent = true
+ end
+
+ it "sets up the master repo if the directory doesn't exists" do
+ @sut.stubs(:master_repo_url).returns(ROOT + 'spec/fixtures/master_repo')
+ @sut.setup_if_needed
+ test_path = @repo_dir + 'AFNetworking/1.2.0/AFNetworking.podspec'
+ test_path.should.exist
+ end
+
+ it "skips the setup of the master repo if the directory exists" do
+ @sut.expects(:git).never
+ @sut.setup_if_needed
+ end
+
+ it "updates the master repo against its remote" do
+ @sut.expects(:git).with("pull", @sut.master_repo_dir)
+ @sut.update
+ end
+
+ it "returns the list of pods added after an update" do
+ @sut.setup_if_needed
+ Dir.chdir(@sut.master_repo_dir) { `git checkout 9235af8 > /dev/null 2>&1` }
+ def @sut.git(command, dir = Dir.pwd)
+ Dir.chdir(master_repo_dir) do
+ `git checkout 406cc94 > /dev/null 2>&1`
+ end
+ end
+ @sut.update
+ Dir.chdir(@sut.master_repo_dir) { `git checkout master > /dev/null 2>&1` }
+ @sut.new_pod_names.should == ["StateMachine-GCDThreadsafe", "TMTumblrSDK", "iOS-GTLYouTube"]
+ end
+
+ end
+
+ #---------------------------------------------------------------------------#
+
+
+end
View
130 spec/unit/rss_spec.rb
@@ -0,0 +1,130 @@
+require File.expand_path('../../spec_helper', __FILE__)
+require 'rexml/document'
+
+describe CocoaPodsNotifier::RSS do
+
+ before do
+ repo = fixutre_repo
+ @af_netowrking = repo.pod_named('AFNetworking')
+ @mb_progress_hud = repo.pod_named('MBProgressHUD')
+ pods = [@af_netowrking, @mb_progress_hud]
+ creation_dates = {
+ 'AFNetworking' => Time.new(2012, 01, 01),
+ 'MBProgressHUD' => Time.new(2012, 01, 02)
+ }
+ @sut = CocoaPodsNotifier::RSS.new(pods, creation_dates)
+ end
+
+ #---------------------------------------------------------------------------#
+
+ describe "In general" do
+
+ it "returns the RSS feed" do
+ feed = REXML::Document.new(@sut.feed).root
+ feed.elements['channel/title'].text.should == 'CocoaPods'
+ feed.elements['channel/link'].text.should == 'http://www.cocoapods.org'
+ feed.elements['channel/description'].text.should == 'CocoaPods new pods feed'
+ feed.elements['channel/language'].text.should == 'en'
+ feed.elements['channel/item[1]/title'].text.should == 'MBProgressHUD'
+ feed.elements['channel/item[2]/title'].text.should == 'AFNetworking'
+ end
+
+ end
+
+ #---------------------------------------------------------------------------#
+
+ describe "Private Helpers" do
+
+ describe "#pods_for_feed" do
+
+ it "returns the list of the Pods to include in the feed" do
+ @sut.send(:pods_for_feed).map(&:name).sort.should == ["AFNetworking", "MBProgressHUD"]
+ end
+
+ it "it sorts the Pods from newest to oldest" do
+ @sut.send(:pods_for_feed).map(&:name).should == ["MBProgressHUD", "AFNetworking"]
+ end
+
+ end
+
+ #--------------------------------------#
+
+ describe "#configure_rss_item" do
+
+ before do
+ rss = ::RSS::Maker.make('2.0') do |rss|
+ rss.channel.title = "CocoaPods"
+ rss.channel.link = "http://www.cocoapods.org"
+ rss.channel.description = "CocoaPods new added pods feed"
+ @item = rss.items.new_item
+ end
+ @sut.send(:configure_rss_item, @item, @af_netowrking)
+ end
+
+ it "uses the name of the Pod as the item title" do
+ @item.title.should == 'AFNetworking'
+ end
+
+ it "uses the Pod homepage as the item link" do
+ @item.link.should == 'https://github.com/AFNetworking/AFNetworking'
+ end
+
+ it "uses the creation date of the Pod as the item publication date" do
+ @item.pubDate.should == Time.new(2012, 01, 01)
+ end
+
+ it "uses the description of the Pod as the item description" do
+ @item.description.should.match /A delightful iOS and OS X networking framework/
+ end
+
+ end
+
+ #--------------------------------------#
+
+ describe "#rss_item_description" do
+
+ before do
+ @desc = @sut.send(:rss_item_description, @af_netowrking)
+ end
+
+ it "includes the description" do
+ @desc.should.include('<p>A delightful iOS and OS X networking framework.</p>')
+ end
+
+ it "includes the list of the authors" do
+ @desc.should.include('<p>Authored by Mattt Thompson and Scott Raymond.</p>')
+ end
+
+ it "includes the link to the source" do
+ @desc.should.include('<p>[ Available at: <a href="https://github.com/AFNetworking/AFNetworking.git">https://github.com/AFNetworking/AFNetworking.git</a> ]</p>')
+ end
+
+ it "includes the latest version" do
+ @desc.should.include('<li>Latest version: 1.2.0</li>')
+ end
+
+ it "includes the latest platforms" do
+ @desc.should.include('<li>Platform: iOS 5.0 - OS X 10.7</li>')
+ end
+
+ it "includes the license" do
+ @desc.should.include('<li>License: MIT</li>')
+ end
+
+ it "includes the github stargazers" do
+ @desc.should.match(/<li>Stargazers: .*<\/li>/)
+ end
+
+ it "includes the github forks" do
+ @desc.should.match(/<li>Forks: .*<\/li>/)
+ end
+
+ end
+
+ #--------------------------------------#
+
+ end
+
+ #---------------------------------------------------------------------------#
+
+end
View
96 spec/unit/twitter_spec.rb
@@ -0,0 +1,96 @@
+#encoding: UTF-8
+require File.expand_path('../../spec_helper', __FILE__)
+
+
+describe CocoaPodsNotifier::Twitter do
+
+ before do
+ repo = CocoaPodsNotifier::Repo.new(ROOT + 'tmp/.cocoapods/master')
+ @pod = repo.pod_named('AFNetworking')
+ @twitter_client = stub()
+ @sut = CocoaPodsNotifier::Twitter.new(@twitter_client)
+ end
+
+ #---------------------------------------------------------------------------#
+
+ describe "In general" do
+
+ it "posts a tweet for the given Pod" do
+ @twitter_client.expects(:update).with('[AFNetworking] A delightful iOS and OS X networking framework. https://github.com/AFNetworking/AFNetworking')
+ @sut.tweet(@pod)
+ end
+
+ end
+
+ #---------------------------------------------------------------------------#
+
+ describe "Private Helpers" do
+
+ describe "#message_for_pod" do
+
+ it "doesn't modifies short messages" do
+ result = @sut.send(:message_for_pod, 'Pod', "A short description.", 'www.example.com')
+ result.should == "[Pod] A short description. www.example.com"
+ end
+
+ it "truncates a long messages" do
+ pod_summary = "A short description"
+ pod_summary << "#" * 140
+ result = @sut.send(:message_for_pod, 'Pod', pod_summary, 'www.example.com')
+ result.gsub('www.example.com', '').length.should == 119
+ result.should.match /\[Pod\] A short description.+ www.example.com/
+ end
+
+ end
+
+ #--------------------------------------#
+
+ describe "#truncate_message" do
+
+ it "truncates a message to the given length" do
+ message = 'A delightful long description.'
+ result = @sut.send(:truncate_message, message, 10, '...')
+ result.should == 'A delig...'
+ result.length.should == 10
+ end
+
+ it "strip trailing white spaces" do
+ message = 'A long description.'
+ result = @sut.send(:truncate_message, message, 10, '...')
+ result.should == 'A long...'
+ result.length.should == 9
+ end
+
+ it "strip trailing dots" do
+ message = 'A long. Description.'
+ result = @sut.send(:truncate_message, message, 10, '...')
+ result.should == 'A long...'
+ result.length.should == 9
+ end
+
+ it "strip trailing comas" do
+ message = 'A long. Description.'
+ result = @sut.send(:truncate_message, message, 10, '...')
+ result.should == 'A long...'
+ result.length.should == 9
+ end
+
+ end
+
+ #--------------------------------------#
+
+ describe "#message_max_length" do
+
+ it "returns the maximum length available for the description excluding the link" do
+ @sut.send(:message_max_length).should == 118
+ end
+
+ end
+
+ #--------------------------------------#
+
+ end
+
+ #---------------------------------------------------------------------------#
+
+end
View
3,697 statistics.yml
3,697 additions, 0 deletions not shown because the diff is too large. Please use a local Git client to view these changes.
View
41 test/app_test.rb
@@ -1,41 +0,0 @@
-require File.expand_path('../test_helper', __FILE__)
-
-class AppTest < Test::Unit::TestCase
- include Rack::Test::Methods
-
- def setup
- super
- FileUtils.rm_f CocoaPodsNotifier::RSS_FILE
- CocoaPodsNotifier::Twitter.stubs(:tweet)
- end
-
- def teardown
- super
- #puts last_response.errors
- end
-
- def app
- CocoaPodsNotifier
- end
-
- def test_it_generates_the_rss_file
- post "/#{HOOK_PATH}", :payload => { 'ref' => 'refs/heads/master' }.to_json
- assert_equal 201, last_response.status
- assert File.exist?(CocoaPodsNotifier::RSS_FILE)
- end
-
- def test_it_does_not_generate_the_rss_file_for_other_branches_than_master
- post "/#{HOOK_PATH}", :payload => { 'ref' => 'refs/heads/other-branch' }.to_json
- assert_equal 200, last_response.status
- assert !File.exist?(CocoaPodsNotifier::RSS_FILE)
- end
-
- def test_it_identifies_the_pods_added_by_an_update
- post "/#{HOOK_PATH}", :payload => { 'ref' => 'refs/heads/master' }.to_json
- assert_equal 201, last_response.status
- CocoaPodsNotifier::Twitter.stubs(:tweet)
- CocoaPodsNotifier::Twitter.stubs(:tweet)
-
- end
-
-end
View
30 test/repo_test.rb
@@ -1,30 +0,0 @@
-require File.expand_path('../test_helper', __FILE__)
-
-class RepoTest < Test::Unit::TestCase
-
- def test_it_detects_the_new_pods_after_an_update
- repo = CocoaPodsNotifier::Repo.new
- Dir.chdir(repo.master_dir) { `git checkout 175726e > /dev/null 2>&1` }
- def repo.git_pull; Dir.chdir(master_dir) { `git checkout fe31dfa > /dev/null 2>&1` }; end
- repo.update
-
- new_pods = repo.new_pods.map { |pod| pod.name }
- assert_equal %w[ AWSiOSSDK
- BlockAlertsAnd-ActionSheets
- CustomBadge
- DTWebArchive
- GAJavaScript
- KIF
- MwfTableViewController
- NLTHTTPStubServer
- NanoStore
- OpenUDID
- ReactiveCocoa
- SFSocialFacebook
- STLOAuth
- ZKRevealingTableViewCell
- ZKTextField ], new_pods
-
- Dir.chdir(repo.master_dir) { `git checkout master > /dev/null 2>&1` }
- end
-end
View
123 test/rss_test.rb
@@ -1,123 +0,0 @@
-require File.expand_path('../test_helper', __FILE__)
-require 'rexml/document'
-
-class RSSTest < Test::Unit::TestCase
- def setup
- super
- unless @rss
- @sets = []
- json_kit = Pod::Source.search(Pod::Dependency.new('JSONKit'))
- json_kit.stubs(:versions).returns([Pod::Version.new('1.4')])
- @sets << json_kit
- ss_zip_archive = Pod::Source.search(Pod::Dependency.new('SSZipArchive'))
- ss_zip_archive.stubs(:versions).returns([Pod::Version.new('0.1.2')])
- @sets << ss_zip_archive
-
- @pods = @sets.map { |set| Pod::UI::UIPod.new(set) }
-
- # SSZipArchive has been created most recently, so comes first
- # JSONKit has been created earlier, so comes last
- @creation_dates = {
- 'JSONKit' => Time.now - 60,
- 'SSZipArchive' => Time.now - 30,
- }
-
- @rss = CocoaPodsNotifier::RSS.new(@pods, @creation_dates)
- @root = REXML::Document.new(@rss.feed).root
- item1 = @root.elements['channel/item[1]']
- item2 = @root.elements['channel/item[2]']
- @desc1 = REXML::Document.new("<root>#{item1.elements['description'].text}</root>").root
- @desc2 = REXML::Document.new("<root>#{item2.elements['description'].text}</root>").root
- end
- end
-
- # Check RSS Items
-
- def check_values(item_node)
- value1 = @root.elements["channel/item[1]/#{item_node}"].text
- value2 = @root.elements["channel/item[2]/#{item_node}"].text
- [value1, value2]
- end
-
- def test_it_uses_the_pod_titles_as_item_title
- value1, value2 = check_values('title')
- assert_equal value1, 'SSZipArchive'
- assert_equal value2, 'JSONKit'
- end
-
- def test_it_uses_the_pod_homepages_as_item_link
- value1, value2 = check_values('link')
- assert_equal value1, 'https://github.com/samsoffes/ssziparchive'
- assert_equal value2, 'https://github.com/johnezang/JSONKit'
- end
-
- # Check Items Descriptions
-
- def check_description_values(desc_node_1, desc_node_2 = nil)
- desc_node_2 = desc_node_1 unless desc_node_2
- value1 = @desc1.elements[desc_node_1].text
- value2 = @desc2.elements[desc_node_2].text
- [value1, value2]
- end
-
- def test_it_shows_the_pod_description
- value1, value2 = check_description_values('p[1]')
- assert_equal value1, 'SSZipArchive is a simple utility class for zipping and unzipping files on iOS and Mac.'
- assert_equal value2, 'A Very High Performance Objective-C JSON Library.'
- end
-
- def test_it_shows_the_pod_author
- value1 = @desc1.elements['p[2]'].to_s
- value2 = @desc2.elements['p[2]'].to_s
- assert_equal value1.to_s, "<p>Authored by Sam Soffes.</p>"
- assert_equal value2.to_s, "<p>Authored by John Engelhart.</p>"
-
- end
-
- def test_it_shows_the_repo_link
- value1 = @desc1.elements['p[3]'].to_s
- value2 = @desc2.elements['p[3]'].to_s
- assert_equal value1.to_s, "<p>[ Available at: <a href='https://github.com/samsoffes/ssziparchive.git'>https://github.com/samsoffes/ssziparchive.git</a> ]</p>"
- assert_equal value2.to_s, "<p>[ Available at: <a href='https://github.com/johnezang/JSONKit.git'>https://github.com/johnezang/JSONKit.git</a> ]</p>"
- end
-
- def test_it_shows_the_pod_description
- value1, value2 = check_description_values('p[1]')
- assert_equal value1, 'SSZipArchive is a simple utility class for zipping and unzipping files on iOS and Mac.'
- assert_equal value2, 'A Very High Performance Objective-C JSON Library.'
- end
-
- def test_it_shows_the_pod_version
- value1, value2 = check_description_values('ul/li[1]')
- assert_equal value1, "Latest version: 0.1.2"
- assert_equal value2, "Latest version: 1.4"
- end
-
- def test_it_shows_the_pod_platform
- value1, value2 = check_description_values('ul/li[2]')
- assert_equal value1, "Platform: iOS - OS X"
- assert_equal value2, "Platform: iOS - OS X"
- end
-
- def test_it_shows_the_pod_license
- value1, value2 = check_description_values('ul/li[3]')
- # SSZipArchive doesn't have a license
- assert_equal value2, "License: BSD / Apache License, Version 2.0"
- end
-
- def test_it_shows_the_pod_github_watchers
- value1, value2 = check_description_values('ul/li[4]', 'ul/li[4]')
- assert_match /Watchers: \d+/, value1
- assert_match /Watchers: \d+/, value2
- end
-
- def test_it_shows_the_pod_github_forks
- value1, value2 = check_description_values('ul/li[5]', 'ul/li[5]')
- assert_match /Forks: \d+/, value1
- assert_match /Forks: \d+/, value2
- end
-
- def test_it_generates_the_CocoaPods_channel
- assert_equal 'CocoaPods', @root.elements['channel/title'].text
- end
-end
View
14 test/test_helper.rb
@@ -1,14 +0,0 @@
-ENV['SPECS_URL'] = 'git://github.com/CocoaPods/Specs.git'
-HOOK_PATH = ENV['HOOK_PATH'] = 'secret'
-
-require File.expand_path('../../app', __FILE__)
-
-require 'test/unit'
-require 'mocha'
-require 'rack/test'
-
-ENV['RACK_ENV'] = 'test'
-$silent = true
-
-CocoaPodsNotifier::Repo.new.setup
-
View
64 test/twitter_test.rb
@@ -1,64 +0,0 @@
-# encoding: UTF-8
-require File.expand_path('../test_helper', __FILE__)
-
-class TwitterTest < Test::Unit::TestCase
- def setup
- super
- set = Pod::Source.search(Pod::Dependency.new('JSONKit'))
- set.stubs(:required_version).returns(Pod::Version.new('1.4'))
- @pod = Pod::UI::UIPod.new(set)
- end
-
- def test_it_creates_a_tweet_for_a_new_tweet
- Twitter.expects(:update).with('[JSONKit] A Very High Performance Objective-C JSON Library. https://github.com/johnezang/JSONKit')
- CocoaPodsNotifier::Twitter.tweet(@pod)
- end
-
- def test_it_truncates_the_summary_if_the_complete_text_would_be_over_140_chars
- @pod.stubs(:summary).returns('This is a message which is waaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaay ' \
- 'too long and will get truncated here (this will be omitted).')
-
- expected = '[JSONKit] This is a message which is waaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaay ' \
- 'too long and will get truncated here… https://github.com/johnezang/JSONKit'
-
- # This is to correctly count (Unicode) chars on Ruby 1.8 as well.
- char_size = expected.unpack("U*").size
-
- # This is because the link can be any size, but will be shortened to 21 chars (because it's https).
- assert_equal 140, (char_size - @pod.homepage.size + 21)
-
- Twitter.expects(:update).with(expected)
- CocoaPodsNotifier::Twitter.tweet(@pod)
- end
-
- def perfrom_trailing_string_truncation_test (tested_string)
- name = '[JSONKit] '
- link = 'https://github.com/johnezang/JSONKit'
- link_length = 21 # expected shortened https link length
- ellipsis = '' # notice the space
- x_length = 140 -
- tested_string.length -
- name.length -
- link_length -
- 2 # ellipsis
-
- summary = 'x' * x_length + "#{tested_string} truncated part"
- status = name + 'x' * x_length + ellipsis + link
-
- @pod.stubs(:summary).returns(summary)
- Twitter.expects(:update).with(status)
- CocoaPodsNotifier::Twitter.tweet(@pod)
- end
-
- def test_it_removes_trailing_spaces_in_truncation
- perfrom_trailing_string_truncation_test ' '
- end
-
- def test_it_removes_trailing_comas_in_truncation
- perfrom_trailing_string_truncation_test ','
- end
-
- def test_it_removes_trailing_dots_in_truncation
- perfrom_trailing_string_truncation_test '.'
- end
-end
1 vendor/CocoaPods
@@ -1 +0,0 @@
-Subproject commit fd03adf9c9c6fb5da4ca87168ce709fe18078f74
1 vendor/Xcodeproj
@@ -1 +0,0 @@
-Subproject commit bcc376caa182da42a563cde2edf6b35be3cbb7a2

0 comments on commit 2e12d8d

Please sign in to comment.
Something went wrong with that request. Please try again.