redis-load 0.1

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGES ADDED
@@ -0,0 +1,4 @@
1
+ == Release 0.1
2
+ First release to github
3
+ Added initial support for loading JSON into Redis. Path support not yet fully tested
4
+ Added basic command line tools for loading/saving
data/README ADDED
@@ -0,0 +1,64 @@
1
+ Utility for managing data structures in Redis, allowing lists, sets, etc to be populated by loading
2
+ data from flat files and JSON. Data structures can also be saved out to local files.
3
+
4
+ The rationale here was to provide some easy ways to persistent out the results of data manipulations from
5
+ Redis in easy to process formats. Similarly I also wanted to load data structures from files for use in Redis,
6
+ but where the original data has been created separately, e.g. by a map-reduce job.
7
+
8
+ == Author
9
+
10
+ Leigh Dodds (leigh.dodds@talis.com)
11
+
12
+ == Download
13
+
14
+ http://github.com/ldodds/redis-load
15
+
16
+ == Installation
17
+
18
+ redis-load is available as a gem:
19
+
20
+ sudo gem install redis-load
21
+
22
+ It has dependencies on the ruby redis library, json/pure and siren
23
+
24
+ If you grab a copy from github then there's an install target in the Rakefile that will build and install the gem
25
+
26
+ == Usage
27
+
28
+ The command-line app is called redis-load. You need to provide it with a file name and generally
29
+ a key to be populated:
30
+
31
+ Load the contents of a json file as entries into a server
32
+
33
+ redis-load load-json --file myfile.json
34
+
35
+ Load the contents of a file as key-value pairs. Must be: key,value
36
+
37
+ redis-load load-keys --file mykeys.txt
38
+
39
+ Load the contents of a file into a list. Each line is a list entry
40
+
41
+ redis-load load-list --key mylist --file mylist.txt
42
+
43
+ Load the contents of a file into a set. Each line is a set entry
44
+
45
+ redis-load load-set --key myset --file myset.txt
46
+
47
+ Load the contents of a file into a sort set. Each line is a key-value pair of entry,score
48
+
49
+ redis-load load-zset --key myzset --file myzset.txt
50
+
51
+ Save a list into a file
52
+
53
+ redis-load save-list --key mylist --file mylist.txt
54
+
55
+ Save a set into a file
56
+
57
+ redis-load save-set --key myset --file myset.txt
58
+
59
+ Save a sorted set into a file
60
+
61
+ redis-load save-zset --key myzset --file myzset.txt
62
+
63
+ Note that when saving a file any existing file will automatically be over-written.
64
+
data/Rakefile ADDED
@@ -0,0 +1,66 @@
1
+ require 'rake'
2
+ require 'rake/gempackagetask'
3
+ require 'rake/rdoctask'
4
+ require 'rake/testtask'
5
+ require 'rake/clean'
6
+
7
+ NAME = "redis-load"
8
+ VER = "0.1"
9
+
10
+ RDOC_OPTS = ['--quiet', '--title', 'redis-load Reference', '--main', 'README']
11
+
12
+ PKG_FILES = %w( README Rakefile CHANGES ) +
13
+ Dir.glob("{bin,doc,tests,examples,lib}/**/*")
14
+
15
+ CLEAN.include ['*.gem', 'pkg']
16
+ SPEC =
17
+ Gem::Specification.new do |s|
18
+ s.name = NAME
19
+ s.version = VER
20
+ s.platform = Gem::Platform::RUBY
21
+ s.required_ruby_version = ">= 1.8.5"
22
+ s.has_rdoc = true
23
+ s.extra_rdoc_files = ["README", "CHANGES"]
24
+ s.rdoc_options = RDOC_OPTS
25
+ s.summary = "Utility of loading/saving data structures from Redis"
26
+ s.description = s.summary
27
+ s.author = "Leigh Dodds"
28
+ s.email = 'leigh.dodds@talis.com'
29
+ s.homepage = 'http://github.org/ldodds/redis-load'
30
+ #s.rubyforge_project = ''
31
+ s.files = PKG_FILES
32
+ s.require_path = "lib"
33
+ s.bindir = "bin"
34
+ s.executables = ["redis-load"]
35
+ s.test_file = "tests/ts_redis-load.rb"
36
+ s.add_dependency("redis")
37
+ s.add_dependency("json_pure")
38
+ s.add_dependency("siren")
39
+ end
40
+
41
+ Rake::GemPackageTask.new(SPEC) do |pkg|
42
+ pkg.need_tar = true
43
+ end
44
+
45
+ Rake::RDocTask.new do |rdoc|
46
+ rdoc.rdoc_dir = 'doc/rdoc'
47
+ rdoc.options += RDOC_OPTS
48
+ rdoc.rdoc_files.include("README", "CHANGES", "lib/**/*.rb")
49
+ rdoc.main = "README"
50
+
51
+ end
52
+
53
+ Rake::TestTask.new do |test|
54
+ test.test_files = FileList['tests/tc_*.rb']
55
+ end
56
+
57
+ desc "Install from a locally built copy of the gem"
58
+ task :install do
59
+ sh %{rake package}
60
+ sh %{sudo gem install pkg/#{NAME}-#{VER}}
61
+ end
62
+
63
+ desc "Uninstall the gem"
64
+ task :uninstall => [:clean] do
65
+ sh %{sudo gem uninstall #{NAME}}
66
+ end
data/bin/redis-load ADDED
@@ -0,0 +1,114 @@
1
+ #!/usr/bin/env ruby
2
+ require 'rubygems'
3
+ require 'redis-load'
4
+
5
+ require 'rubygems'
6
+ require 'redis'
7
+ require 'getoptlong'
8
+
9
+ PROGRAM = File::basename $0
10
+
11
+ USAGE = <<-EOL
12
+ SYNOPSIS
13
+
14
+ #{ PROGRAM } mode [args]*
15
+
16
+ DESCRIPTION
17
+
18
+ MODE
19
+
20
+ load-json
21
+ Load the contents of a json file as entries into a server
22
+
23
+ load-keys
24
+ Load the contents of a file as key-value pairs. Must be: key,value
25
+
26
+ load-list
27
+ Load the contents of a file into a list. Each line is a list entry
28
+
29
+ load-set
30
+ Load the contents of a file into a set. Each line is a set entry
31
+
32
+ load-zset
33
+ Load the contents of a file into a sort set. Each line is a key-value pair of entry,score
34
+
35
+ save-list
36
+ Save a list into a file
37
+
38
+ save-set
39
+ Save a set into a file
40
+
41
+ save-zset
42
+ Save a sorted set into a file
43
+
44
+
45
+ OPTIONS
46
+
47
+ --database , -d
48
+ specify database
49
+ --help , -?
50
+ show this message
51
+ --host , -h
52
+ specify host on which server is running
53
+ --file , -f
54
+ path to file containing the data to load
55
+ --password , -x
56
+ specify password to access server
57
+ --port , -p
58
+ specify port on which server is running
59
+ --key , -k
60
+ specify a key into which data is loaded
61
+
62
+ EOL
63
+
64
+ mode = ARGV.shift
65
+
66
+ opts = GetoptLong::new(
67
+ [ "--help" , "-?" , GetoptLong::NO_ARGUMENT ],
68
+ [ "--host" , "-h" , GetoptLong::REQUIRED_ARGUMENT ],
69
+ [ "--port" , "-p" , GetoptLong::REQUIRED_ARGUMENT ],
70
+ [ "--password" , "-x" , GetoptLong::REQUIRED_ARGUMENT ],
71
+ [ "--database" , "-d" , GetoptLong::REQUIRED_ARGUMENT ],
72
+ [ "--key" , "-k" , GetoptLong::REQUIRED_ARGUMENT ],
73
+ [ "--file" , "-f" , GetoptLong::REQUIRED_ARGUMENT ],
74
+ [ "--jsonpath" , "-j" , GetoptLong::REQUIRED_ARGUMENT ]
75
+ ).enum_for.inject({}) { |h, (k, v)| h.update k.delete('-') => v }
76
+
77
+ mode = "help" if opts["--help"]
78
+
79
+ if mode == "help"
80
+ USAGE.display
81
+ else
82
+
83
+ if opts["file"] == nil
84
+ abort USAGE
85
+ end
86
+
87
+ if (mode != "load-json" && mode != "load-keys") && opts["key"] == nil
88
+ abort USAGE
89
+ end
90
+
91
+ cmdline = RedisLoad::CommandLine.new(opts, ENV)
92
+
93
+ case mode
94
+ when "load-json"
95
+ cmdline.load_json()
96
+ when "load-keys"
97
+ cmdline.load_keys()
98
+ when "load-list"
99
+ cmdline.load_list()
100
+ when "load-set"
101
+ cmdline.load_set()
102
+ when "load-zset"
103
+ cmdline.load_zset()
104
+ when "save-list"
105
+ cmdline.save_list()
106
+ when "save-set"
107
+ cmdline.save_set()
108
+ when "save-zset"
109
+ cmdline.save_zset()
110
+ else
111
+ puts "Unknown command"
112
+ end
113
+ end
114
+
data/lib/redis-load.rb ADDED
@@ -0,0 +1,7 @@
1
+ require 'rubygems'
2
+ require 'redis'
3
+ require 'json/pure'
4
+ require 'siren'
5
+
6
+ require 'redis-load/command_line.rb'
7
+ require 'redis-load/loader.rb'
@@ -0,0 +1,71 @@
1
+ module RedisLoad
2
+
3
+ #This class acts as an adapter, taking parameters and environment variables and
4
+ #using those to drive an instance of RedisLoad::Loader
5
+ class CommandLine
6
+
7
+ def initialize(opts, env)
8
+ @opts = opts
9
+ @env = env
10
+
11
+ opts = Hash.new
12
+ opts[:host] = @opts["host"] if @opts["host"]
13
+ opts[:port] = @opts["port"] if @opts["port"]
14
+ opts[:db] = @opts["database"] if @opts["database"]
15
+ opts[:password] = @opts["password"] if @opts["password"]
16
+
17
+ @redis = Redis.new(opts)
18
+ @loader = RedisLoad::Loader.new(@redis)
19
+
20
+ end
21
+
22
+ def load_json()
23
+ puts "Loading data from JSON file #{@opts["file"]} into server"
24
+ counter = @loader.load_json( @opts["file"], @opts["jsonpath"] )
25
+ puts "#{counter} keys loaded."
26
+ end
27
+
28
+ def load_list()
29
+ puts "Loading #{@opts["file"]} into list #{@opts["key"]} into server"
30
+ counter = @loader.load_list( @opts["key"] , @opts["file"] )
31
+ puts "#{counter} lines loaded."
32
+ end
33
+
34
+ def save_list()
35
+ puts "Saving list #{@opts["key"]} into #{@opts["file"]}"
36
+ size = @loader.save_list( @opts["key"], @opts["file"] )
37
+ puts "#{size} lines saved."
38
+ end
39
+
40
+ def load_keys()
41
+ puts "Loading keys from #{@opts["file"]} into server"
42
+ counter = @loader.load_keys( @opts["file"] )
43
+ puts "#{counter} lines loaded."
44
+ end
45
+
46
+ def load_set()
47
+ puts "Loading #{@opts["file"]} into set #{@opts["key"]} on server"
48
+ counter @loader.load_set( @opts["key"], @opts["file"] )
49
+ puts "#{counter} lines loaded."
50
+ end
51
+
52
+ def save_set()
53
+ puts "Saving set #{@opts["key"]} into #{@opts["file"]}"
54
+ size = @loader.save_set( @opts["key"], @opts["file"] )
55
+ puts "#{size} items saved."
56
+ end
57
+
58
+ def load_zset()
59
+ puts "Loading #{@opts["file"]} into sorted set #{@opts["key"]} on server"
60
+ counter = @loader.load_zset( @opts["key"], @opts["file"] )
61
+ puts "#{counter} items loaded."
62
+ end
63
+
64
+ def save_zset()
65
+ puts "Saving set #{@opts["key"]} into #{@opts["file"]}"
66
+ size = @loader.save_zset( @opts["key"], @opts["file"] )
67
+ puts "#{size} items saved."
68
+ end
69
+
70
+ end
71
+ end
@@ -0,0 +1,136 @@
1
+ module RedisLoad
2
+
3
+ #Exposes support for loading and saving different types of redis data
4
+ #structures to and from files
5
+ class Loader
6
+
7
+ attr_reader :redis
8
+
9
+ def initialize(redis)
10
+ @redis = redis
11
+ end
12
+
13
+ def load_keys(file)
14
+ f = File.new( file )
15
+ counter = process_lines(f) do |redis,line|
16
+ line = line.chomp
17
+ parts = line.split(",")
18
+ redis.set( parts[0], parts[1] )
19
+ end
20
+ return counter
21
+ end
22
+
23
+ #Load the contents of a JSON file into Redis
24
+ #
25
+ #Each key in the JSON file becomes a redis key. If the value is a literal then this is added
26
+ #as a redis key-value pair. If an array, then a List. If a hash, then a Hash.
27
+ #
28
+ #Nested objects will be flattened.
29
+ #
30
+ # file:: json file
31
+ # path:: optional, path in the json file indicating the subsection to load
32
+ def load_json(file, path=nil)
33
+ f = File.new( file )
34
+ json = JSON.parse( f.read )
35
+ if path != nil
36
+ json = Siren.query(query, json)
37
+ end
38
+ counter = 0
39
+ #TODO could support proper results, not just path?
40
+ if json != nil
41
+ json.each_pair do |key,value|
42
+ if value.class() == Array
43
+ value.each do |v|
44
+ @redis.lpush( key, v )
45
+ end
46
+ elsif value.class == Hash
47
+ value.each_pair do |field, v|
48
+ #TODO better handling of nesting?
49
+ #E.g. convert hash or array back into JSON?
50
+ @redis.hset(key, field, v)
51
+ end
52
+ else
53
+ @redis.set(key, value)
54
+ end
55
+ counter = counter + 1
56
+ end
57
+ end
58
+ return counter
59
+ end
60
+
61
+ #Load the contents of a file into a Redis list
62
+ #
63
+ # key:: name of the list
64
+ # file:: name of file (string)
65
+ def load_list(key, file)
66
+ f = File.new( file )
67
+ counter = process_lines(f) do |redis,line|
68
+ redis.lpush( key , line.chomp)
69
+ end
70
+ return counter
71
+ end
72
+
73
+ def load_set(key, file)
74
+ f = File.new( file )
75
+ counter = process_lines(f) do |redis,line|
76
+ redis.sadd( key , line.chomp)
77
+ end
78
+ return counter
79
+ end
80
+
81
+ def load_zset(key, file)
82
+ f = File.new( file )
83
+ counter = process_lines(f) do |redis,line|
84
+ line = line.chomp
85
+ parts = line.split(",")
86
+ redis.set( key , parts[1], parts[0] )
87
+ end
88
+ return counter
89
+ end
90
+
91
+ def save_list(key, file)
92
+ size = @redis.llen( key )
93
+ count = 0
94
+ File.open( file , "w") do |file|
95
+ while (count < size)
96
+ file.puts( @redis.lindex(key , count) )
97
+ count = count + 1
98
+ end
99
+ end
100
+ return size
101
+ end
102
+
103
+ def save_set(key, file)
104
+ members = @redis.smembers( key )
105
+ File.open( file , "w") do |file|
106
+ members.each do |m|
107
+ file.puts(m)
108
+ end
109
+ end
110
+ return members.length
111
+ end
112
+
113
+ def save_zset(key, file)
114
+ members = @redis.zrange( key , 0, -1, :with_scores => true)
115
+ File.open( file , "w") do |file|
116
+ members.each_slice(2) do |member,value|
117
+ file.puts("#{member},#{value}")
118
+ end
119
+ end
120
+ return members.length/2
121
+ end
122
+
123
+ #Process lines
124
+ #
125
+ # io:: IO Object
126
+ def process_lines(io)
127
+ counter = 0
128
+ io.each_line do |line|
129
+ yield @redis, line
130
+ counter = counter + 1
131
+ end
132
+ return counter
133
+ end
134
+
135
+ end
136
+ end
@@ -0,0 +1,2 @@
1
+ $:.unshift File.dirname(__FILE__)
2
+ require 'test/unit'
metadata ADDED
@@ -0,0 +1,121 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: redis-load
3
+ version: !ruby/object:Gem::Version
4
+ hash: 9
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 1
9
+ version: "0.1"
10
+ platform: ruby
11
+ authors:
12
+ - Leigh Dodds
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-07-22 00:00:00 +01:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: redis
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ hash: 3
29
+ segments:
30
+ - 0
31
+ version: "0"
32
+ type: :runtime
33
+ version_requirements: *id001
34
+ - !ruby/object:Gem::Dependency
35
+ name: json_pure
36
+ prerelease: false
37
+ requirement: &id002 !ruby/object:Gem::Requirement
38
+ none: false
39
+ requirements:
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ hash: 3
43
+ segments:
44
+ - 0
45
+ version: "0"
46
+ type: :runtime
47
+ version_requirements: *id002
48
+ - !ruby/object:Gem::Dependency
49
+ name: siren
50
+ prerelease: false
51
+ requirement: &id003 !ruby/object:Gem::Requirement
52
+ none: false
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ hash: 3
57
+ segments:
58
+ - 0
59
+ version: "0"
60
+ type: :runtime
61
+ version_requirements: *id003
62
+ description: Utility of loading/saving data structures from Redis
63
+ email: leigh.dodds@talis.com
64
+ executables:
65
+ - redis-load
66
+ extensions: []
67
+
68
+ extra_rdoc_files:
69
+ - README
70
+ - CHANGES
71
+ files:
72
+ - README
73
+ - Rakefile
74
+ - CHANGES
75
+ - bin/redis-load
76
+ - tests/ts_redis-load.rb
77
+ - lib/redis-load.rb
78
+ - lib/redis-load/command_line.rb
79
+ - lib/redis-load/loader.rb
80
+ has_rdoc: true
81
+ homepage: http://github.org/ldodds/redis-load
82
+ licenses: []
83
+
84
+ post_install_message:
85
+ rdoc_options:
86
+ - --quiet
87
+ - --title
88
+ - redis-load Reference
89
+ - --main
90
+ - README
91
+ require_paths:
92
+ - lib
93
+ required_ruby_version: !ruby/object:Gem::Requirement
94
+ none: false
95
+ requirements:
96
+ - - ">="
97
+ - !ruby/object:Gem::Version
98
+ hash: 61
99
+ segments:
100
+ - 1
101
+ - 8
102
+ - 5
103
+ version: 1.8.5
104
+ required_rubygems_version: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ">="
108
+ - !ruby/object:Gem::Version
109
+ hash: 3
110
+ segments:
111
+ - 0
112
+ version: "0"
113
+ requirements: []
114
+
115
+ rubyforge_project:
116
+ rubygems_version: 1.3.7
117
+ signing_key:
118
+ specification_version: 3
119
+ summary: Utility of loading/saving data structures from Redis
120
+ test_files:
121
+ - tests/ts_redis-load.rb