flico 0.0.2 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: d21b66c84738f5f1ef760829af88cf4f40d7c8fb
4
- data.tar.gz: b27c22ad9028030c2be42e66b9d00a7f42534811
2
+ SHA256:
3
+ metadata.gz: 473a26ece9ff8cf869ea977bdc1a96a5b800848ce82f4ee0282dad74bda96bdc
4
+ data.tar.gz: b4e83f8d45ea19e79e4d5884603f3941af769e279d8267dff9379f662f3acbbf
5
5
  SHA512:
6
- metadata.gz: affd36e436da1a851f4e3d43382b8530d17c8db550d220bf89063567076cbb61d87c69e4068ce5bd4dc4b0492abd29fe0a570c50a85c8935844d911d66ce7a3c
7
- data.tar.gz: 9186a05a4a81b5f15eea86c955eb8e92b1e6061bf7ae84fa401393e07c12a26b30df7c8a2133cdbab9264d8ff22b3375c3987f41109025f7ddb8c72565a7f928
6
+ metadata.gz: 55a33bfccc1180547b859c572ef4802f4bd6b6299298319e0b21519ae381b4f040b0620ffbde06e002c9d9216d6f00b559af148aa38b93d9968b10464351c918
7
+ data.tar.gz: c96edd9e612f9cb0dbc1711c0d0dedf5ade8be1b1e685316055cd4d48b3bbda9bbdc6c2b8aa8eea6073a9da88f10d8589b9f1c4bfad9db033d7d20664ec3ab7a
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- 2.3.0
1
+ 3.2.2
data/Gemfile CHANGED
@@ -1,2 +1,4 @@
1
+ # frozen_string_literal: true
2
+
1
3
  source 'https://rubygems.org'
2
4
  gemspec
data/Gemfile.lock CHANGED
@@ -1,33 +1,33 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- flico (0.0.1)
4
+ flico (1.0.0)
5
5
  flickraw
6
6
  mini_magick
7
7
 
8
8
  GEM
9
9
  remote: https://rubygems.org/
10
10
  specs:
11
- diff-lcs (1.3)
12
- flickraw (0.9.9)
13
- mini_magick (4.7.1)
14
- rake (12.0.0)
15
- rspec (3.6.0)
16
- rspec-core (~> 3.6.0)
17
- rspec-expectations (~> 3.6.0)
18
- rspec-mocks (~> 3.6.0)
19
- rspec-core (3.6.0)
20
- rspec-support (~> 3.6.0)
21
- rspec-expectations (3.6.0)
11
+ diff-lcs (1.5.0)
12
+ flickraw (0.9.10)
13
+ mini_magick (4.12.0)
14
+ rake (13.0.6)
15
+ rspec (3.12.0)
16
+ rspec-core (~> 3.12.0)
17
+ rspec-expectations (~> 3.12.0)
18
+ rspec-mocks (~> 3.12.0)
19
+ rspec-core (3.12.2)
20
+ rspec-support (~> 3.12.0)
21
+ rspec-expectations (3.12.3)
22
22
  diff-lcs (>= 1.2.0, < 2.0)
23
- rspec-support (~> 3.6.0)
24
- rspec-mocks (3.6.0)
23
+ rspec-support (~> 3.12.0)
24
+ rspec-mocks (3.12.5)
25
25
  diff-lcs (>= 1.2.0, < 2.0)
26
- rspec-support (~> 3.6.0)
27
- rspec-support (3.6.0)
26
+ rspec-support (~> 3.12.0)
27
+ rspec-support (3.12.1)
28
28
 
29
29
  PLATFORMS
30
- ruby
30
+ arm64-darwin-22
31
31
 
32
32
  DEPENDENCIES
33
33
  bundler
@@ -36,4 +36,4 @@ DEPENDENCIES
36
36
  rspec
37
37
 
38
38
  BUNDLED WITH
39
- 1.15.1
39
+ 2.4.13
data/LICENSE.text CHANGED
@@ -1,6 +1,6 @@
1
1
  The MIT License
2
2
 
3
- Copyright 2017 Amandeep Bhamra
3
+ Copyright 2017 Amandeep Singh Bhamra
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6
6
 
data/Rakefile CHANGED
@@ -1,5 +1,7 @@
1
- require "rspec/core/rake_task"
2
- require "bundler/gem_tasks"
1
+ # frozen_string_literal: true
2
+
3
+ require 'rspec/core/rake_task'
4
+ require 'bundler/gem_tasks'
3
5
 
4
6
  RSpec::Core::RakeTask.new(:spec)
5
- task :default => :spec
7
+ task default: :spec
data/bin/console CHANGED
@@ -1,7 +1,8 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
2
3
 
3
- require "bundler/setup"
4
- require "flico"
5
- require "irb"
4
+ require 'bundler/setup'
5
+ require 'flico'
6
+ require 'irb'
6
7
 
7
8
  IRB.start
data/bin/flico CHANGED
@@ -1,4 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
2
4
  $LOAD_PATH << './lib'
3
5
  require 'flico'
