barnyard_harvester 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.DS_Store ADDED
Binary file
data/.gitignore ADDED
@@ -0,0 +1,6 @@
1
+ *.gem
2
+ *.idea
3
+ .bundle
4
+ Gemfile.lock
5
+ pkg/*
6
+ 1-BarnyardHarvester::*
data/.rvmrc ADDED
@@ -0,0 +1 @@
1
+ rvm use ruby-1.9.3-p125@barnyard
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in harvester.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,23 @@
1
+ Copyright (c) 2012 Jon Gillies
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
+
data/README.md ADDED
@@ -0,0 +1,94 @@
1
+ # BarnyardHarvester
2
+
3
+ The Harvester gem provides a simple interface where you can iterate your data source and send records to the sync engine. The default backend storage is Redis. Since the workers will use Resque which requires Redis, this make sense to use Redis to cache the data. However, any backend cache can be implemented.
4
+
5
+ Your data sources are called "crops". You must assign a unique integer 1..100 to each crop. This is the integer that is used to create the Redis collection. By default Redis only allows a maximum of 16 databases, so this must be changed if you go above 16.
6
+
7
+ WARNING! Do not use crop number 1-9 in production, they are reserved for system testing.
8
+
9
+ The sync engine keeps a copy of your data source in a Redis databased indexed on the data source's primary key. When you sync your data, the engine uses this "cached" copy of the data to determine adds, deletes and changes. None of the data is inspected and the sync engine does not care what is in the data as long as it can be marshaled into a JSON string. The sync engine automatically does the conversion to and from JSON, so all you pass in is an object that responds to .to_json.
10
+
11
+ ## Installation
12
+
13
+ Add this line to your application's Gemfile:
14
+
15
+ gem 'barnyard_harvester'
16
+
17
+ And then execute:
18
+
19
+ $ bundle
20
+
21
+ Or install it yourself as:
22
+
23
+ $ gem install barnyard_harvester
24
+
25
+ ## Getting Started
26
+
27
+ This example assumes that you have Redis running on your local box localhost:6379. If you do not have Redis installed you can add :backend => :hash to the constructor.
28
+
29
+ require "barnyard_harvester"
30
+
31
+ # Get a connection to your data source
32
+ # (For this example we will use a YAML file)
33
+
34
+ @data = YAML::load_file("test.yml")
35
+
36
+ h = BarnyardHarvester::Sync.new(:debug => false, :crop_number => 1,
37
+ #:backend => :hash
38
+ )
39
+
40
+ h.run do
41
+
42
+ # Iterate your data here and call #process with the primary key and the value
43
+ @data.each do |primary_key, value|
44
+ my_log.info "PK: #{primary_key}"
45
+ h.process primary_key, value # <<-- Send your data here!
46
+ end
47
+
48
+ end
49
+
50
+ puts h.stats
51
+
52
+ Sample output:
53
+
54
+ (0) adds, (0) deletes, (0) changes, (5) source records, (5) cache records
55
+
56
+ ## Testing
57
+
58
+ RSPEC is used to test this gem. Note that you should run each spec test individually. For some reason they confilict if run together. You will need Redis installed on the localhost:6379 or adjust the test accordingly.
59
+
60
+ This tests the initilization and parameters:
61
+
62
+ rspec -c spec/loader_spec.rb
63
+ .......
64
+
65
+ Finished in 0.24556 seconds
66
+ 7 examples, 0 failures
67
+
68
+ This tests the Redis backend of the harvester:
69
+
70
+ rspec -c spec/redis_spec.rb
71
+ .....
72
+
73
+ Finished in 0.27042 seconds
74
+ 5 examples, 0 failures
75
+
76
+ This tests the Hash backend of the harvester:
77
+
78
+ rspec -c spec/hash_spec.rb
79
+ ....
80
+
81
+ Finished in 0.06071 seconds
82
+ 4 examples, 0 failures
83
+
84
+ ## Questions or Problems?
85
+
86
+ supercoder@gmail.com
87
+
88
+ ## Contributing
89
+
90
+ 1. Fork it
91
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
92
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
93
+ 4. Push to the branch (`git push origin my-new-feature`)
94
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,28 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "barnyard_harvester/version"
4
+
5
+ Gem::Specification.new do |gem|
6
+ gem.name = "barnyard_harvester"
7
+ gem.version = BarnyardHarvester::VERSION
8
+ gem.authors = ["Jon Gillies"]
9
+ gem.email = %w(supercoder@gmail.com)
10
+ gem.description = %q{Performs harvests on data sources and detects adds, changes and deletes.}
11
+ gem.summary = %q{Please check the README.md for more information.}
12
+ gem.homepage = "https://github.com/jongillies/barnyard/tree/master/barnyard_harvester"
13
+
14
+ gem.rubyforge_project = "barnyard_harvester"
15
+
16
+ gem.files = `git ls-files`.split("\n")
17
+ gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ gem.require_paths = %w(lib)
20
+
21
+ # specify any dependencies here; for example:
22
+ gem.add_development_dependency "rspec"
23
+ gem.add_runtime_dependency "resque"
24
+ gem.add_runtime_dependency "crack"
25
+ gem.add_runtime_dependency "json"
26
+ gem.add_runtime_dependency "uuid"
27
+
28
+ end
data/lib/.DS_Store ADDED
Binary file
@@ -0,0 +1,84 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'crack'
4
+ require 'json'
5
+
6
+ module BarnyardHarvester
7
+
8
+ class Barn
9
+
10
+ def initialize(args)
11
+
12
+ @crop_number = args.fetch(:crop_number) { raise "You must provide :crop_number" }
13
+ @redis_settings = args.fetch(:redis_settings) { DEFAULT_REDIS_SETTINGS }
14
+ @debug = args.fetch(:debug) { false }
15
+ @log = args.fetch(:logger) { Logger.new(STDOUT) }
16
+
17
+ @my_id = "#{args[:crop_number]}-#{self.class}"
18
+ @barn = Hash.new
19
+
20
+ # Setup data source connection
21
+ begin
22
+ @barn = YAML::load_file("#{@my_id}.yml")
23
+ rescue
24
+ end
25
+ end
26
+
27
+ def log_run(harvester_uuid, crop_number, began_at, ended_at, source_count, change_count, add_count, delete_count)
28
+ @log.debug "HARVESTER_LOG #{harvester_uuid}, #{crop_number}, #{began_at}, #{ended_at}, #{source_count}, #{change_count}, #{add_count}, #{delete_count})"
29
+ end
30
+
31
+ def delete(primary_key)
32
+ check_key primary_key
33
+ object = Crack::JSON.parse(@barn[primary_key])
34
+ @barn.delete primary_key
35
+ object # Return the object
36
+ end
37
+
38
+ def []= primary_key, object
39
+ check_key primary_key
40
+ check_object object
41
+
42
+ @barn[primary_key]= object.to_json
43
+ end
44
+
45
+ def [] primary_key
46
+ check_key primary_key
47
+
48
+ Crack::JSON.parse(@barn[primary_key])
49
+ end
50
+
51
+ def has_key?(primary_key)
52
+ check_key primary_key
53
+
54
+ if @barn.has_key? primary_key
55
+ Crack::JSON.parse(@barn[primary_key])
56
+ else
57
+ nil
58
+ end
59
+ end
60
+
61
+ def each
62
+ @barn.each do |primary_key, value|
63
+ yield primary_key, Crack::JSON.parse(value)
64
+ end
65
+ end
66
+
67
+ def flush
68
+ File.open("#{@my_id}.yml", "w") { |file| file.puts(@barn.to_yaml) }
69
+ end
70
+
71
+ private
72
+
73
+ def check_key(primary_key)
74
+ # Raise an exception here if the key must conform to a specific format
75
+ # Example: raise "key must be a string object" unless key.is_a? String
76
+ end
77
+
78
+ def check_object(object)
79
+ raise "#{object.class} must implement the to_json method" unless object.respond_to? :to_json
80
+ end
81
+ end
82
+
83
+
84
+ end
@@ -0,0 +1,53 @@
1
+ module BarnyardHarvester
2
+ class Queue
3
+
4
+ def initialize(args)
5
+
6
+ @debug = args.fetch(:debug) { false }
7
+
8
+ @log = args.fetch(:logger) { Logger.new(STDOUT) }
9
+
10
+ @my_id = "#{args[:crop_number]}-#{self.class}"
11
+
12
+ @queue = Hash.new
13
+
14
+ end
15
+
16
+ def push(harvester_uuid, crop_change_uuid, crop_number, primary_key, transaction_type, value, old_value=Hash.new)
17
+ check_key primary_key
18
+
19
+ @queue[primary_key] = value.to_json
20
+
21
+ @log.debug "HashQueue: Now: #{DateTime.now}, Harvester:#{harvester_uuid}, Change:#{crop_change_uuid} crop_number: #{crop_number}, key: #{primary_key}, transaction_type: #{transaction_type})"
22
+
23
+ end
24
+
25
+ def flush
26
+ File.open("#{@my_id}.yml", "w") { |file| file.puts(@queue.to_yaml) }
27
+ end
28
+
29
+ private
30
+
31
+ def check_key(primary_key)
32
+ # Raise an exception here if the key must conform to a specific format
33
+ # Example: raise "key must be a string object" unless key.is_a? String
34
+ end
35
+
36
+ end
37
+
38
+ # AddQueue
39
+ #
40
+ class AddQueue < Queue
41
+ end
42
+
43
+ # ChangeQueue
44
+ #
45
+ class ChangeQueue < Queue
46
+ end
47
+
48
+ # DeleteQueue
49
+ #
50
+ class DeleteQueue < Queue
51
+ end
52
+
53
+ end
@@ -0,0 +1,93 @@
1
+ require "redis"
2
+ require "crack"
3
+ require "json"
4
+ require "resque"
5
+
6
+ module BarnyardHarvester
7
+
8
+ class HarvesterLogs
9
+ @queue = :logs_harvester
10
+ end
11
+
12
+ class Barn
13
+
14
+ def initialize(args)
15
+
16
+ @crop_number = args.fetch(:crop_number) { raise "You must provide :crop_number" }
17
+ @redis_settings = args.fetch(:redis_settings) { DEFAULT_REDIS_SETTINGS }
18
+ @debug = args.fetch(:debug) { false }
19
+ @log = args.fetch(:logger) { Logger.new(STDOUT) }
20
+
21
+ redis_args = @redis_settings
22
+
23
+ # This sets the database number for redis to store the cached data
24
+ redis_args[:db] = args[:crop_number]
25
+
26
+ # Connect to Redis
27
+ @redis = Redis.new(redis_args)
28
+
29
+ end
30
+
31
+ def log_run(harvester_uuid, crop_number, began_at, ended_at, source_count, change_count, add_count, delete_count)
32
+
33
+ begin
34
+ Resque.enqueue(HarvesterLogs, Time.now, harvester_uuid, crop_number, began_at, ended_at, source_count, change_count, add_count, delete_count)
35
+ rescue Exception => e
36
+ logger.fatal "#{self.class} Fail in Resque.enqueue of HarvesterLogs. #{e.backtrace}"
37
+ end
38
+
39
+ end
40
+
41
+ def delete(primary_key)
42
+ check_key primary_key
43
+
44
+ value = @redis.get primary_key # Save the value
45
+ @redis.del primary_key # Delete the key
46
+ Crack::JSON.parse(value) # Return the object
47
+ end
48
+
49
+ def []= primary_key, object
50
+ check_key primary_key
51
+ check_object object
52
+
53
+ @redis.set primary_key, object.to_json
54
+ end
55
+
56
+ def [] primary_key
57
+ check_key primary_key
58
+
59
+ Crack::JSON.parse(@redis.get primary_key)
60
+ end
61
+
62
+ def has_key?(primary_key)
63
+ check_key primary_key
64
+
65
+ if @redis.exists primary_key
66
+ Crack::JSON.parse(@redis.get primary_key)
67
+ else
68
+ nil
69
+ end
70
+ end
71
+
72
+ def each
73
+ @redis.keys('*').each do |primary_key|
74
+ yield primary_key, Crack::JSON.parse(@redis.get(primary_key))
75
+ end
76
+ end
77
+
78
+ def flush
79
+ end
80
+
81
+ private
82
+
83
+ def check_key(primary_key)
84
+ # Raise an exception here if the key must conform to a specific format
85
+ # Example: raise "key must be a string object" unless key.is_a? String
86
+ end
87
+
88
+ def check_object(object)
89
+ raise "#{object.class} must implement the to_json method" unless object.respond_to? :to_json
90
+ end
91
+ end
92
+
93
+ end
@@ -0,0 +1,84 @@
1
+ module BarnyardHarvester
2
+
3
+ class ChangeLogs
4
+ @queue = :logs_change
5
+ end
6
+
7
+ class Queue
8
+
9
+ class ResqueQueue
10
+ def initialize(queue, queued_at, harvester_uuid, crop_change_uuid, crop_number, primary_key, transaction_type, value, old_value)
11
+ Resque.enqueue(queue, queued_at, harvester_uuid, crop_change_uuid, crop_number, primary_key, transaction_type, value, old_value)
12
+ Resque.enqueue(ChangeLogs,queued_at, harvester_uuid, crop_change_uuid, crop_number, primary_key, transaction_type, value, old_value)
13
+ end
14
+ end
15
+
16
+ def initialize(args)
17
+
18
+ @debug = args.fetch(:debug) { false }
19
+ @log = args.fetch(:logger) { Logger.new(STDOUT) }
20
+
21
+ raise "arguments must contain :crop_number => some_integer" if args[:crop_number].nil?
22
+
23
+ resque_class_name = "Distribute"
24
+
25
+ # If the class does not exist, the rescue block will create it.
26
+ # The Class Queue is inherited by the AddQueue, ChangeQueue and DeleteQueue, but
27
+ # we only want to create one "resque" queue for this instantiation
28
+ begin
29
+ Object.const_get(resque_class_name)
30
+ rescue
31
+ # Set the queue name to this apol_harvester's id prefixed with a Q_
32
+ #Object.const_set(resque_class_name, Class.new { @queue = "Q_#{args[:crop_number]}"})
33
+ Object.const_set(resque_class_name, Class.new { @queue = "Farmer"})
34
+ end
35
+
36
+ @resque_queue = Object.const_get(resque_class_name)
37
+
38
+ end
39
+
40
+ def push(harvester_uuid, crop_change_uuid, crop_number, primary_key, transaction_type, value, old_value=Hash.new)
41
+ check_key primary_key
42
+
43
+ ResqueQueue.new(@resque_queue, DateTime.now, harvester_uuid, crop_change_uuid, crop_number, primary_key, transaction_type, value.to_json, old_value.to_json)
44
+
45
+ message = "RedisQueue: #{@resque_queue}, Now: #{DateTime.now}, Harvester:#{harvester_uuid}, Change:#{crop_change_uuid} crop_number: #{crop_number}, key: #{primary_key}, transaction_type: #{transaction_type})"
46
+
47
+ if @log.level == Logger::DEBUG
48
+ message += ", value: #{value.to_json}, old_value: #{old_value.to_json}"
49
+ @log.debug message
50
+ end
51
+ end
52
+
53
+ # Flush any data if needed.
54
+ #
55
+ def flush
56
+ end
57
+
58
+ private
59
+
60
+ # Raise an exception here if the key must conform to a specific format
61
+ #
62
+ def check_key(primary_key)
63
+ # Example: raise "key must be a string object" unless key.is_a? String
64
+ primary_key
65
+ end
66
+
67
+ end
68
+
69
+ # AddQueue
70
+ #
71
+ class AddQueue < Queue
72
+ end
73
+
74
+ # ChangeQueue
75
+ #
76
+ class ChangeQueue < Queue
77
+ end
78
+
79
+ # DeleteQueue
80
+ #
81
+ class DeleteQueue < Queue
82
+ end
83
+
84
+ end
@@ -0,0 +1,3 @@
1
+ module BarnyardHarvester
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,162 @@
1
+ require "uuid"
2
+ require "logger"
3
+
4
+ require "barnyard_harvester/version"
5
+
6
+ module BarnyardHarvester
7
+ ADD = "add"
8
+ CHANGE = "change"
9
+ DELETE = "delete"
10
+
11
+ # Available Options:
12
+ #
13
+ # :crop_number (required)
14
+ # :debug true/1
15
+ # :resque_enqueue true/1 (Goes to Farmer queue)
16
+ #
17
+ # :logger_call_back (calls this function with a load of params)
18
+ # :data_call_back (I totally forgot what this was for)
19
+ # :backend (default is :redis, available are: :hash, :mongodb)
20
+ #
21
+
22
+ DEFAULT_REDIS_SETTINGS = {:host => "localhost", :port => 6379}
23
+
24
+ class Sync
25
+
26
+ attr_reader :my_barn, :my_add_queue, :my_change_queue, :my_delete_queue
27
+ attr_reader :add_count, :change_count, :delete_count, :source_count, :cache_count
28
+ attr_reader :resque_enqueue, :redis_settings
29
+ attr_reader :harvester_uuid, :backend, :crop_number
30
+
31
+ def initialize(args)
32
+
33
+ @crop_number = args.fetch(:crop_number) { raise "You must provide :crop_number" }
34
+ @redis_settings = args.fetch(:redis_settings) { DEFAULT_REDIS_SETTINGS }
35
+ @debug = args.fetch(:debug) { false }
36
+ @log = args.fetch(:logger) { Logger.new(STDOUT) }
37
+
38
+
39
+ @backend = args.fetch(:backend) { :redis }
40
+
41
+ require "barnyard_harvester/#{@backend.to_s}_queue"
42
+ require "barnyard_harvester/#{@backend.to_s}"
43
+
44
+ # YAML::ENGINE.yamler = 'syck'
45
+
46
+ @uuid = UUID.new
47
+ @harvester_uuid = @uuid.generate
48
+
49
+ @key_store = Hash.new
50
+
51
+ # Setup barn and queues
52
+ @my_barn = BarnyardHarvester::Barn.new args
53
+ @my_add_queue = BarnyardHarvester::AddQueue.new args
54
+ @my_change_queue = BarnyardHarvester::ChangeQueue.new args
55
+ @my_delete_queue = BarnyardHarvester::DeleteQueue.new args
56
+
57
+ @add_count = @change_count = @delete_count = @source_count = @cache_count = 0
58
+
59
+ end
60
+
61
+ def delete_run
62
+
63
+ deletes = Array.new
64
+
65
+ # Iterate Cache Data, detect deletes.
66
+ @my_barn.each do |primary_key, value|
67
+
68
+ @cache_count += 1
69
+
70
+ next unless @key_store[primary_key].nil?
71
+
72
+ # We got delete
73
+ begin
74
+ crop_change_uuid = @uuid.generate
75
+ @my_delete_queue.push @harvester_uuid, crop_change_uuid, @crop_number, primary_key, BarnyardHarvester::DELETE, value
76
+ rescue Exception => e
77
+ @log.fatal "FATAL error pushing delete #{primary_key} to queue. #{e}"
78
+ exit 1
79
+ end
80
+
81
+ deletes << primary_key
82
+
83
+ @delete_count += 1
84
+ end
85
+
86
+ # Remove Deletes from the Cache Data
87
+ deletes.each do |primary_key|
88
+ @my_barn.delete primary_key
89
+ end
90
+
91
+ end
92
+
93
+ def process primary_key, value
94
+
95
+ @source_count += 1
96
+
97
+ crop_change_uuid = @uuid.generate
98
+
99
+ @key_store[primary_key] = :present # TODO What did this do: if @call_back.nil?
100
+
101
+ if @my_barn.has_key? primary_key
102
+
103
+ @log.debug "original: #{@my_barn[primary_key]}"
104
+
105
+ YAML::ENGINE.yamler = 'syck'
106
+
107
+ @log.debug "current : #{Crack::JSON.parse(value.to_json)}"
108
+
109
+ if @my_barn[primary_key] != Crack::JSON.parse(value.to_json)
110
+ #We got change!
111
+ begin
112
+ @my_change_queue.push(@harvester_uuid, crop_change_uuid, @crop_number, primary_key, BarnyardHarvester::CHANGE, value, @my_barn[primary_key])
113
+ rescue Exception => e
114
+ @log.fatal "FATAL error pushing change #{primary_key} to queue. #{e}"
115
+ exit 1
116
+ end
117
+
118
+ @my_barn[primary_key] = value
119
+ @change_count += 1
120
+ end
121
+ else
122
+ # We got add!
123
+ begin
124
+ @my_add_queue.push(@harvester_uuid, crop_change_uuid, @crop_number, primary_key, BarnyardHarvester::ADD, value)
125
+ rescue Exception => e
126
+ @log.fatal "FATAL error pushing add #{primary_key} to queue. #{e}"
127
+ exit 1
128
+ end
129
+
130
+ @my_barn[primary_key] = value
131
+ @add_count += 1
132
+ end
133
+ end
134
+
135
+ def stats
136
+ "(#{@add_count}) adds, (#{@delete_count}) deletes, (#{@change_count}) changes, (#{@source_count}) source records, (#{@cache_count}) cache records"
137
+ end
138
+
139
+ def run
140
+
141
+ @began_at = Time.now
142
+
143
+ yield
144
+
145
+ # Detect and queue Deletes
146
+ delete_run
147
+
148
+ @ended_at = Time.now
149
+
150
+ # Let Farmer know I'm done and to flush the updates
151
+ @my_barn.flush
152
+ @my_add_queue.flush
153
+ @my_change_queue.flush
154
+ @my_delete_queue.flush
155
+
156
+ @my_barn.log_run(@harvester_uuid, @crop_number, @began_at, @ended_at, @source_count, @change_count, @add_count, @delete_count)
157
+
158
+ end
159
+
160
+ end
161
+
162
+ end
data/lib/test.yml ADDED
@@ -0,0 +1,15 @@
1
+ A:
2
+ name: adam
3
+ sex: qsdasdffasdf5q345
4
+ H:
5
+ name: berry
6
+ sex: aasdfasfdsdaasdffffffffff
7
+ S:
8
+ name: cara
9
+ sex: asdaasdfasdf
10
+ L:
11
+ name: foo
12
+ sex: GGGG
13
+ Z:
14
+ name: foo
15
+ sex: GGGG
@@ -0,0 +1,18 @@
1
+ A:
2
+ name: adam
3
+ sex: F
4
+ B:
5
+ name: berry
6
+ sex: M
7
+ C:
8
+ name: cara
9
+ sex: f
10
+ D:
11
+ name: dee
12
+ sex: f
13
+ E:
14
+ name: evin
15
+ sex: M
16
+ Z:
17
+ name: zoo
18
+ sex: T
@@ -0,0 +1,15 @@
1
+ A:
2
+ name: adam
3
+ sex: F
4
+ B:
5
+ name: berry
6
+ sex: FFF
7
+ C:
8
+ name: cara
9
+ sex: f
10
+ D:
11
+ name: dee
12
+ sex: f
13
+ E:
14
+ name: evin
15
+ sex: M
@@ -0,0 +1,3 @@
1
+ P:
2
+ name: peter
3
+ sex: M
@@ -0,0 +1,12 @@
1
+ A:
2
+ name: adam
3
+ sex: F
4
+ B:
5
+ name: berry
6
+ sex: M
7
+ C:
8
+ name: cara
9
+ sex: f
10
+ D:
11
+ name: dee
12
+ sex: f
@@ -0,0 +1,15 @@
1
+ A:
2
+ name: adam
3
+ sex: F
4
+ B:
5
+ name: berry
6
+ sex: M
7
+ C:
8
+ name: cara
9
+ sex: f
10
+ D:
11
+ name: dee
12
+ sex: f
13
+ E:
14
+ name: evin
15
+ sex: M
data/spec/hash_spec.rb ADDED
@@ -0,0 +1,146 @@
1
+ require "barnyard_harvester"
2
+ require "yaml"
3
+ require "redis"
4
+ require "logger"
5
+ require "json"
6
+
7
+
8
+ # TODO.. Move the flush to the backend object, not here!!!!!!!!!
9
+
10
+ describe BarnyardHarvester do
11
+
12
+
13
+ def load_and_process_file(file, backend)
14
+
15
+ data = YAML::load_file file
16
+
17
+ redis_settings = {
18
+ :host => "localhost",
19
+ :port => 6379,
20
+ }
21
+
22
+ my_logger = Logger.new(STDOUT)
23
+ my_logger.level = Logger::INFO
24
+
25
+ h = BarnyardHarvester::Sync.new(:backend => backend, :debug => false, :crop_number => 1, :hash_settings => redis_settings, :logger => my_logger)
26
+
27
+ h.run do
28
+ data.each do |primary_key, value|
29
+ h.process primary_key, value
30
+ end
31
+ end
32
+
33
+ h
34
+ end
35
+
36
+ def flush(crop_number)
37
+
38
+ File.delete("#{crop_number}-BarnyardHarvester::Barn.yml") if File.exist? "#{crop_number}-BarnyardHarvester::Barn.yml"
39
+
40
+ end
41
+
42
+ before(:each) do
43
+
44
+
45
+ @crop_number = 1
46
+
47
+ flush @crop_number
48
+
49
+ file = "spec/fixtures/data-init.yml"
50
+
51
+ data = YAML::load_file file
52
+
53
+ h = load_and_process_file(file, :hash)
54
+
55
+ h.add_count.should eq(data.count)
56
+ h.delete_count.should eq(0)
57
+ h.change_count.should eq(0)
58
+ h.source_count.should eq(data.count)
59
+ h.cache_count.should eq(data.count)
60
+
61
+ end
62
+
63
+ #it "test initial load of records" do
64
+ #
65
+ # data = YAML::load_file "spec/fixtures/data-init.yml"
66
+ #
67
+ # redis = Redis.new(:db => 1)
68
+ #
69
+ # data.each do |primary_key, value|
70
+ # value.to_json.should eq(redis.get(primary_key))
71
+ # end
72
+ #
73
+ #end
74
+
75
+ it "test add one record" do
76
+
77
+ file = "spec/fixtures/data-add.yml"
78
+ data = YAML::load_file file
79
+
80
+ h = load_and_process_file(file, :hash)
81
+
82
+ h.add_count.should eq(1)
83
+ h.delete_count.should eq(0)
84
+ h.change_count.should eq(0)
85
+ h.source_count.should eq(data.count)
86
+ h.cache_count.should eq(data.count)
87
+
88
+ end
89
+
90
+ it "test change one record" do
91
+
92
+ file = "spec/fixtures/data-change.yml"
93
+
94
+ data = YAML::load_file file
95
+
96
+ h = load_and_process_file(file, :hash)
97
+
98
+ h.add_count.should eq(0)
99
+ h.delete_count.should eq(0)
100
+ h.change_count.should eq(1)
101
+ h.source_count.should eq(data.count)
102
+ h.cache_count.should eq(data.count)
103
+
104
+ end
105
+
106
+ it "test delete one record" do
107
+
108
+ file = "spec/fixtures/data-delete.yml"
109
+
110
+ data = YAML::load_file file
111
+
112
+ h = load_and_process_file(file, :hash)
113
+
114
+
115
+ h.add_count.should eq(0)
116
+ h.delete_count.should eq(1)
117
+ h.change_count.should eq(0)
118
+ h.source_count.should eq(data.count)
119
+ h.cache_count.should eq(data.count + 1)
120
+
121
+ end
122
+
123
+ it "test delete all records and add one" do
124
+
125
+ init_file = "spec/fixtures/data-init.yml"
126
+ init_data = YAML::load_file init_file
127
+
128
+ file = "spec/fixtures/data-delete-all-records-add-one.yml"
129
+ #data = YAML::load_file file
130
+
131
+ h = load_and_process_file(file, :hash)
132
+
133
+ h.add_count.should eq(1)
134
+ h.delete_count.should eq(5)
135
+ h.change_count.should eq(0)
136
+ h.source_count.should eq(1)
137
+ h.cache_count.should eq(init_data.count + 1)
138
+
139
+ end
140
+
141
+
142
+ after(:each) do
143
+ end
144
+
145
+
146
+ end
@@ -0,0 +1,37 @@
1
+ require "barnyard_harvester"
2
+
3
+ describe BarnyardHarvester do
4
+
5
+ it "no parameters should raise error" do
6
+ lambda{BarnyardHarvester::Sync.new}.should raise_error
7
+ end
8
+
9
+ it "passing only :crop_number => 1 should return BarnyardHarvester::Sync object" do
10
+ BarnyardHarvester::Sync.new(crop_number: 1).class.should eq(BarnyardHarvester::Sync)
11
+ end
12
+
13
+ it "default backend should be :redis" do
14
+ BarnyardHarvester::Sync.new(crop_number: 1).backend.should eq(:redis)
15
+ end
16
+
17
+ it "passing backend :hash should be :hash" do
18
+ BarnyardHarvester::Sync.new(crop_number: 1, backend: :hash).backend.should eq(:hash)
19
+ end
20
+
21
+ it "passing bogus backend should raise an error" do
22
+ lambda{BarnyardHarvester::Sync.new(crop_number: 1, backend: :foobar)}.should raise_error
23
+ end
24
+
25
+ it "crop_number should be 1001" do
26
+ BarnyardHarvester::Sync.new(crop_number: 1001).crop_number.should eq(1001)
27
+ end
28
+
29
+ it "all counters should be zero" do
30
+ BarnyardHarvester::Sync.new(crop_number: 1).change_count.should eq(0)
31
+ BarnyardHarvester::Sync.new(crop_number: 1).add_count.should eq(0)
32
+ BarnyardHarvester::Sync.new(crop_number: 1).delete_count.should eq(0)
33
+ BarnyardHarvester::Sync.new(crop_number: 1).source_count.should eq(0)
34
+ BarnyardHarvester::Sync.new(crop_number: 1).cache_count.should eq(0)
35
+ end
36
+
37
+ end
@@ -0,0 +1,149 @@
1
+ require "barnyard_harvester"
2
+ require "yaml"
3
+ require "redis"
4
+ require "logger"
5
+ require "json"
6
+
7
+
8
+ # TODO.. Move the flush to the backend object, not here!!!!!!!!!
9
+
10
+ describe BarnyardHarvester do
11
+
12
+
13
+ def load_and_process_file(file, backend)
14
+
15
+ data = YAML::load_file file
16
+
17
+ redis_settings = {
18
+ :host => "localhost",
19
+ :port => 6379,
20
+ }
21
+
22
+ my_logger = Logger.new(STDOUT)
23
+ my_logger.level = Logger::INFO
24
+
25
+ h = BarnyardHarvester::Sync.new(:backend => backend, :debug => false, :crop_number => 1, :redis_settings => redis_settings, :logger => my_logger)
26
+
27
+ h.run do
28
+ data.each do |primary_key, value|
29
+ h.process primary_key, value
30
+ end
31
+ end
32
+
33
+ h
34
+ end
35
+
36
+ def flush
37
+
38
+ r = Redis.new(:db => 1)
39
+
40
+ r.keys.each do |k|
41
+ r.del k
42
+ end
43
+
44
+ end
45
+
46
+ before(:each) do
47
+
48
+ flush
49
+
50
+ @crop_number = 1
51
+
52
+ file = "spec/fixtures/data-init.yml"
53
+
54
+ data = YAML::load_file file
55
+
56
+ h = load_and_process_file(file, :redis)
57
+
58
+ h.add_count.should eq(data.count)
59
+ h.delete_count.should eq(0)
60
+ h.change_count.should eq(0)
61
+ h.source_count.should eq(data.count)
62
+ h.cache_count.should eq(data.count)
63
+
64
+ end
65
+
66
+ it "test initial load of records" do
67
+
68
+ data = YAML::load_file "spec/fixtures/data-init.yml"
69
+
70
+ redis = Redis.new(:db => 1)
71
+
72
+ data.each do |primary_key, value|
73
+ value.to_json.should eq(redis.get(primary_key))
74
+ end
75
+
76
+ end
77
+
78
+ it "test add one record" do
79
+
80
+ file = "spec/fixtures/data-add.yml"
81
+ data = YAML::load_file file
82
+
83
+ h = load_and_process_file(file, :redis)
84
+
85
+ h.add_count.should eq(1)
86
+ h.delete_count.should eq(0)
87
+ h.change_count.should eq(0)
88
+ h.source_count.should eq(data.count)
89
+ h.cache_count.should eq(data.count)
90
+
91
+ end
92
+
93
+ it "test change one record" do
94
+
95
+ file = "spec/fixtures/data-change.yml"
96
+
97
+ data = YAML::load_file file
98
+
99
+ h = load_and_process_file(file, :redis)
100
+
101
+ h.add_count.should eq(0)
102
+ h.delete_count.should eq(0)
103
+ h.change_count.should eq(1)
104
+ h.source_count.should eq(data.count)
105
+ h.cache_count.should eq(data.count)
106
+
107
+ end
108
+
109
+ it "test delete one record" do
110
+
111
+ file = "spec/fixtures/data-delete.yml"
112
+
113
+ data = YAML::load_file file
114
+
115
+ h = load_and_process_file(file, :redis)
116
+
117
+
118
+ h.add_count.should eq(0)
119
+ h.delete_count.should eq(1)
120
+ h.change_count.should eq(0)
121
+ h.source_count.should eq(data.count)
122
+ h.cache_count.should eq(data.count + 1)
123
+
124
+ end
125
+
126
+ it "test delete all records and add one" do
127
+
128
+ init_file = "spec/fixtures/data-init.yml"
129
+ init_data = YAML::load_file init_file
130
+
131
+ file = "spec/fixtures/data-delete-all-records-add-one.yml"
132
+ #data = YAML::load_file file
133
+
134
+ h = load_and_process_file(file, :redis)
135
+
136
+ h.add_count.should eq(1)
137
+ h.delete_count.should eq(5)
138
+ h.change_count.should eq(0)
139
+ h.source_count.should eq(1)
140
+ h.cache_count.should eq(init_data.count + 1)
141
+
142
+ end
143
+
144
+
145
+ after(:each) do
146
+ end
147
+
148
+
149
+ end
@@ -0,0 +1,19 @@
1
+ # This file was generated by the `rspec --init` command. Conventionally, all
2
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
3
+ # Require this file using `require "spec_helper"` to ensure that it is only
4
+ # loaded once.
5
+ #
6
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
7
+ RSpec.configure do |config|
8
+ config.treat_symbols_as_metadata_keys_with_true_values = true
9
+ config.run_all_when_everything_filtered = true
10
+ config.filter_run :focus
11
+
12
+ # Run specs in random order to surface order dependencies. If you find an
13
+ # order dependency and want to debug it, you can fix the order by providing
14
+ # the seed, which is printed after each run.
15
+ # --seed 1234
16
+ config.order = 'random'
17
+ end
18
+
19
+
metadata ADDED
@@ -0,0 +1,134 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: barnyard_harvester
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Jon Gillies
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-11-10 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rspec
16
+ requirement: &70309177353080 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: *70309177353080
25
+ - !ruby/object:Gem::Dependency
26
+ name: resque
27
+ requirement: &70309177352620 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: *70309177352620
36
+ - !ruby/object:Gem::Dependency
37
+ name: crack
38
+ requirement: &70309177352180 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ type: :runtime
45
+ prerelease: false
46
+ version_requirements: *70309177352180
47
+ - !ruby/object:Gem::Dependency
48
+ name: json
49
+ requirement: &70309177351760 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ type: :runtime
56
+ prerelease: false
57
+ version_requirements: *70309177351760
58
+ - !ruby/object:Gem::Dependency
59
+ name: uuid
60
+ requirement: &70309177351320 !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ! '>='
64
+ - !ruby/object:Gem::Version
65
+ version: '0'
66
+ type: :runtime
67
+ prerelease: false
68
+ version_requirements: *70309177351320
69
+ description: Performs harvests on data sources and detects adds, changes and deletes.
70
+ email:
71
+ - supercoder@gmail.com
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - .DS_Store
77
+ - .gitignore
78
+ - .rvmrc
79
+ - Gemfile
80
+ - LICENSE.txt
81
+ - README.md
82
+ - Rakefile
83
+ - barnyard_harvester.gemspec
84
+ - lib/.DS_Store
85
+ - lib/barnyard_harvester.rb
86
+ - lib/barnyard_harvester/hash.rb
87
+ - lib/barnyard_harvester/hash_queue.rb
88
+ - lib/barnyard_harvester/redis.rb
89
+ - lib/barnyard_harvester/redis_queue.rb
90
+ - lib/barnyard_harvester/version.rb
91
+ - lib/test.yml
92
+ - spec/fixtures/data-add.yml
93
+ - spec/fixtures/data-change.yml
94
+ - spec/fixtures/data-delete-all-records-add-one.yml
95
+ - spec/fixtures/data-delete.yml
96
+ - spec/fixtures/data-init.yml
97
+ - spec/hash_spec.rb
98
+ - spec/loader_spec.rb
99
+ - spec/redis_spec.rb
100
+ - spec/spec_helper.rb
101
+ homepage: https://github.com/jongillies/barnyard/tree/master/barnyard_harvester
102
+ licenses: []
103
+ post_install_message:
104
+ rdoc_options: []
105
+ require_paths:
106
+ - lib
107
+ required_ruby_version: !ruby/object:Gem::Requirement
108
+ none: false
109
+ requirements:
110
+ - - ! '>='
111
+ - !ruby/object:Gem::Version
112
+ version: '0'
113
+ required_rubygems_version: !ruby/object:Gem::Requirement
114
+ none: false
115
+ requirements:
116
+ - - ! '>='
117
+ - !ruby/object:Gem::Version
118
+ version: '0'
119
+ requirements: []
120
+ rubyforge_project: barnyard_harvester
121
+ rubygems_version: 1.8.15
122
+ signing_key:
123
+ specification_version: 3
124
+ summary: Please check the README.md for more information.
125
+ test_files:
126
+ - spec/fixtures/data-add.yml
127
+ - spec/fixtures/data-change.yml
128
+ - spec/fixtures/data-delete-all-records-add-one.yml
129
+ - spec/fixtures/data-delete.yml
130
+ - spec/fixtures/data-init.yml
131
+ - spec/hash_spec.rb
132
+ - spec/loader_spec.rb
133
+ - spec/redis_spec.rb
134
+ - spec/spec_helper.rb