persistent_blocks 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.
checksums.yaml ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ YWVjNDQ5ZGQ1Nzk0OTc2N2UyNjg5ODQwMTM5ZjJjZTUwMjhjOTY4ZA==
5
+ data.tar.gz: !binary |-
6
+ ODg5MTcxYzNmN2U3YTZiZTY0MzJiOThkN2NjOGQyNDljMDM0OTBkYg==
7
+ SHA512:
8
+ metadata.gz: !binary |-
9
+ MGMzY2QxMDU5ODA5M2UxMDhhMWZmNjhiNDQyZTY5OTViZGM5MmJmMWEyMTY0
10
+ ODM4YmMzM2RmMjdmYWE2YWNjZDYxYjEyZDE1MzI2ZDhlZjhkZDEzY2U2NDhj
11
+ M2MyMjdkM2QzZjdkMDI4NjYzMWM3YzRjNDJjZDE2NjQ2OTMxZTI=
12
+ data.tar.gz: !binary |-
13
+ YmZjOWUxYWExMzZhZjhkOGM3NjFjMDUwODhiODY2NjI3MmI1OTM3ZjBhNjdh
14
+ OWRiYzY0NmE3ODA3MzY3NmRmNzk2MmUyZGJiNWYyOGMzNWIzOWQyNzgyZmIw
15
+ MTQ5Y2E2YzA0MDBlNmEwNTJjM2E3MmI3OGZjZWEzNzI0NmNmZTY=
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Morris Feldman
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.
data/README.md ADDED
@@ -0,0 +1,49 @@
1
+ # PersistentBlocks
2
+
3
+ Persist the output of ruby blocks
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'persistent_blocks'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install persistent_blocks
18
+
19
+ ## Usage
20
+ ## put the following in a `rakefile.rb`
21
+
22
+ ```ruby
23
+ require 'persistent_blocks'
24
+ extend PersistentBlocks
25
+
26
+ persist :some_persistent_data do
27
+ # Do a long running process to generate some data called :some_persistent_data
28
+ # You don't want to repeat the calculation unecessarily, so persist will
29
+ # automatically persist it to disk for you.
30
+ sleep(1)
31
+ puts "I will return an array that will be automatically persisted to disk"
32
+ puts "Next time you run the rakefile I won't be run."
33
+ ["First persisted element", "Second persisted element"]
34
+ end
35
+
36
+ persist do |some_persistent_data|
37
+ puts "Now I will print out the persistent data"
38
+ p some_persistent_data # => ["First persisted element", "Second persisted element"]
39
+ end
40
+ ```
41
+
42
+ # Then run the rake file using `rake` from the command line. The
43
+ # first time you run it the block generating some_persistent_data will
44
+ # be run, but the second time you run it :some_persistent_data will
45
+ # just be retrieved from disk. The name "some_persistent_data"
46
+ # between the pipes in the second block is important because it tells
47
+ # persist which data to lookup from the marshal files and pass to the
48
+ # block of code.
49
+
@@ -0,0 +1,142 @@
1
+ require 'rake/clean'
2
+ require 'debugger'
3
+
4
+ module PersistentBlocks
5
+ extend self
6
+
7
+ def persist(*args, &block)
8
+ #puts "#{__FILE__}"
9
+ output_syms, specified_output_files, options = PBsubs.parse_inputs(args)
10
+ marshal_output_files = PBsubs.sym_ar_to_file_list(output_syms)
11
+ marshal_dir = PBsubs.ensure_marshal_dir
12
+ file marshal_output_files[0] => marshal_dir unless marshal_output_files[0].nil?
13
+ all_output_files = marshal_output_files + specified_output_files
14
+ target_file = all_output_files[0] # rake tasks only accept one file
15
+ params = if options[:input_overide]
16
+ [*options[:input_overide]]
17
+ else
18
+ block.parameters.map{|p| p[1]}
19
+ end
20
+ prereqs = PBsubs.sym_ar_to_file_list(params)
21
+ file target_file => prereqs unless prereqs.nil?
22
+ file target_file do |f|
23
+ puts "Persistent_blocks: Persisting #{output_syms} and/or #{specified_output_files} from #{[*params]}"
24
+ raw_inputs = PBsubs.load_inputs(prereqs)
25
+ inputs = PBsubs.check_for_single_input(raw_inputs, prereqs.count)
26
+ raw_outputs = block.(inputs)
27
+ outputs = PBsubs.ensure_array(raw_outputs, output_syms.count)
28
+ unless output_syms.empty?
29
+ PBsubs.check_outputs(outputs, output_syms)
30
+ PBsubs.display_output_info(outputs, output_syms)
31
+ PBsubs.save_outputs(outputs, marshal_output_files)
32
+ end
33
+ unless specified_output_files.empty?
34
+ PBsubs.check_specified_output_files(specified_output_files)
35
+ end
36
+ end
37
+ options[:task] ||= PBsubs.default_persistent_blocks_task
38
+ task options[:task].to_sym => target_file
39
+ all_output_files.each do |out_file|
40
+ delete_task = "delete_#{out_file}"
41
+ task delete_task do
42
+ rm out_file if File.exists? out_file
43
+ end
44
+ task "delete_#{options[:task].to_sym}" => delete_task
45
+ file out_file => target_file unless out_file == target_file
46
+ end
47
+ specified_output_files.each do |out_file|
48
+ CLOBBER.include(out_file)
49
+ end
50
+ return target_file # allows file target_file => extra_dependency
51
+ end
52
+ end
53
+
54
+ module PBsubs
55
+ extend Rake::DSL
56
+ extend self
57
+
58
+ attr_accessor :marshal_dir, :default_persistent_blocks_task
59
+ @marshal_dir ||= 'marshal_dir'
60
+ @default_persistent_blocks_task ||= :default
61
+
62
+ def ensure_array(raw, n_expected)
63
+ (n_expected == 1) ? [raw] : raw
64
+ end
65
+
66
+ def check_for_single_input(raw, n_expected)
67
+ (n_expected == 1) ? raw[0] : raw
68
+ end
69
+
70
+ def sym_to_filename(sym)
71
+ File.join(@marshal_dir, sym.to_s)
72
+ end
73
+
74
+ def sym_ar_to_file_list(sym_ar)
75
+ sym_ar.map{|sym| sym_to_filename(sym)}
76
+ end
77
+
78
+ def marshal_save(object, filename)
79
+ File.open(filename, 'w') {|io| Marshal.dump(object, io)}
80
+ end
81
+
82
+ def marshal_load(filename)
83
+ File.open(filename, 'r') {|io| Marshal.load(io)}
84
+ end
85
+
86
+ def load_inputs(filenames)
87
+ return nil if filenames.nil? || filenames.empty?
88
+ filenames.map{|f| marshal_load(f)}
89
+ end
90
+
91
+ def save_outputs(objects_to_save, filenames)
92
+ return if objects_to_save.nil?
93
+ return if filenames.nil? || filenames.empty?
94
+ objects_to_save.zip(filenames).each {|o,f| marshal_save(o, f)}
95
+ end
96
+
97
+ def check_outputs(outputs, output_syms)
98
+ n_expect = output_syms.count
99
+ case n_expect
100
+ when 0 # NP, the block might return an argument, but we don't need to save it
101
+ when 1
102
+ raise "Error: expecting an output (#{outputs}) from the block but got none" if outputs.nil?
103
+ # if we get an array of outputs they will just be mapped to a single marshal file
104
+ else
105
+ raise "Error: expecting #{n_expect} outputs (#{output_sym}) from block but got none" if outputs.nil?
106
+ n_received = [*outputs].count
107
+ raise "Error: expecting #{n_expect} outputs (#{output_syms}) from block but got #{n_received} instead" if n_expect != n_received
108
+ end
109
+ end
110
+
111
+ def display_output_info(outputs, output_syms)
112
+ output_ar = output_syms.zip(outputs).map do |sym, output|
113
+ how_long = (output.respond_to?:length) ? output.length : nil
114
+ "#{sym} (length = #{how_long})"
115
+ end
116
+ puts "Block Outputs:"
117
+ puts output_ar
118
+ end
119
+
120
+ def ensure_marshal_dir
121
+ CLOBBER.include(@marshal_dir) unless CLOBBER.include?(@marshal_dir)
122
+ directory @marshal_dir
123
+ @marshal_dir
124
+ end
125
+
126
+ def parse_inputs(input_args)
127
+ args = input_args.dup
128
+ options = (args[-1].is_a? Hash) ? args.pop : {}
129
+ syms = args.select{|out| out.is_a? Symbol}
130
+ files = args.select{|out| out.is_a? String}
131
+ return syms, files, options
132
+ end
133
+
134
+ def check_specified_output_files(specified_files)
135
+ specified_files.each do |file|
136
+ unless File.exists? file
137
+ raise "Error: #{file} was not created"
138
+ end
139
+ end
140
+ end
141
+
142
+ end
@@ -0,0 +1,22 @@
1
+ Gem::Specification.new do |gem|
2
+ gem.name = "persistent_blocks"
3
+ gem.version = "0.1.0"
4
+ gem.author = "Morris Feldman"
5
+ gem.email = "morrifeldman@gmail.com"
6
+ gem.summary = "Persists the output of Ruby blocks"
7
+ gem.homepage = "http://github.com/morrifeldman/persistent_blocks"
8
+
9
+ gem.files = `git ls-files`.split($/)
10
+
11
+ gem.add_runtime_dependency 'rake'
12
+
13
+ gem.description = <<EODESC
14
+ This gem provides a rake extension to wrap blocks of ruby code so
15
+ that the output of the block is persisted using marshal. Blocks
16
+ with persisted data will not be rerun and their data is available to
17
+ subsequent blocks which can themselves generate persistent data.
18
+ This allow a very simple and robust pipeline to be constructed in
19
+ within a regular rakefile.
20
+ EODESC
21
+
22
+ end
data/rakefile.rb ADDED
@@ -0,0 +1,10 @@
1
+ require "rake/testtask"
2
+
3
+ Rake::TestTask.new do |t|
4
+ t.libs << "test"
5
+ t.test_files = FileList["test/**/*_test.rb"]
6
+ t.verbose = true
7
+ end
8
+
9
+ task :default => 'test'
10
+
@@ -0,0 +1,76 @@
1
+ #!/usr/bin/env ruby
2
+ require 'minitest/autorun'
3
+ require 'persistent_blocks'
4
+
5
+ Test_rakefile = 'test_rakefile.rb'
6
+ Test_dir = 'test'
7
+ def run_rakefile(arg = '')
8
+ `rake -I../lib -f #{Test_rakefile} #{arg}` #../lib require to locate
9
+ #persistent_blocks because we are shelling out
10
+ end
11
+
12
+ describe 'persistent_blocks' do
13
+ before do
14
+ Dir.chdir(Test_dir) do
15
+ puts run_rakefile('clobber')
16
+
17
+ puts "\nRunning tests for the first time:\n"
18
+ puts run_rakefile
19
+
20
+ puts "\nRunning tests for the second time:\n"
21
+ puts @second_run = run_rakefile
22
+
23
+ puts run_rakefile('delete_marshal_dir/test3')
24
+ puts run_rakefile('delete_marshal_dir/test4')
25
+ # this last line should not raise an error
26
+ puts run_rakefile('delete_marshal_dir/test4')
27
+ # asking for another delete shouldn't cause a problem either
28
+
29
+ # clean up
30
+ puts run_rakefile('clobber')
31
+ end
32
+ end
33
+ it 'should persist data' do
34
+ @second_run.must_equal ''
35
+ end
36
+ end
37
+
38
+ describe 'specify task when calling perist' do
39
+ before do
40
+ Dir.chdir(Test_dir) do
41
+ puts run_rakefile('clobber')
42
+ run_rakefile('with_set_task')
43
+ @test_task_persisted = PBsubs.marshal_load(PBsubs.sym_to_filename(:set_task))
44
+ run_rakefile('delete_with_set_task')
45
+ @set_task_exists = File.exists?(File.join(Test_dir, 'marshal_dir','set_task'))
46
+ puts run_rakefile('clobber')
47
+ end
48
+ end
49
+ it 'should run the test_task' do
50
+ @test_task_persisted.must_equal 'My task was set with the :task option'
51
+ end
52
+ it 'the delete_task should delete the specific task' do
53
+ @set_task_exists.must_equal false
54
+ end
55
+ end
56
+
57
+ describe 'setting default_peristent_blocks_task' do
58
+ before do
59
+ Dir.chdir(Test_dir) do
60
+ puts run_rakefile('clobber')
61
+ run_rakefile('non_default_task')
62
+ @non_default_peristed = PBsubs.marshal_load(PBsubs.sym_to_filename(:not_a_default_task))
63
+ run_rakefile('delete_non_default_task')
64
+ @non_default_task_exists = File.exists?(File.join(Test_dir, 'marshal_dir', 'not_a_default_task'))
65
+ run_rakefile('clobber')
66
+ end
67
+ end
68
+ it 'should run the non-default task' do
69
+ @non_default_peristed.must_equal 'I am not a default task, I am a non_default_task'
70
+ end
71
+ it 'should run the delete_task' do
72
+ @non_default_task_exists.must_equal false
73
+ end
74
+ end
75
+
76
+
@@ -0,0 +1,65 @@
1
+ require "persistent_blocks"
2
+ require 'debugger'
3
+
4
+ extend PersistentBlocks
5
+
6
+ persist :test do
7
+ puts "running the first task"
8
+ puts a = [1,2,3]
9
+ a
10
+ end
11
+
12
+ persist :test2 do |test|
13
+ puts "running the second task"
14
+ puts "input = #{test}"
15
+ 'I do not want to be packed into an array'
16
+ end
17
+
18
+ persist :test3, :test4 do |test2|
19
+ puts "About to simulate a 2 sec calc"
20
+ sleep(2)
21
+ puts "test2 = #{test2}"
22
+ puts "test2 = '#{test2}', it should not be an array"
23
+ [test2*1, test2.upcase]
24
+ end
25
+
26
+ persist :task4 do |test3, test4|
27
+ puts "running the 4th task"
28
+ puts "test3 = #{test3}"
29
+ puts "test4 = #{test4}"
30
+ ['String Output\nSecond Line', {test: 1, :test => [1, 2, 3]}]
31
+ end
32
+
33
+ persist :no_paren do
34
+ 'no parentheses'
35
+ end
36
+
37
+ persist :overide_test, input_overide: [:test3, :test4] do |x,y|
38
+ puts "test3 (#{x}) was mapped to x"
39
+ puts "test4 (#{y})) was mapped to y"
40
+ 1
41
+ end
42
+
43
+ persist :overide_test_no_bracket, :input_overide => :test3 do |x|
44
+ puts "input overide also works without brackets"
45
+ end
46
+
47
+ persist :set_task, :task => :with_set_task do
48
+ 'My task was set with the :task option'
49
+ end
50
+
51
+ PBsubs.default_persistent_blocks_task = :non_default_task
52
+ persist :not_a_default_task do
53
+ 'I am not a default task, I am a non_default_task'
54
+ end
55
+
56
+ #persist(:should_fail1, :should_fail_2) do
57
+ # 'one_output, but expecting two'
58
+ #end
59
+
60
+ #persist (:should_fail) do
61
+ # puts "This task should fail because it doesn't return anything"
62
+ #end
63
+
64
+
65
+
metadata ADDED
@@ -0,0 +1,68 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: persistent_blocks
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Morris Feldman
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-09-16 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rake
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ! '>='
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ! '>='
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ description: ! " This gem provides a rake extension to wrap blocks of ruby code so\n
28
+ \ that the output of the block is persisted using marshal. Blocks\n with persisted
29
+ data will not be rerun and their data is available to\n subsequent blocks which
30
+ can themselves generate persistent data.\n This allow a very simple and robust
31
+ pipeline to be constructed in\n within a regular rakefile. \n"
32
+ email: morrifeldman@gmail.com
33
+ executables: []
34
+ extensions: []
35
+ extra_rdoc_files: []
36
+ files:
37
+ - .gitignore
38
+ - LICENSE.txt
39
+ - README.md
40
+ - lib/persistent_blocks.rb
41
+ - persistent_blocks.gemspec
42
+ - rakefile.rb
43
+ - test/persistent_blocks_test.rb
44
+ - test/test_rakefile.rb
45
+ homepage: http://github.com/morrifeldman/persistent_blocks
46
+ licenses: []
47
+ metadata: {}
48
+ post_install_message:
49
+ rdoc_options: []
50
+ require_paths:
51
+ - lib
52
+ required_ruby_version: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - ! '>='
55
+ - !ruby/object:Gem::Version
56
+ version: '0'
57
+ required_rubygems_version: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ requirements: []
63
+ rubyforge_project:
64
+ rubygems_version: 2.1.3
65
+ signing_key:
66
+ specification_version: 4
67
+ summary: Persists the output of Ruby blocks
68
+ test_files: []