4
6
  Flico::CommandLineInterface.start(ARGV)
data/flico.gemspec CHANGED
@@ -1,26 +1,26 @@
1
- # coding: utf-8
2
- lib = File.expand_path('../lib', __FILE__)
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path('lib', __dir__)
3
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
5
 
5
6
  Gem::Specification.new do |spec|
6
-
7
- spec.version = '0.0.2'
8
- spec.name = 'flico'
9
- spec.summary = %q{CLI tool to create collage using Flickr}
10
- spec.homepage = "https://github.com/amandeepbhamra/flico"
11
- spec.license = 'MIT'
7
+ spec.name = 'flico'
8
+ spec.version = '2.0.0'
9
+ spec.summary = 'A CLI tool to create collage from keywords using Flickr'
10
+ spec.homepage = 'https://github.com/amandeepbhamra/flico'
11
+ spec.license = 'MIT'
12
+
13
+ spec.authors = ['Amandeep Singh Bhamra']
14
+ spec.email = ['amandeep.bhamra@gmail.com']
12
15
 
13
- spec.authors = ['Amandeep Bhamra']
14
- spec.email = ["amandeep.bhamra@gmail.com"]
15
-
16
- spec.files = `git ls-files -z`.split("\x0")
17
- spec.bindir = 'bin'
18
- spec.executables = 'flico'
19
- spec.require_paths = ['lib']
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.bindir = 'bin'
18
+ spec.executables = 'flico'
19
+ spec.require_paths = ['lib']
20
20
 
21
- spec.add_dependency 'flickraw'
22
- spec.add_dependency 'mini_magick'
23
- spec.add_development_dependency "bundler"
24
- spec.add_development_dependency "rake"
25
- spec.add_development_dependency "rspec"
21
+ spec.add_dependency 'flickraw', '~> 0.9.10'
22
+ spec.add_dependency 'mini_magick', '~> 4.12'
23
+ spec.add_development_dependency 'bundler', '~> 2.4', '>= 2.4.14'
24
+ spec.add_development_dependency 'rake', '~> 13.0', '>= 13.0.6'
25
+ spec.add_development_dependency 'rspec', '~> 3.12'
26
26
  end
data/lib/flico/app.rb CHANGED
@@ -1,45 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'flico/dictionary'
4
+ require 'flico/collager'
5
+ require 'flico/image_file'
6
+
1
7
  module Flico
8
+ class ApplicationError < StandardError; end
9
+
10
+ class App
11
+ KEYWORDS_COUNT = 10
12
+
13
+ attr_reader :keywords, :options
14
+
15
+ def initialize(keywords, options)
16
+ @keywords = add_missing_keywords(keywords)
17
+ @options = options
18
+ end
19
+
20
+ def create
21
+ options[:file_name] = prepare_file_name(options)
22
+
23
+ Collager.new(prepare_images, options[:file_name]).save
24
+
25
+ puts "Flicollage saved at #{options[:file_name]}"
26
+ end
27
+
28
+ def prepare_images
29
+ keywords.map { |keyword| ImageFile.new(keyword).fetch_from_flickr }
30
+ end
31
+
32
+ private
33
+
34
+ def add_missing_keywords(words)
35
+ words_count = words.size
36
+ return words if words_count == KEYWORDS_COUNT
37
+
38
+ words += Dictionary.new.words(KEYWORDS_COUNT - words_count)
39
+ puts "KEYWORDS: #{words}"
40
+ words
41
+ end
2
42
 
3
- class ApplicationError < StandardError; end
4
- class KeywordMissing < StandardError; end
5
- class NoImage < StandardError; end
6
- class FetchingError < StandardError; end
7
-
8
- class App
9
-
10
- attr_reader :resources
11
-
12
- def initialize(resources)
13
- @resources = resources
14
- end
15
-
16
- def create_collage
17
- image_urls = []
18
- begin
19
- image_urls.push get_images
20
- end while image_urls.count < 10
21
- collage(image_urls)
22
- end
23
-
24
- def collage(images)
25
- resources.save_collage.call(resources.collager.call(images))
26
- end
27
-
28
- def get_images
29
- keywords_count = 10
30
- keyword = resources.dictionary.call
31
- image_url = resources.flickr_api.call(keyword)
32
- downloaded_image = resources.fetch_image.call(image_url)
33
- rescue NoImage => e
34
- puts "Image not found for keyword '#{keyword}'. Message: #{e.message}. Retrying"
35
- unless (tries -= 1) > 0
36
- raise ApplicationError, "Failed getting image after retrying #{MAX_KEYWORD_RETRIES} times"
37
- else
38
- retry
39
- end
40
- rescue FetchingError => e
41
- raise ApplicationError, e.message
42
- end
43
- end
43
+ def prepare_file_name(options)
44
+ return options[:file_name] unless options[:file_name].nil?
44
45
 
46
+ puts 'Enter file name for collage (press ENTER to use default)'
47
+ file_name = $stdin.gets.strip
48
+ file_name.empty? ? "flicollage-#{Time.now.strftime('%Y%m%d%H%M%S')}.png" : file_name
49
+ end
50
+ end
45
51
  end
