feedx 0.2.1 → 0.3.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
2
  SHA256:
3
- metadata.gz: 268fb5c866f233c844f43b2ba3a19a607bdf719db6a84e0e843ad98f7f4ec899
4
- data.tar.gz: 19e359168ab87c5f1049a2a325480d8a48611c3693b1d33b00bed6150dd3bc20
3
+ metadata.gz: f277d04178e84d34dbdaf5a34c0583cd8e058f7afd3780986fd4a5ba18e27e1c
4
+ data.tar.gz: b806a72c9f263a4819d13068fa347a002d5084c8f95c4c1586e843af95f683fa
5
5
  SHA512:
6
- metadata.gz: 2a6a8b0282f4a17556ae261ba6f90399c2e8e2633cb6fee594ca529fe0f592cce25cceafa523ba9fda9ea6fd13a794a843c75125abd7a633abd033b1996ffd12
7
- data.tar.gz: fb5577e3dd555ee27b9a399f2eabf3b30957be6c41218b74e9daaf9dfef335b50cd529a66d723903f04d2ba7063c4bd7d8b49ceb425ca3d7899f6ebd6ec41680
6
+ metadata.gz: 381ac4112b4b9da045f52f6663f004f80404e58c533b6be0298cf55212db700ae50d7e5f0f6e4064930c266d4c3677f3f6aded9480c6191c936580810a88ee03
7
+ data.tar.gz: b90fc37870f6767b4b19b15cca79a242f36e5922c93084f2b235430ac6879e8e4877703dd1335d9b44f879db3dc79f680775e95aa3c7b0c1aad9b276ac24b6aa
@@ -1,14 +1,14 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- feedx (0.2.1)
5
- bfs
4
+ feedx (0.3.0)
5
+ bfs (>= 0.3.3)
6
6
 
7
7
  GEM
8
8
  remote: https://rubygems.org/
9
9
  specs:
10
10
  ast (2.4.0)
11
- bfs (0.3.2)
11
+ bfs (0.3.3)
12
12
  diff-lcs (1.3)
13
13
  google-protobuf (3.6.1)
14
14
  jaro_winkler (1.5.1)
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = 'feedx'
3
- s.version = '0.2.1'
3
+ s.version = '0.3.0'
4
4
  s.authors = ['Black Square Media Ltd']
5
5
  s.email = ['info@blacksquaremedia.com']
6
6
  s.summary = %(Exchange data between components via feeds)
@@ -13,7 +13,7 @@ Gem::Specification.new do |s|
13
13
  s.require_paths = ['lib']
14
14
  s.required_ruby_version = '>= 2.2'
15
15
 
16
- s.add_dependency 'bfs'
16
+ s.add_dependency 'bfs', '>= 0.3.3'
17
17
 
18
18
  s.add_development_dependency 'bundler'
19
19
  s.add_development_dependency 'pbio'
@@ -6,6 +6,8 @@ module Feedx
6
6
  class Pusher
7
7
  autoload :Recurring, 'feedx/pusher/recurring'
8
8
 
9
+ META_LAST_MODIFIED = 'X-Feedx-Pusher-Last-Modified'.freeze
10
+
9
11
  # See constructor.
10
12
  def self.perform(url, opts={}, &block)
11
13
  new(url, opts, &block).perform
@@ -16,6 +18,7 @@ module Feedx
16
18
  # @option opts [Enumerable,ActiveRecord::Relation] :enum relation or enumerator to stream.
17
19
  # @option opts [Symbol,Class<Feedx::Format::Abstract>] :format custom formatter. Default: from file extension.
18
20
  # @option opts [Symbol,Class<Feedx::Compression::Abstract>] :compress enable compression. Default: from file extension.
21
+ # @option opts [Time,Proc] :last_modified the last modified time, used to determine if a push is necessary.
19
22
  # @yield A block factory to generate the relation or enumerator.
20
23
  # @yieldreturn [Enumerable,ActiveRecord::Relation] the relation or enumerator to stream.
21
24
  def initialize(url, opts={}, &block)
@@ -25,20 +28,26 @@ module Feedx
25
28
  @blob = BFS::Blob.new(url)
26
29
  @format = detect_format(opts[:format])
27
30
  @compress = detect_compress(opts[:compress])
31
+ @last_mod = opts[:last_modified]
28
32
  end
29
33
 
