feedx 0.2.1 → 0.3.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.
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