@@ -1,45 +1,25 @@
1
- require 'flico/grid'
1
+ # frozen_string_literal: true
2
+
2
3
  require 'mini_magick'
3
4
 
4
5
  module Flico
5
-
6
- class Collager
7
- attr_reader :grid
8
-
9
- def initialize(grid=Grid.new)
10
- @grid = grid
11
- end
12
-
13
- def call(image_urls)
14
- images = image_urls.map { |p| MiniMagick::Image.open p.path }
15
- temp_file = Tempfile.new ['collage_maker', '.png']
16
- canvas = MiniMagick::Tool::Convert.new do |i|
17
- i.size "#{grid.canv_width}x#{grid.canv_height}"
18
- i.xc "white"
19
- i << temp_file.path
20
- end
21
-
22
- resized_images = image_urls.map.with_index do |path, idx|
23
- image = MiniMagick::Image.open path.path
24
- image.crop(grid.crop_rectangle(idx, image.width, image.height).to_mm)
25
- image.resize(grid.resize_rectangle(idx, image.width, image.height).to_mm)
26
- print_to_canvas(image, grid.cell_rectangle(idx), temp_file)
27
- end
28
- temp_file
29
- end
30
-
31
- private
6
+ class Collager
7
+ attr_reader :images_url, :file_name
32
8
 
33
- def print_to_canvas(image, rectangle, temp_file)
34
- canvas = MiniMagick::Image.new temp_file.path
35
- result = canvas.composite(image) do |c|
36
- c.compose 'Over'
37
- c.geometry rectangle.to_mm
38
- end
39
- result.write temp_file.path
40
- temp_file.rewind
41
- end
42
-
43
- end
9
+ def initialize(images_url, file_name)
10
+ @images_url = images_url
11
+ @file_name = file_name
12
+ end
44
13
 
14
+ def save
15
+ montage = MiniMagick::Tool::Montage.new
16
+ montage.density '600'
17
+ montage.tile '5X2'
18
+ montage.geometry '+10+20'
19
+ montage.border '5'
20
+ images_url.each { |image_url| montage << image_url }
21
+ montage << file_name
22
+ montage.call
23
+ end
24
+ end
45
25
  end
@@ -1,30 +1,35 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Flico
2
-
3
- class Dictionary
4
-
5
- def initialize(dictionary_path)
6
- @dictionary_path = dictionary_path
7
- @words = []
8
- end
4
+ class Dictionary
5
+ PATH = '/usr/share/dict/words'
6
+ ALPHABETS_LOWER_LIMIT = 3
7
+ ALPHABETS_UPPER_LIMIT = 10
8
+
9
+ def initialize
10
+ @dictionary_path = validate(PATH)
11
+ @words = []
12
+ end
9
13
 
10
- def call
11
- @words.shift || dictionary_word
12
- end
14
+ def words(count)
15
+ @words += pick_words(count)
16
+ end
13
17
 
14
- def append(keywords)
15
- @words += keywords
16
- end
18
+ private
17
19
 
18
- private
20
+ def pick_words(count)
21
+ File.readlines(PATH).select do |word|
22
+ word.size > ALPHABETS_LOWER_LIMIT && word.size < ALPHABETS_UPPER_LIMIT
23
+ end.sample(count).map(&:strip)
24
+ end
19
25
 
20
- def dictionary_word
21
- selected_line = nil
22
- File.foreach(@dictionary_path).each_with_index do |line, number|
23
- selected_line = line if (rand < 1.0) / (number + 1)
24
- end
25
- selected_line.strip
26
- end
27
-
28
- end
26
+ def validate(path)
27
+ unless File.exist?(path)
28
+ warn "Exiting due to missing dictionary at path: #{path}"
29
+ exit 1
30
+ end
29
31
 
32
+ path
33
+ end
34
+ end
30
35
  end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'flickraw'
