flicket 0.1.0

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 55f142070269cb20ed163e5ca4f93af5c8e152b5
4
+ data.tar.gz: 6d5631bc63d86b81e38629600a93e2afe7143f7d
5
+ SHA512:
6
+ metadata.gz: 965bc8261f3e9786ee71abb968d4d0d68f05a64f84700861c8d12f97da5b7629011fdb296c3a540eb85cc039c7f09a81ac8da1be063ddce623b3c7f74ec9ad56
7
+ data.tar.gz: fe4a382b6b425cdf17f89bee9299e9a9258d5db60bf84cefb2ca1512be5ebedc40ddec914ecd0885b7b480cde9e4356fcb151ba86c4526edc898848ace9df736
@@ -0,0 +1,10 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ .tags*
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
@@ -0,0 +1 @@
1
+ flicket
@@ -0,0 +1 @@
1
+ 2.3.0
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.3.0
4
+ before_install: gem install bundler -v 1.11.2
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in flicket.gemspec
4
+ gemspec
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 Madis Nõmme
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,72 @@
1
+ # Flicket
2
+
3
+ ## About
4
+
5
+ Flicket is a ruby command line tool that:
6
+
7
+ * accepts a list of search keywords as arguments
8
+ * queries the Flickr API for the top-rated image for each keyword
9
+ * downloads the results
10
+ * crops them rectangularly
11
+ * assembles a collage grid from ten images and
12
+ * writes the result to a user-supplied filename
13
+
14
+ If given less than ten keywords, or if any keyword fails to
15
+ result in a match, it retrieves random words from a dictionary
16
+ source such as `/usr/share/dict/words`. Repeating as necessary
17
+ until you have gathered ten images.
18
+
19
+ ## Installation
20
+
21
+ ```
22
+ git clone https://github.com/madis/flicket.git
23
+ cd flicket
24
+ bundle install
25
+ rake build
26
+ gem install pkg/flicket.gem
27
+ ```
28
+
29
+ ## Usage
30
+
31
+ 1. Obtain flickr API key from https://www.flickr.com/services/apps/create/apply
32
+ 2. Export them as environment variables
33
+ ```bash
34
+ export FLICKR_KEY=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx # Replace with your key
35
+ export FLICKR_SECRET=xxxxxxxxxxxxxxxx # Replace with your secret
36
+ ```
37
+
38
+ 3. After building and installing the gem: `flicket [keyword ...]`
39
+ 4. Or inside the gem's source folder (after installing dependencies with `bundle install`): `./bin/flicket [keyword ...]`
40
+
41
+ > NB! Output filename can be provided with `--output or -o` e.g. `flicket -o mycollage.png tree river bratwurst`
42
+
43
+ ## Development
44
+
45
+ Most of the code is covered with tests using RSpec. [VCR](https://github.com/vcr/vcr) is used to avoid hitting Flickr api every time and to speed up tests.
46
+
47
+ [ImageMagick](http://www.imagemagick.org/) must be installed too. On OS X it can be done with Homebrew `brew install imagemagick`.
48
+
49
+
50
+ 1. Clone the repo `git clone https://github.com/madis/flicket`
51
+ 2. Install dependencies `cd flicket && bundle install`
52
+ 3. Run tests `rspec`
53
+
54
+
55
+ ## Layouts
56
+
57
+ Layouts can be defined in LayoutCalculator. Example layout is defined.
58
+
59
+ ```ruby
60
+ LAYOUT = [
61
+ [0,1,2,3],
62
+ [4,5,6,3],
63
+ [4,7,8,9]
64
+ ]
65
+ ```
66
+
67
+ ![10 image layout](docs/layout-10.jpg)
68
+
69
+
70
+ The numbers in the matrix are *cell names*. To mark bigger cell, e.g. cell **4** that is double the height of single cell, use same name in consequtive rows. Layout calculator will crop and resize automatically
71
+
72
+ > NB The LayoutCalculator may not handle more complex layouts correctly in its initial incarnation. It is mainly here as a starting point and a direction for future development.
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "flicket"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $LOAD_PATH << './lib'
4
+
5
+ require 'flicket'
6
+
7
+ Flicket::CLI.start(ARGV)
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
Binary file
@@ -0,0 +1,30 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'flicket/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "flicket"
8
+ spec.version = Flicket::VERSION
9
+ spec.authors = ["Madis Nõmme"]
10
+ spec.email = ["madis.nomme@gmail.com"]
11
+
12
+ spec.summary = %q{Command line tool to create collages of flickr images}
13
+ spec.homepage = "http://github.com/madis/flicket"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
17
+ spec.bindir = "bin"
18
+ spec.executables = "flicket"
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency 'flickraw', '0.9.8'
22
+ spec.add_dependency 'mini_magick', '~>4.5.1'
23
+ spec.add_development_dependency 'awesome_print', '~>1.7'
24
+ spec.add_development_dependency 'pry', '~>0.10.3'
25
+ spec.add_development_dependency 'vcr', '~>3.0.3'
26
+ spec.add_development_dependency 'webmock', '~>2.1.0'
27
+ spec.add_development_dependency "bundler", "~> 1.11"
28
+ spec.add_development_dependency "rake", "~> 10.0"
29
+ spec.add_development_dependency "rspec", "~> 3.0"
30
+ end
@@ -0,0 +1,76 @@
1
+ require 'ostruct'
2
+ require 'flickraw'
3
+ require 'optparse'
4
+
5
+ require "flicket/version"
6
+ require 'flicket/environment'
7
+ require 'flicket/app'
8
+ require 'flicket/flicker_query'
9
+ require 'flicket/downloader'
10
+ require 'flicket/keyword_source'
11
+ require 'flicket/collage_maker'
12
+ require 'flicket/storage'
13
+
14
+ module Flicket
15
+
16
+ class CLI
17
+ def self.start(args)
18
+ puts "Flicket #{VERSION}\n"
19
+ keywords, options = parse_args(args)
20
+ puts "Got keywords: #{keywords} options: #{options}"
21
+ puts "Using initial keywords #{keywords.join(' ')}"
22
+ env = setup_env(keywords, options)
23
+ App.new(env).call
24
+ end
25
+
26
+ def self.parse_args(args)
27
+ options = {}
28
+ parser = OptionParser.new do |opts|
29
+ opts.banner = "Usage: flicket [options] [keywords...]"
30
+ opts.on("-o", "--output [FILENAME]", "Output filename to store collage") do |o|
31
+ options[:output] = o
32
+ end
33
+ end
34
+ parser.parse!(args)
35
+ keywords = args
36
+ [keywords, options]
37
+ end
38
+
39
+ def self.setup_env(keywords, options={})
40
+ flicker_query = setup_flicker_query
41
+ keyword_source = setup_keyword_source
42
+ keyword_source.add_user_words keywords
43
+
44
+ downloader = Downloader.new
45
+ collage_maker = CollageMaker.new
46
+ storage = Storage.new
47
+ storage.output_filename = options[:output]
48
+ Environment.new(
49
+ flicker_query: flicker_query,
50
+ downloader: downloader,
51
+ keyword_source: keyword_source,
52
+ collage_maker: collage_maker,
53
+ storage: storage)
54
+ end
55
+
56
+ def self.setup_keyword_source(dictionary='/usr/share/dict/words')
57
+ if File.exist?(dictionary)
58
+ keyword_source = KeywordSource.new(dictionary)
59
+ else
60
+ STDERR.puts "Exiting as dictionary not found at #{dictionary}"
61
+ exit 1
62
+ end
63
+ end
64
+
65
+ def self.setup_flicker_query(key=ENV['FLICKR_KEY'], secret=ENV['FLICKR_SECRET'])
66
+ if key != nil && secret != nil
67
+ FlickRaw.api_key = key
68
+ FlickRaw.shared_secret = secret
69
+ FlickerQuery.build(FlickRaw::Flickr.new)
70
+ else
71
+ STDERR.puts "Exiting because environment variables FLICKR_KEY and FLICKR_SECRET were not set"
72
+ exit 1
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,42 @@
1
+ module Flicket
2
+ class App
3
+ MAX_KEYWORD_RETRIES = 10
4
+
5
+ class ApplicationError < StandardError; end
6
+
7
+ attr_reader :env
8
+
9
+ def initialize(env)
10
+ @env = env
11
+ end
12
+
13
+ def call
14
+ image_paths = []
15
+ image_paths << obtain_image while image_paths.count < 10
16
+ make_collage(image_paths)
17
+ rescue ApplicationError => e
18
+ puts "Application could not continue. #{e.message}"
19
+ end
20
+
21
+ def obtain_image
22
+ tries ||= MAX_KEYWORD_RETRIES
23
+
24
+ keyword = env.keyword_source.call
25
+ image_url = env.flicker_query.call(keyword)
26
+ downloaded_image = env.downloader.call(image_url)
27
+ rescue NoImageForKeyword => e
28
+ puts "Image not found for keyword '#{keyword}'. Message: #{e.message}. Retrying"
29
+ if (tries -= 1) > 0
30
+ retry
31
+ else
32
+ raise ApplicationError, "Failed getting image after retrying #{MAX_KEYWORD_RETRIES} times"
33
+ end
34
+ rescue DownloadError => e
35
+ raise ApplicationError, e.message
36
+ end
37
+
38
+ def make_collage(images)
39
+ env.storage.call(env.collage_maker.call(images))
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,50 @@
1
+ require 'mini_magick'
2
+ require 'flicket/layout_calculator'
3
+
4
+ module Flicket
5
+ class CollageMaker
6
+ attr_reader :layout
7
+
8
+ def initialize(layout=LayoutCalculator.new)
9
+ @layout = layout
10
+ end
11
+
12
+ def call(image_paths)
13
+ images = image_paths.map { |p| MiniMagick::Image.open p.path }
14
+ min_height = images.min_by(&:height).height
15
+ min_width = images.min_by(&:width).width
16
+ canvas_tempfile = Tempfile.new ['collage_maker', '.png']
17
+ canvas = create_new_image(path: canvas_tempfile.path)
18
+
19
+ resized_images = image_paths.map.with_index do |path, idx|
20
+ image = MiniMagick::Image.open path.path
21
+ image.crop(layout.crop_rect(idx, image.width, image.height).to_minimagick)
22
+ image.resize(layout.resize_rect(idx, image.width, image.height).to_minimagick)
23
+
24
+ add_image_to_canvas(image, layout.cell_rect(idx), canvas_tempfile)
25
+ end
26
+ canvas_tempfile
27
+ end
28
+
29
+ private
30
+
31
+ def add_image_to_canvas(image, rect, canvas_tempfile)
32
+ canvas = MiniMagick::Image.new canvas_tempfile.path
33
+ result = canvas.composite(image) do |c|
34
+ puts "Adding image to collage #{image}"
35
+ c.compose 'Over'
36
+ c.geometry rect.to_minimagick
37
+ end
38
+ result.write canvas_tempfile.path
39
+ canvas_tempfile.rewind
40
+ end
41
+
42
+ def create_new_image(width:layout.width, height:layout.height, path:)
43
+ MiniMagick::Tool::Convert.new do |i|
44
+ i.size "#{width}x#{height}"
45
+ i.xc "white"
46
+ i << path
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,24 @@
1
+ require 'open-uri'
2
+ require 'tempfile'
3
+
4
+ module Flicket
5
+ class DownloadError < StandardError; end
6
+
7
+ class Downloader
8
+ def call(url)
9
+ download_to_temp_file(url)
10
+ rescue OpenURI::HTTPError => e
11
+ raise DownloadError, "Unable to download from #{url} #{e.message}"
12
+ end
13
+
14
+ private
15
+
16
+ def download_to_temp_file(url)
17
+ tempfile = Tempfile.new 'flicket-image'
18
+ IO.copy_stream(open(url, read_timeout: 5), tempfile)
19
+ tempfile.rewind
20
+ puts "Downloaded to #{tempfile.path}"
21
+ tempfile
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,13 @@
1
+ module Flicket
2
+ class Environment
3
+ attr_reader :flicker_query, :downloader, :keyword_source, :collage_maker, :storage
4
+
5
+ def initialize(params={})
6
+ @flicker_query = params.fetch(:flicker_query)
7
+ @downloader = params.fetch(:downloader)
8
+ @keyword_source = params.fetch(:keyword_source)
9
+ @collage_maker = params.fetch(:collage_maker)
10
+ @storage = params.fetch(:storage)
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,59 @@
1
+ module Flicket
2
+ class NoImageForKeyword < StandardError; end
3
+ class KeywordMissing < StandardError; end
4
+
5
+ class BaseCommand
6
+ attr_reader :api
7
+
8
+ def initialize(flickraw)
9
+ @api = flickraw
10
+ end
11
+ end
12
+
13
+ class SearchCommand < BaseCommand
14
+ def call(text)
15
+ api.photos.search({text: text, sort: 'interestingness-desc', per_page: 10})
16
+ end
17
+ end
18
+
19
+ class InfoCommand < BaseCommand
20
+ def call(photo_id)
21
+ api.photos.getInfo(photo_id: photo_id)
22
+ end
23
+ end
24
+
25
+ class SizesCommand < BaseCommand
26
+ def call(photo_id)
27
+ api.photos.getSizes(photo_id: photo_id)
28
+ end
29
+ end
30
+
31
+ class FlickerQuery
32
+ def self.build(flickraw)
33
+ search_command = SearchCommand.new(flickraw)
34
+ sizes_command = SizesCommand.new(flickraw)
35
+ new(search_command: search_command, sizes_command: sizes_command)
36
+ end
37
+
38
+ def initialize(search_command:, sizes_command:)
39
+ @search_command = search_command
40
+ @sizes_command = sizes_command
41
+ end
42
+
43
+ def call(keyword)
44
+ raise KeywordMissing, "Search keyword is required (can't be nil)" if keyword.nil?
45
+ puts "Searching images for keyword '#{keyword}'"
46
+ results = @search_command.call(keyword)
47
+ raise NoImageForKeyword if results.count == 0
48
+ get_image_url(results.first['id'])
49
+ end
50
+
51
+ private
52
+
53
+ def get_image_url(photo_id)
54
+ sizes = @sizes_command.call(photo_id)
55
+ middle = sizes.count / 2 # Will pick middle sized image from the ones available
56
+ sizes[middle]['source']
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,31 @@
1
+ module Flicket
2
+ class KeywordSource
3
+ def initialize(dictionary_path)
4
+ @dictionary_path = dictionary_path
5
+ @user_words = []
6
+ end
7
+
8
+ def call
9
+ next_user_word || random_dictionary_word
10
+ end
11
+
12
+ def add_user_words(words)
13
+ @user_words += words
14
+ end
15
+
16
+ private
17
+
18
+ def next_user_word
19
+ @user_words.shift
20
+ end
21
+
22
+ # Based on http://goo.gl/HbJbR2
23
+ def random_dictionary_word
24
+ chosen_line = nil
25
+ File.foreach(@dictionary_path).each_with_index do |line, number|
26
+ chosen_line = line if rand < 1.0 / (number + 1)
27
+ end
28
+ chosen_line.strip
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,101 @@
1
+ require 'flicket/rectangle'
2
+
3
+ module Flicket
4
+ class LayoutCalculator
5
+ WIDTH = 2304
6
+ HEIGHT = 1440
7
+ LAYOUT = [
8
+ [0,1,2,3],
9
+ [4,5,6,3],
10
+ [4,7,8,9]
11
+ ]
12
+
13
+ def rows
14
+ LAYOUT.size
15
+ end
16
+
17
+ def columns
18
+ LAYOUT.max_by(&:length).length
19
+ end
20
+
21
+ def row_cells
22
+ LAYOUT
23
+ end
24
+
25
+ def column_cells
26
+ LAYOUT.transpose
27
+ end
28
+
29
+ def cell_width(name)
30
+ size_multiplier = repeated_matches(LAYOUT, name)
31
+ calculate_cell_size(WIDTH, columns, size_multiplier)
32
+ end
33
+
34
+ def cell_height(name)
35
+ size_multiplier = repeated_matches(column_cells, name)
36
+ calculate_cell_size(HEIGHT, rows, size_multiplier)
37
+ end
38
+
39
+ def crop_rect(cell_name, image_width, image_height)
40
+ image = Rectangle.new(image_width, image_height)
41
+ cell = cell_rect(cell_name)
42
+
43
+ target_x = 0
44
+ target_y = 0
45
+
46
+ if image.aspect_ratio >= cell.aspect_ratio # image is wider, make it narrower
47
+ crop_size = image.width - (image.height * cell.aspect_ratio)
48
+ target_width = image_width - crop_size
49
+ target_height = image.height
50
+ else # image is narrower, cut it shorter (thus it becoming relatively wider)
51
+ crop_size = image.height - (image.width / cell.aspect_ratio)
52
+ target_width = image.width
53
+ target_height = image.height - crop_size
54
+ end
55
+
56
+ crop_rect = Rectangle.new(target_x, target_y, target_width, target_height)
57
+ crop_rect
58
+ end
59
+
60
+ def resize_rect(cell_name, image_width, image_height)
61
+ image = Rectangle.new(image_width, image_height)
62
+ cell_rect(cell_name)
63
+ end
64
+
65
+ def cell_rect(name)
66
+ row, column = cell_coordinates(name)
67
+ width = WIDTH / columns
68
+ height = HEIGHT / rows
69
+
70
+ Rectangle.new(column*width, row*height, cell_width(name), cell_height(name))
71
+ end
72
+
73
+ def height
74
+ HEIGHT
75
+ end
76
+
77
+ def width
78
+ WIDTH
79
+ end
80
+
81
+ private
82
+
83
+ def cell_coordinates(name)
84
+ row = LAYOUT.find_index {|row| row.include? name }
85
+ column = LAYOUT.transpose.find_index {|row| row.include? name }
86
+ [row, column]
87
+ end
88
+
89
+ def repeated_matches(table, name)
90
+ wideness_multiplier = table.map {|row| row.count(name)}.max
91
+ end
92
+
93
+ def calculate_cell_size(total, divisions, multiplier)
94
+ if multiplier == 0
95
+ 0
96
+ else
97
+ (total / divisions) * multiplier
98
+ end
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,38 @@
1
+ module Flicket
2
+ class Rectangle
3
+ attr_reader :x, :y, :width, :height
4
+
5
+ def initialize(x=0, y=0, width, height)
6
+ @x = x
7
+ @y = y
8
+ @width = width
9
+ @height = height
10
+ end
11
+
12
+ def aspect_ratio
13
+ width.to_f / height
14
+ end
15
+
16
+ def landscape?(width, height)
17
+ aspect_ratio >= 1
18
+ end
19
+
20
+ def portrait?(width, height)
21
+ !landscape?
22
+ end
23
+
24
+ def to_minimagick
25
+ "#{width}x#{height}+#{x}+#{y}"
26
+ end
27
+
28
+ def ==(o)
29
+ o.class == self.class && o.state == state
30
+ end
31
+
32
+ protected
33
+
34
+ def state
35
+ [@x, @y, @width, @height]
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,38 @@
1
+ require 'fileutils'
2
+ require 'date'
3
+
4
+ module Flicket
5
+ class Storage
6
+
7
+ attr_accessor :output_filename
8
+
9
+ def call(file_path)
10
+ destination = collage_filename
11
+ FileUtils.mv file_path, destination
12
+ puts "Finished collage is available at #{destination}"
13
+ end
14
+
15
+ private
16
+
17
+ def collage_filename
18
+ output_filename || ask_filename_from_user
19
+ end
20
+
21
+ def ask_filename_from_user
22
+ default = dated_filename
23
+ puts "Enter file name for collage (press ENTER to use '#{default}')"
24
+ filename = STDIN.gets.strip
25
+ if filename.empty?
26
+ default
27
+ else
28
+ filename
29
+ end
30
+ end
31
+
32
+ def dated_filename
33
+ now = Date.new
34
+ timestamp = now.strftime('%Y%m%d%H%M%S')
35
+ "collage-#{timestamp}.png"
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,3 @@
1
+ module Flicket
2
+ VERSION = "0.1.0"
3
+ end
metadata ADDED
@@ -0,0 +1,196 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: flicket
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Madis Nõmme
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-06-21 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: flickraw
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '='
18
+ - !ruby/object:Gem::Version
19
+ version: 0.9.8
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '='
25
+ - !ruby/object:Gem::Version
26
+ version: 0.9.8
27
+ - !ruby/object:Gem::Dependency
28
+ name: mini_magick
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 4.5.1
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 4.5.1
41
+ - !ruby/object:Gem::Dependency
42
+ name: awesome_print
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.7'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.7'
55
+ - !ruby/object:Gem::Dependency
56
+ name: pry
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 0.10.3
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 0.10.3
69
+ - !ruby/object:Gem::Dependency
70
+ name: vcr
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 3.0.3
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 3.0.3
83
+ - !ruby/object:Gem::Dependency
84
+ name: webmock
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: 2.1.0
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: 2.1.0
97
+ - !ruby/object:Gem::Dependency
98
+ name: bundler
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '1.11'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '1.11'
111
+ - !ruby/object:Gem::Dependency
112
+ name: rake
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '10.0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '10.0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: rspec
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: '3.0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: '3.0'
139
+ description:
140
+ email:
141
+ - madis.nomme@gmail.com
142
+ executables:
143
+ - flicket
144
+ extensions: []
145
+ extra_rdoc_files: []
146
+ files:
147
+ - ".gitignore"
148
+ - ".rspec"
149
+ - ".ruby-gemset"
150
+ - ".ruby-version"
151
+ - ".travis.yml"
152
+ - Gemfile
153
+ - LICENSE.txt
154
+ - README.md
155
+ - Rakefile
156
+ - bin/console
157
+ - bin/flicket
158
+ - bin/setup
159
+ - docs/layout-10.jpg
160
+ - flicket.gemspec
161
+ - lib/flicket.rb
162
+ - lib/flicket/app.rb
163
+ - lib/flicket/collage_maker.rb
164
+ - lib/flicket/downloader.rb
165
+ - lib/flicket/environment.rb
166
+ - lib/flicket/flicker_query.rb
167
+ - lib/flicket/keyword_source.rb
168
+ - lib/flicket/layout_calculator.rb
169
+ - lib/flicket/rectangle.rb
170
+ - lib/flicket/storage.rb
171
+ - lib/flicket/version.rb
172
+ homepage: http://github.com/madis/flicket
173
+ licenses:
174
+ - MIT
175
+ metadata: {}
176
+ post_install_message:
177
+ rdoc_options: []
178
+ require_paths:
179
+ - lib
180
+ required_ruby_version: !ruby/object:Gem::Requirement
181
+ requirements:
182
+ - - ">="
183
+ - !ruby/object:Gem::Version
184
+ version: '0'
185
+ required_rubygems_version: !ruby/object:Gem::Requirement
186
+ requirements:
187
+ - - ">="
188
+ - !ruby/object:Gem::Version
189
+ version: '0'
190
+ requirements: []
191
+ rubyforge_project:
192
+ rubygems_version: 2.5.1
193
+ signing_key:
194
+ specification_version: 4
195
+ summary: Command line tool to create collages of flickr images
196
+ test_files: []