30
- def perform(enum=build_enum)
31
- @blob.create do |io|
34
+ def perform
35
+ enum = @enum.is_a?(Proc) ? @enum.call : @enum
36
+ last_mod = @last_mod.is_a?(Proc) ? @last_mod.call(enum) : @last_mod
37
+ current = (last_mod.to_f * 1000).floor
38
+
39
+ begin
40
+ previous = @blob.info.metadata[META_LAST_MODIFIED].to_i
41
+ return -1 unless current > previous
42
+ rescue BFS::FileNotFound # rubocop:disable Lint/HandleExceptions
43
+ end if current.positive?
44
+
45
+ @blob.create metadata: { META_LAST_MODIFIED => current.to_s } do |io|
32
46
  @compress.wrap(io) {|w| write_all(enum, w) }
33
47
  end
34
48
  @blob.info.size
35
49
  end
36
50
 
37
- # @return [Enumerable,ActiveRecord::Relation] the relation or enumerator.
38
- def build_enum
39
- @enum.is_a?(Proc) ? @enum.call : @enum
40
- end
41
-
42
51
  private
43
52
 
44
53
  def detect_format(val)
@@ -17,44 +17,63 @@ RSpec.describe Feedx::Pusher do
17
17
  %w[x y z].map {|t| model.new(t) } * 100
18
18
  end
19
19
 
20
- let(:tempdir) { Dir.mktmpdir }
21
- after { FileUtils.rm_rf tempdir }
20
+ let(:bucket) { BFS::Bucket::InMem.new }
21
+ before { allow(BFS).to receive(:resolve).and_return(bucket) }
22
22
 
23
23
  it 'should reject invalid inputs' do
24
24
  expect do
25
- described_class.perform "file://#{tempdir}/file.txt", enum: enumerable
25
+ described_class.perform 'mock:///dir/file.txt', enum: enumerable
26
26
  end.to raise_error(/unable to detect format/)
27
27
  end
28
28
 
29
29
  it 'should push compressed JSON' do
30
- size = described_class.perform "file://#{tempdir}/file.jsonz", enum: enumerable
30
+ size = described_class.perform 'mock:///dir/file.jsonz', enum: enumerable
31
31
  expect(size).to be_within(20).of(166)
32
- expect(File.size("#{tempdir}/file.jsonz")).to eq(size)
32
+ expect(bucket.info('dir/file.jsonz').size).to eq(size)
33
33
  end
34
34
 
35
35
  it 'should push plain JSON' do
36
- size = described_class.perform "file://#{tempdir}/file.json", enum: enumerable
36
+ size = described_class.perform 'mock:///dir/file.json', enum: enumerable
37
37
  expect(size).to eq(15900)
38
- expect(File.size("#{tempdir}/file.json")).to eq(size)
38
+ expect(bucket.info('dir/file.json').size).to eq(size)
39
39
  end
40
40
 
41
41
  it 'should push compressed PB' do
42
- size = described_class.perform "file://#{tempdir}/file.pbz", enum: enumerable
42
+ size = described_class.perform 'mock:///dir/file.pbz', enum: enumerable
43
43
  expect(size).to be_within(20).of(41)
44
- expect(File.size("#{tempdir}/file.pbz")).to eq(size)
44
+ expect(bucket.info('dir/file.pbz').size).to eq(size)
45
45
  end
46
46
 
47
47
  it 'should push plain PB' do
48
- size = described_class.perform "file://#{tempdir}/file.pb", enum: enumerable
48
+ size = described_class.perform 'mock:///dir/file.pb', enum: enumerable
49
49
  expect(size).to eq(1200)
50
- expect(File.size("#{tempdir}/file.pb")).to eq(size)
50
+ expect(bucket.info('dir/file.pb').size).to eq(size)
51
51
  end
52
52
 
53
53
  it 'should support factories' do
54
- size = described_class.perform("file://#{tempdir}/file.json") do
54
+ size = described_class.perform('mock:///dir/file.json') do
55
55
  enumerable
56
56
  end
57
57
  expect(size).to eq(15900)
58
- expect(File.size("#{tempdir}/file.json")).to eq(size)
58
+ expect(bucket.info('dir/file.json').size).to eq(size)
59
+ end
60
+
61
+ it 'should support last-modified' do
62
+ described_class.perform 'mock:///dir/file.json', last_modified: Time.at(1515151515), enum: enumerable
63
+ expect(bucket.info('dir/file.json').metadata).to eq('X-Feedx-Pusher-Last-Modified' => '1515151515000')
64
+ end
65
+
66
+ it 'should perform conditionally' do
67
+ size = described_class.perform 'mock:///dir/file.json', last_modified: Time.at(1515151515), enum: enumerable
68
+ expect(size).to eq(15900)
69
+
70
+ size = described_class.perform 'mock:///dir/file.json', last_modified: Time.at(1515151515), enum: enumerable
71
+ expect(size).to eq(-1)
72
+
73
+ size = described_class.perform 'mock:///dir/file.json', last_modified: Time.at(1515151514), enum: enumerable
74
+ expect(size).to eq(-1)
75
+
76
+ size = described_class.perform 'mock:///dir/file.json', last_modified: Time.at(1515151516), enum: enumerable
77
+ expect(size).to eq(15900)
59
78
  end