4
+
5
+ module Flico
6
+ module Flickr
7
+ class BaseApi
8
+ attr_reader :key, :secret, :api
9
+
10
+ def initialize
11
+ @key = ENV['FLICKR_KEY']
12
+ @secret = ENV['FLICKR_SECRET']
13
+ @api = flickraw_api
14
+ end
15
+
16
+ def get_photo(keyword)
17
+ valid_keyword?(keyword)
18
+
19
+ puts "Searching images for keyword: #{keyword}"
20
+
21
+ results = search_api(keyword)
22
+
23
+ results.count.zero? ? (warn "No Image for keyword: #{keyword}") : photo_url(results.first.id)
24
+ end
25
+
26
+ private
27
+
28
+ def flickraw_api
29
+ valid_secrets?
30
+
31
+ FlickRaw.api_key = key
32
+ FlickRaw.shared_secret = secret
33
+ FlickRaw::Flickr.new
34
+ end
35
+
36
+ def valid_secrets?
37
+ if key.nil? || secret.nil?
38
+ warn 'Exiting due to missing required environment variables: FLICKR_KEY or FLICKR_SECRET'
39
+ exit 1
40
+ end
41
+
42
+ true
43
+ end
44
+
45
+ def search_api(keyword)
46
+ api.photos.search({ text: keyword, per_page: 10, sort: 'interestingness-desc' })
47
+ end
48
+
49
+ def info_api(image)
50
+ api.photos.getInfo(photo_id: image)
51
+ end
52
+
53
+ def size_api(id)
54
+ api.photos.getSizes(photo_id: id)
55
+ end
56
+
57
+ def photo_url(id)
58
+ size_api(id).find { |v| v['label'] == 'Square' }['source']
59
+ end
60
+
61
+ def valid_keyword?(keyword)
62
+ warn "Missing Keywords (can't be nil)" if keyword.nil?
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'flico/flickr/base_api'
4
+
5
+ module Flico
6
+ class NoImage < StandardError; end
7
+ class FetchingError < StandardError; end
8
+
9
+ class ImageFile
10
+ attr_accessor :keyword
11
+
12
+ def initialize(keyword)
13
+ @keyword = keyword
14
+ end
15
+
16
+ def fetch_from_flickr
17
+ Flickr::BaseApi.new.get_photo(keyword)
18
+ rescue NoImage => e
19
+ puts "Image not found for keyword '#{keyword}'. Message: #{e.message}. Retrying"
20
+ unless (tries -= 1).positive?
21
+ raise ApplicationError, "Failed getting image after retrying #{MAX_KEYWORD_RETRIES} times"
22
+ end
23
+
24
+ retry
25
+ rescue FetchingError => e
26
+ raise ApplicationError, e.message
27
+ end
28
+ end
29
+ end
data/lib/flico.rb CHANGED
@@ -1,79 +1,25 @@
1
- require 'flickraw'
1
+ # frozen_string_literal: true
2
+
2
3
  require 'ostruct'
3
4
  require 'optparse'
4
5
  require 'flico/app'
5
- require 'flico/flickr_command'
6
- require 'flico/dictionary'
7
- require 'flico/saver'
8
- require 'flico/collager'
9
6
 
10
7
  module Flico
11
-
12
- class CommandLineInterface
13
-
14
- def self.parse_args(args)
15
- options = {}
16
- OptionParser.new do |opts|
17
- opts.banner = "Usage: flico [options] [10 keywords with space as delimiter]"
18
- opts.on("-f", "--file_name [FileName]", "Collage FileName") do |f|
19
- options[:file_name] = f
20
- end
21
- end.parse!(args)
22
- [args, options]
23
- end
24
-
25
- def self.start(args)
26
- keywords, options = parse_args(args)
27
- puts "Given Keywords: #{keywords.join(' ')} with Options: #{options}"
28
- resources = validate_resources(keywords, options)
29
- App.new(resources).create_collage
30
- end
31
-
32
- def self.validate_resources(keywords, options={})
33
- dictionary = validate_dictionary
34
- dictionary.append(keywords)
35
- col = SaveCollage.new
36
- col.output_file_name = options[:file_name]
37
-
38
- Resource.new(flickr_api: validate_flickr_api, dictionary: dictionary, fetch_image: FetchImage.new, collager: Collager.new, save_collage: col)
39
- end
40
-
41
- def self.validate_flickr_api
42
- key, secret = ENV['FLICKR_KEY'], ENV['FLICKR_SECRET']
43
- unless key == nil && secret == nil
44
- FlickRaw.api_key = key
45
- FlickRaw.shared_secret = secret
46
- FlickrCommand.setup(FlickRaw::Flickr.new)
47
- else
48
- STDERR.puts "Performing AutoExit due to missing required environment variables: FLICKR_KEY and FLICKR_SECRET"
49
- exit 1
50
- end
51
- end
52
-
53
- def self.validate_dictionary
54
- dictionary_path = '/usr/share/dict/words'
55
- if File.exist?(dictionary_path)
56
- dictionary = Dictionary.new(dictionary_path)
57
- else
58
- STDERR.puts "Performing AutoExit due to missing required dictionary at path: #{dictionary_path}"
59
- exit 1
60
- end
61
- end
62
-
63
- end
64
-
65
- class Resource
66
-
67
- attr_reader :flickr_api, :dictionary, :fetch_image, :collager, :save_collage
68
-
69
- def initialize(params={})
70
- @flickr_api = params.fetch(:flickr_api)
71
- @dictionary = params.fetch(:dictionary)
72
- @fetch_image = params.fetch(:fetch_image)
73
- @collager = params.fetch(:collager)
74
- @save_collage = params.fetch(:save_collage)
75
- end
76
-
77
- end
78
-
8
+ class CommandLineInterface
9
+ def self.parse_args(args)
10
+ options = {}
11
+ OptionParser.new do |opts|
12
+ opts.banner = 'Usage: flico [options] [10 keywords with space as delimiter]'
13
+ opts.on('-f', '--file_name [FileName]', 'Collage FileName') do |f|
14
+ options[:file_name] = f
15
+ end
16
+ end.parse!(args)
17
+ [args, options]
18
+ end
19
+
20
+ def self.start(args)
21
+ keywords, options = parse_args(args)
22
+ App.new(keywords, options).create
23
+ end
24
+ end
79
25
  end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+ require 'flico/flickr/base_api'
5
+
6
+ describe Flico::Flickr::BaseApi do
7
+ let(:photo_url) do
8
+ {
9
+ 'label' => 'Square',
10
+ 'width' => 75,
11
+ 'height' => 75,
12
+ 'source' => 'https://farm3.staticflickr.com/2805/33472607802_42e011bcf5_s.jpg',
13
+ 'url' => 'https://farm3.staticflickr.com/2805/33472607802_42e011bcf5/sizes/sq/',
14
+ 'media' => 'photo'
15
+ }
16
+ end
17
+
18
+ let(:subject) { described_class.new }
19
+
20
+ context 'search success' do
21
+ before do
22
+ ENV['FLICKR_KEY'] = 'FOO'
23
+ ENV['FLICKR_SECRET'] = 'BAR'
24
+ end
25
+
26
+ it 'should return image url for search keyword' do
27
+ allow_any_instance_of(Flico::Flickr::BaseApi).to receive(:flickraw_api).and_return(double('flickraw_api'))
28
+ allow_any_instance_of(Flico::Flickr::BaseApi).to receive(:get_photo).and_return(photo_url)
29
+
30
+ expect(subject.get_photo('Volkswagen Golf in Luxembourg')['source']).to eq('https://farm3.staticflickr.com/2805/33472607802_42e011bcf5_s.jpg')
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+ require 'flico/dictionary'
5
+
6
+ describe Flico::Dictionary do
7
+ let(:subject) { described_class.new }
8
+
9
+ context '#words' do
10
+ let(:count) { 3 }
11
+ let(:words) { subject.words(count) }
12
+
13
+ it 'should not be empty' do
14
+ expect(words).to_not be_empty
15
+ end
16
+
17
+ it 'should return 3 random words' do
18
+ expect(words.count).to eq count
19
+ end
20
+ end
21
+ end
data/spec/spec_helper.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # This file was generated by the `rspec --init` command. Conventionally, all
2
4
  # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
3
5
  # The generated `.rspec` file contains `--require spec_helper` which will cause
@@ -44,57 +46,55 @@ RSpec.configure do |config|
44
46
  # triggering implicit auto-inclusion in groups with matching metadata.
45
47
  config.shared_context_metadata_behavior = :apply_to_host_groups
46
48
 