60
79
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: feedx
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Black Square Media Ltd
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-11-23 00:00:00.000000000 Z
11
+ date: 2018-11-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bfs
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '0'
19
+ version: 0.3.3
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.3.3
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: bundler
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -121,14 +121,12 @@ files:
121
121
  - lib/feedx/format/json.rb
122
122
  - lib/feedx/format/protobuf.rb
123
123
  - lib/feedx/pusher.rb
124
- - lib/feedx/pusher/recurring.rb
125
124
  - spec/feedx/compression/gzip_spec.rb
126
125
  - spec/feedx/compression/none_spec.rb
127
126
  - spec/feedx/compression_spec.rb
128
127
  - spec/feedx/format/json_spec.rb
129
128
  - spec/feedx/format/protobuf_spec.rb
130
129
  - spec/feedx/format_spec.rb
131
- - spec/feedx/pusher/recurring_spec.rb
132
130
  - spec/feedx/pusher_spec.rb
133
131
  - spec/spec_helper.rb
134
132
  homepage: https://github.com/bsm/feedx
@@ -162,6 +160,5 @@ test_files:
162
160
  - spec/feedx/format/json_spec.rb
163
161
  - spec/feedx/format/protobuf_spec.rb
164
162
  - spec/feedx/format_spec.rb
165
- - spec/feedx/pusher/recurring_spec.rb
166
163
  - spec/feedx/pusher_spec.rb
167
164
  - spec/spec_helper.rb
@@ -1,39 +0,0 @@
1
- module Feedx
2
- class Pusher
3
- module Recurring
4
- DEFAULT_CHECK = proc do |enum|
5
- enum.respond_to?(:maximum) ? enum.maximum(:updated_at).to_f : 0.0
6
- end.freeze
7
-
8
- # Registered pushers
9
- def self.registry
10
- @registry ||= {}
11
- end
12
-
13
- # Performs a recurring Pusher task. See see Feedx::Pusher.new for additional
14
- # information on params and options.
15
- #
16
- # @param [String] name a unique task name.
17
- # @param [Hash] opts options.
18
- # @option [Proc] :check a custom revision check to evalute before perform.
19
- # Receives the enum as an argument and returns a Numeric revision.
20
- # Default: ->(enum) { enum.maximum(:updated_at).to_f }
21
- def self.perform(name, url, opts={}, &block)
22
- stored = registry[name]
23
- unless stored
24
- check = opts.delete(:check) || DEFAULT_CHECK
25
- stored = { pusher: Feedx::Pusher.new(url, opts, &block), check: check, revision: 0 }
26
- registry[name] = stored
27
- end
28
-
29
- enum = stored[:pusher].build_enum
30
- latest = stored[:check].call(enum).to_f
31
- return -1 unless stored[:revision] < latest
32
-
33
- size = stored[:pusher].perform(enum)
34
- stored[:revision] = latest
35
- size
36
- end
37
- end
38
- end
39
- end
@@ -1,28 +0,0 @@
1
- require 'spec_helper'
2
-
3
- RSpec.describe Feedx::Pusher::Recurring do
4
- let :enumerable do
5
- enum = %w[x y z].map {|t| { title: t } } * 100
6
- enum.instance_eval do
7
- def maximum(*)
8
- 1515151515.0
9
- end
10
- end
11
- enum
12
- end
13
-
14
- let(:tempdir) { Dir.mktmpdir }
15
- after { FileUtils.rm_rf tempdir }
16
- after { described_class.registry.clear }
17
-
18
- it 'should perform conditionally' do
19
- size = described_class.perform 'test.task', "file://#{tempdir}/file.json", enum: enumerable
20
- expect(size).to eq(4200)
21
- expect(File.size("#{tempdir}/file.json")).to eq(size)
22
- expect(described_class.registry).to include('test.task')
23
- expect(described_class.registry['test.task']).to include(revision: 1515151515.0)
24
-
25
- size = described_class.perform 'test.task', "file://#{tempdir}/file.json", enum: enumerable
26
- expect(size).to eq(-1)
27
- end
28
- end