47
- # The settings below are suggested to provide a good initial experience
48
- # with RSpec, but feel free to customize to your heart's content.
49
- =begin
50
- # This allows you to limit a spec run to individual examples or groups
51
- # you care about by tagging them with `:focus` metadata. When nothing
52
- # is tagged with `:focus`, all examples get run. RSpec also provides
53
- # aliases for `it`, `describe`, and `context` that include `:focus`
54
- # metadata: `fit`, `fdescribe` and `fcontext`, respectively.
55
- config.filter_run_when_matching :focus
56
-
57
- # Allows RSpec to persist some state between runs in order to support
58
- # the `--only-failures` and `--next-failure` CLI options. We recommend
59
- # you configure your source control system to ignore this file.
60
- config.example_status_persistence_file_path = "spec/examples.txt"
61
-
62
- # Limits the available syntax to the non-monkey patched syntax that is
63
- # recommended. For more details, see:
64
- # - http://rspec.info/blog/2012/06/rspecs-new-expectation-syntax/
65
- # - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
66
- # - http://rspec.info/blog/2014/05/notable-changes-in-rspec-3/#zero-monkey-patching-mode
67
- config.disable_monkey_patching!
68
-
69
- # This setting enables warnings. It's recommended, but in some cases may
70
- # be too noisy due to issues in dependencies.
71
- config.warnings = true
72
-
73
- # Many RSpec users commonly either run the entire suite or an individual
74
- # file, and it's useful to allow more verbose output when running an
75
- # individual spec file.
76
- if config.files_to_run.one?
77
- # Use the documentation formatter for detailed output,
78
- # unless a formatter has already been configured
79
- # (e.g. via a command-line flag).
80
- config.default_formatter = "doc"
81
- end
82
-
83
- # Print the 10 slowest examples and example groups at the
84
- # end of the spec run, to help surface which specs are running
85
- # particularly slow.
86
- config.profile_examples = 10
87
-
88
- # Run specs in random order to surface order dependencies. If you find an
89
- # order dependency and want to debug it, you can fix the order by providing
90
- # the seed, which is printed after each run.
91
- # --seed 1234
92
- config.order = :random
93
-
94
- # Seed global randomization in this process using the `--seed` CLI option.
95
- # Setting this allows you to use `--seed` to deterministically reproduce
96
- # test failures related to randomization by passing the same `--seed` value
97
- # as the one that triggered the failure.
98
- Kernel.srand config.seed
99
- =end
49
+ # The settings below are suggested to provide a good initial experience
50
+ # with RSpec, but feel free to customize to your heart's content.
51
+ # # This allows you to limit a spec run to individual examples or groups
52
+ # # you care about by tagging them with `:focus` metadata. When nothing
53
+ # # is tagged with `:focus`, all examples get run. RSpec also provides
54
+ # # aliases for `it`, `describe`, and `context` that include `:focus`
55
+ # # metadata: `fit`, `fdescribe` and `fcontext`, respectively.
56
+ # config.filter_run_when_matching :focus
57
+ #
58
+ # # Allows RSpec to persist some state between runs in order to support
59
+ # # the `--only-failures` and `--next-failure` CLI options. We recommend
60
+ # # you configure your source control system to ignore this file.
61
+ # config.example_status_persistence_file_path = "spec/examples.txt"
62
+ #
63
+ # # Limits the available syntax to the non-monkey patched syntax that is
64
+ # # recommended. For more details, see:
65
+ # # - http://rspec.info/blog/2012/06/rspecs-new-expectation-syntax/
66
+ # # - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
67
+ # # - http://rspec.info/blog/2014/05/notable-changes-in-rspec-3/#zero-monkey-patching-mode
68
+ # config.disable_monkey_patching!
69
+ #
70
+ # # This setting enables warnings. It's recommended, but in some cases may
71
+ # # be too noisy due to issues in dependencies.
72
+ # config.warnings = true
73
+ #
74
+ # # Many RSpec users commonly either run the entire suite or an individual
75
+ # # file, and it's useful to allow more verbose output when running an
76
+ # # individual spec file.
77
+ # if config.files_to_run.one?
78
+ # # Use the documentation formatter for detailed output,
79
+ # # unless a formatter has already been configured
80
+ # # (e.g. via a command-line flag).
81
+ # config.default_formatter = "doc"
82
+ # end
83
+ #
84
+ # # Print the 10 slowest examples and example groups at the
85
+ # # end of the spec run, to help surface which specs are running
86
+ # # particularly slow.
87
+ # config.profile_examples = 10
88
+ #
89
+ # # Run specs in random order to surface order dependencies. If you find an
90
+ # # order dependency and want to debug it, you can fix the order by providing
91
+ # # the seed, which is printed after each run.
92
+ # # --seed 1234
93
+ # config.order = :random
94
+ #
95
+ # # Seed global randomization in this process using the `--seed` CLI option.
96
+ # # Setting this allows you to use `--seed` to deterministically reproduce
97
+ # # test failures related to randomization by passing the same `--seed` value
98
+ # # as the one that triggered the failure.
99
+ # Kernel.srand config.seed
100
100
  end
metadata CHANGED
@@ -1,86 +1,98 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: flico
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
- - Amandeep Bhamra
8
- autorequire:
7
+ - Amandeep Singh Bhamra
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-06-18 00:00:00.000000000 Z
11
+ date: 2023-07-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: flickraw
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ">="
17
+ - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '0'
19
+ version: 0.9.10
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - ">="
24
+ - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '0'
26
+ version: 0.9.10
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: mini_magick
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - ">="
31
+ - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '0'
33
+ version: '4.12'
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - ">="
38
+ - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '0'
40
+ version: '4.12'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: bundler
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '2.4'
45
48
  - - ">="
46
49
  - !ruby/object:Gem::Version
47
- version: '0'
50
+ version: 2.4.14
48
51
  type: :development
49
52
  prerelease: false
50
53
  version_requirements: !ruby/object:Gem::Requirement
51
54
  requirements:
55
+ - - "~>"
56
+ - !ruby/object:Gem::Version
57
+ version: '2.4'
52
58
  - - ">="
53
59
  - !ruby/object:Gem::Version
54
- version: '0'
60
+ version: 2.4.14
55
61
  - !ruby/object:Gem::Dependency
56
62
  name: rake
57
63
  requirement: !ruby/object:Gem::Requirement
58
64
  requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '13.0'
59
68
  - - ">="
60
69
  - !ruby/object:Gem::Version
61
- version: '0'
70
+ version: 13.0.6
62
71
  type: :development
63
72
  prerelease: false
64
73
  version_requirements: !ruby/object:Gem::Requirement
65
74
  requirements:
75
+ - - "~>"
76
+ - !ruby/object:Gem::Version
77
+ version: '13.0'
66
78
  - - ">="
67
79
  - !ruby/object:Gem::Version
68
- version: '0'
80
+ version: 13.0.6
69
81
  - !ruby/object:Gem::Dependency
70
82
  name: rspec
71
83
  requirement: !ruby/object:Gem::Requirement
72
84
  requirements:
73
- - - ">="
85
+ - - "~>"
74
86
  - !ruby/object:Gem::Version
75
- version: '0'
87
+ version: '3.12'
76
88
  type: :development
77
89
  prerelease: false
78
90
  version_requirements: !ruby/object:Gem::Requirement
79
91
  requirements:
80
- - - ">="
92
+ - - "~>"
81
93
  - !ruby/object:Gem::Version
82
- version: '0'
83
- description:
94
+ version: '3.12'
95
+ description:
84
96
  email:
85
97
  - amandeep.bhamra@gmail.com
86
98
  executables:
@@ -102,19 +114,18 @@ files:
102
114
  - flico.gemspec
103
115
  - lib/flico.rb
104
116
  - lib/flico/app.rb
105
- - lib/flico/cell.rb
106
117
  - lib/flico/collager.rb
107
118
  - lib/flico/dictionary.rb
108
- - lib/flico/flickr_command.rb
109
- - lib/flico/grid.rb
110
- - lib/flico/saver.rb
111
- - spec/flickr_command_spec.rb
119
+ - lib/flico/flickr/base_api.rb
120
+ - lib/flico/image_file.rb
121
+ - spec/base_api_spec.rb
122
+ - spec/dictionary_spec.rb
112
123
  - spec/spec_helper.rb
113
124
  homepage: https://github.com/amandeepbhamra/flico
114
125
  licenses:
115
126
  - MIT
116
127
  metadata: {}
117
- post_install_message:
128
+ post_install_message:
118
129
  rdoc_options: []
119
130
  require_paths:
120
131
  - lib
@@ -129,9 +140,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
129
140
  - !ruby/object:Gem::Version
130
141
  version: '0'
131
142
  requirements: []
132
- rubyforge_project:
133
- rubygems_version: 2.5.1
134
- signing_key:
143
+ rubygems_version: 3.4.14
144
+ signing_key:
135
145
  specification_version: 4
136
- summary: CLI tool to create collage using Flickr
146
+ summary: A CLI tool to create collage from keywords using Flickr
137
147
  test_files: []
data/lib/flico/cell.rb DELETED
@@ -1,30 +0,0 @@
1
- module Flico
2
-
3
- class Cell
4
- attr_reader :x, :y, :width, :height
5
-
6
- def initialize(x=0, y=0, width, height)
7
- @x, @y = x, y
8
- @width, @height = width, height
9
- end
10
-
11
- def aspect_ratio
12
- width.to_f / height.to_f
13
- end
14
-
15
- def to_mm
16
- "#{width}x#{height}+#{x}+#{y}"
17
- end
18
-
19
- def ==(o)
20
- o.class == self.class && o.state == state
21
- end
22
-
23
- protected
24
-
25
- def state
26
- [@x, @y, @width, @height]
27
- end
28
- end
29
-
30
- end
@@ -1,58 +0,0 @@
1
- module Flico
2
-
3
- class FlickrCommand
4
-
5
- def initialize(search_command:, sizes_command:)
6
- @search_command = search_command
7
- @sizes_command = sizes_command
8
- end
9
-
10
- def self.setup(flickraw)
11
- new(search_command: SearchCommand.new(flickraw), sizes_command: SizesCommand.new(flickraw))
12
- end
13
-
14
- def call(keyword)
15
- unless keyword.nil?
16
- puts "Searching images for keyword: #{keyword}"
17
- results = @search_command.call(keyword)
18
- unless results.count == 0
19
- get_image_url(results.first['id'])
20
- else
21
- STDERR.puts "No Image for keyword: #{keyword}"
22
- end
23
- else
24
- STDERR.puts "Missing Keywords (can't be nil)"
25
- end
26
- end
27
-
28
- private
29
-
30
- def get_image_url(photo_id)
31
- sizes = @sizes_command.call(photo_id)
32
- mid = sizes.count / 2
33
- sizes[mid]['source']
34
- end
35
-
36
- end
37
-
38
-
39
- class FlickrBase
40
- attr_reader :api
41
- def initialize(flickraw)
42
- @api = flickraw
43
- end
44
- end
45
-
46
- class SearchCommand < FlickrBase
47
- def call(keyword)
48
- api.photos.search({text: keyword, per_page: 10, sort: 'interestingness-desc'})
49
- end
50
- end
51
-
52
- class SizesCommand < FlickrBase
53
- def call(photo_id)
54
- api.photos.getSizes(photo_id: photo_id)
55
- end
56
- end
57
-
58
- end
data/lib/flico/grid.rb DELETED
@@ -1,81 +0,0 @@
1
- require 'flico/cell'
2
-
3
- module Flico
4
-
5
- class Grid
6
-
7
- GRID = [[0,1,1,2], [3,4,5,6], [7,8,8,9]]
8
- CANV_WIDTH = 1750
9
- CANV_HEIGHT = 1250
10
-
11
- def canv_width
12
- CANV_WIDTH
13
- end
14
-
15
- def canv_height
16
- CANV_HEIGHT
17
- end
18
-
19
- def columns
20
- GRID.max_by(&:size).size
21
- end
22
-
23
- def column_cells
24
- GRID.transpose
25
- end
26
-
27
- def rows
28
- GRID.size
29
- end
30
-
31
- def row_cells
32
- GRID
33
- end
34
-
35
- def cell_width(name)
36
- cell_size(CANV_WIDTH, columns, size_multiplier(row_cells, name))
37
- end
38
-
39
- def cell_height(name)
40
- cell_size(CANV_HEIGHT, rows, size_multiplier(column_cells, name))
41
- end
42
-
43
- def crop_rectangle(cell_name, image_width, image_height)
44
- x, y = 0, 0
45
- image = Cell.new(image_width, image_height)
46
- cell = cell_rectangle(cell_name)
47
-
48
- unless image.aspect_ratio >= cell.aspect_ratio
49
- final_width = image.width
50
- final_height = image.height - (image.height - (image.width / cell.aspect_ratio))
51
- else
52
- final_width = image_width - (image.width - (image.height * cell.aspect_ratio))
53
- final_height = image.height
54
- end
55
-
56
- crop_rectangle = Cell.new(x, y, final_width, final_height)
57
- crop_rectangle
58
- end
59
-
60
- def resize_rectangle(cell_name, image_width, image_height)
61
- image = Cell.new(image_width, image_height)
62
- cell_rectangle(cell_name)
63
- end
64
-
65
- def cell_rectangle(name)
66
- row, column = row_cells.find_index {|row| row.include? name }, column_cells.find_index {|row| row.include? name }
67
- Cell.new(column * (CANV_WIDTH / columns), row * (CANV_HEIGHT / rows), cell_width(name), cell_height(name))
68
- end
69
-
70
- private
71
-
72
- def size_multiplier(table, name)
73
- table.map {|row| row.count(name)}.max
74
- end
75
-
76
- def cell_size(total, divisions, multiplier)
77
- (multiplier == 0) ? 0 : ((total / divisions) * multiplier)
78
- end
79
- end
80
-
81
- end
data/lib/flico/saver.rb DELETED
@@ -1,44 +0,0 @@
1
- require 'open-uri'
2
- require 'tempfile'
3
- require 'fileutils'
4
- require 'date'
5
-
6
-
7
- module Flico
8
-
9
- class FetchingError < StandardError; end
10
-
11
- class FetchImage
12
-
13
- def call(url)
14
- tempfile = Tempfile.new 'temp_image'
15
- IO.copy_stream(open(url, read_timeout: 5), tempfile)
16
- tempfile.rewind
17
- tempfile
18
- rescue OpenURI::HTTPError => e
19
- raise FetchingError, "Couldn't download from: #{url} due to #{e.message}"
20
- end
21
-
22
- end
23
-
24
- class SaveCollage
25
-
26
- attr_accessor :output_file_name
27
-
28
- def call(file_path)
29
- file_name = output_file_name || validate_file_name
30
- FileUtils.mv file_path, file_name
31
- puts "Flicollage saved at #{file_name}"
32
- end
33
-
34
- private
35
-
36
- def validate_file_name
37
- puts "Enter file name for collage (press ENTER to use '#{default}')"
38
- file_name = STDIN.gets.strip
39
- file_name.empty? ? "flicollage-#{Time.now.strftime('%Y%m%d%H%M%S')}.png" : file_name
40
- end
41
-
42
- end
43
-
44
- end
@@ -1,24 +0,0 @@
1
- require 'spec_helper'
2
- require 'flico/flickr_command'
3
-
4
- describe Flico::FlickrCommand do
5
-
6
- let(:search_results) {[{"id"=>"33472607802", "owner"=>"78759190@N05", "secret"=>"42e011bcf5", "server"=>"2805", "farm"=>3, "title"=>"Volkswagen Golf in Luxembourg", "ispublic"=>1, "isfriend"=>0, "isfamily"=>0}]}
7
- let(:sizes_results) {
8
- [
9
- {"label"=>"Square", "width"=>75, "height"=>75, "source"=>"https://farm3.staticflickr.com/2805/33472607802_42e011bcf5_s.jpg", "url"=>"https://farm3.staticflickr.com/2805/33472607802_42e011bcf5/sizes/sq/", "media"=>"photo"},
10
- {"label"=>"Large Square", "width"=>"150", "height"=>"150", "source"=>"https://farm3.staticflickr.com/2805/33472607802_42e011bcf5_q.jpg", "url"=>"https://farm3.staticflickr.com/2805/33472607802_42e011bcf5/sizes/q/", "media"=>"photo"},
11
- {"label"=>"Thumbnail", "width"=>"100", "height"=>"66", "source"=>"https://farm3.staticflickr.com/2805/33472607802_42e011bcf5_t.jpg", "url"=>"https://farm3.staticflickr.com/2805/33472607802_42e011bcf5/sizes/t/", "media"=>"photo"}
12
- ]
13
- }
14
- let(:subject) { described_class.new(search_command: search_command, sizes_command: sizes_command) }
15
- let(:search_command) { double('search', call: search_results ) }
16
- let(:sizes_command) { double('sizes', call: sizes_results)}
17
-
18
- context 'search success' do
19
- it 'should return image url for search keyword' do
20
- expect(subject.call 'Volkswagen Golf in Luxembourg').to eq('https://farm3.staticflickr.com/2805/33472607802_42e011bcf5_q.jpg')
21
- end
22
- end
23
-
24
- end