active_model_archive 0.0.1
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.
- data/.gitignore +4 -0
- data/.rspec +2 -0
- data/Gemfile +4 -0
- data/Rakefile +2 -0
- data/active_model_archive.gemspec +24 -0
- data/lib/active_model_archive/version.rb +3 -0
- data/lib/active_model_archive.rb +166 -0
- data/spec/archive_spec.rb +3 -0
- data/spec/spec_helper.rb +12 -0
- metadata +91 -0
data/.gitignore
ADDED
data/.rspec
ADDED
data/Gemfile
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "active_model_archive/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "active_model_archive"
|
7
|
+
s.version = ActiveModelArchive::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Grant Rodgers"]
|
10
|
+
s.email = ["grantr@gmail.com"]
|
11
|
+
s.homepage = ""
|
12
|
+
s.summary = %q{Dump and restore activemodel objects}
|
13
|
+
s.description = %q{Adds dump and restore functionality to activemodel classes}
|
14
|
+
|
15
|
+
s.rubyforge_project = "active_model_archive"
|
16
|
+
|
17
|
+
s.files = `git ls-files`.split("\n")
|
18
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
19
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
20
|
+
s.require_paths = ["lib"]
|
21
|
+
s.add_dependency('activesupport', '~> 3')
|
22
|
+
s.add_dependency('yajl-ruby', '>= 0')
|
23
|
+
s.add_development_dependency('rspec', '~> 2')
|
24
|
+
end
|
@@ -0,0 +1,166 @@
|
|
1
|
+
require 'zlib'
|
2
|
+
require 'active_support/concern'
|
3
|
+
|
4
|
+
module ActiveModel
|
5
|
+
module Archive
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
MAGIC = 42098
|
9
|
+
CURRENT_VERSION = 1
|
10
|
+
SUPPORTED_VERSIONS = [1]
|
11
|
+
|
12
|
+
# dump file format:
|
13
|
+
# version 1:
|
14
|
+
#
|
15
|
+
# magic 4 bytes
|
16
|
+
# version 4 bytes
|
17
|
+
#
|
18
|
+
# record length 4 bytes
|
19
|
+
# record + \n
|
20
|
+
# crc 4 bytes
|
21
|
+
# record length 4 bytes
|
22
|
+
# record + \n
|
23
|
+
# crc 4 bytes
|
24
|
+
# ...
|
25
|
+
#
|
26
|
+
# null 4 bytes
|
27
|
+
# record count 4 bytes
|
28
|
+
# magic 4 bytes
|
29
|
+
#
|
30
|
+
|
31
|
+
module ClassMethods
|
32
|
+
# Dump records to a stream.
|
33
|
+
# uses find_each without query params
|
34
|
+
# uses paginated_each with query params
|
35
|
+
# executes the block with each dumped record if given
|
36
|
+
# the block can be used to log progress
|
37
|
+
def dump!(stream, query_params={}, &block)
|
38
|
+
# set the encoding so we can write packed ints
|
39
|
+
stream.set_encoding("ASCII-8BIT")
|
40
|
+
|
41
|
+
archive_write_int(stream, MAGIC)
|
42
|
+
archive_write_int(stream, CURRENT_VERSION)
|
43
|
+
|
44
|
+
count = 0
|
45
|
+
encoder = Yajl::Encoder.new
|
46
|
+
|
47
|
+
writer = lambda do |json|
|
48
|
+
# write size, record, and crc
|
49
|
+
archive_write_int(stream, json.bytesize)
|
50
|
+
stream.write(json)
|
51
|
+
archive_write_int(stream, Zlib::crc32(json))
|
52
|
+
count += 1
|
53
|
+
end
|
54
|
+
|
55
|
+
if query_params.blank?
|
56
|
+
find_each do |object|
|
57
|
+
encoder.encode(object.as_archive, nil, &writer)
|
58
|
+
block.call(object) if block_given?
|
59
|
+
end
|
60
|
+
else
|
61
|
+
paginated_each(query_params) do |object|
|
62
|
+
encoder.encode(object.as_archive, nil, &writer)
|
63
|
+
block.call(object) if block_given?
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
archive_write_int(stream, 0)
|
68
|
+
archive_write_int(stream, count)
|
69
|
+
archive_write_int(stream, MAGIC)
|
70
|
+
|
71
|
+
stream.flush
|
72
|
+
return count
|
73
|
+
end
|
74
|
+
|
75
|
+
# Restore records from a stream.
|
76
|
+
# require attributes= and save accepting :validate option
|
77
|
+
# set :check true to only check the archive and not restore. if no exceptions are thrown, archive is valid.
|
78
|
+
# set :validate true to validate records as they are saved
|
79
|
+
# executes the block with each restored record if given
|
80
|
+
# the block can be used to log progress and warn about invalid objects
|
81
|
+
def restore!(stream, options={}, &block)
|
82
|
+
# check magic byte
|
83
|
+
magic = archive_read_int(stream)
|
84
|
+
raise "Not an archive stream" unless magic == MAGIC
|
85
|
+
|
86
|
+
# check version byte
|
87
|
+
version = archive_read_int(stream)
|
88
|
+
raise "Unsupported version #{version}" unless SUPPORTED_VERSIONS.include?(version)
|
89
|
+
|
90
|
+
count = 0
|
91
|
+
parser = Yajl::Parser.new
|
92
|
+
|
93
|
+
if options[:check]
|
94
|
+
# if we are just checking, increment but do not create anything
|
95
|
+
parser.on_parse_complete = lambda do |hash|
|
96
|
+
count += 1
|
97
|
+
end
|
98
|
+
else
|
99
|
+
# for each hash read, create an object
|
100
|
+
parser.on_parse_complete = lambda do |hash|
|
101
|
+
o = from_archive(hash)
|
102
|
+
o.save(:validate => !!options[:validate])
|
103
|
+
block.call(o) if block_given?
|
104
|
+
count += 1
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
# loop through records until a null is read
|
109
|
+
record_size = archive_read_int(stream)
|
110
|
+
while record_size != 0
|
111
|
+
json = stream.read(record_size)
|
112
|
+
crc = archive_read_int(stream)
|
113
|
+
raise "Invalid CRC at #{stream.pos-1}" unless crc == Zlib::crc32(json)
|
114
|
+
parser << json
|
115
|
+
record_size = archive_read_int(stream)
|
116
|
+
end
|
117
|
+
|
118
|
+
record_count = archive_read_int(stream)
|
119
|
+
magic = archive_read_int(stream)
|
120
|
+
raise "Unexpected end of archive file" unless magic == MAGIC
|
121
|
+
raise "Record counts didn't match: #{record_count} in stream, #{count} read" unless record_count == count
|
122
|
+
|
123
|
+
return count
|
124
|
+
end
|
125
|
+
|
126
|
+
# returns true if the archive is valid
|
127
|
+
def valid_archive?(stream)
|
128
|
+
begin
|
129
|
+
restore!(stream, :check => true)
|
130
|
+
rescue
|
131
|
+
return false
|
132
|
+
end
|
133
|
+
true
|
134
|
+
end
|
135
|
+
|
136
|
+
# generates an object from the archived hash
|
137
|
+
def from_archive(hash)
|
138
|
+
new.tap do |o|
|
139
|
+
o.id = hash.keys.first
|
140
|
+
o.attributes = hash.values.first[model_name.element]
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
private
|
145
|
+
|
146
|
+
def archive_read_int(stream)
|
147
|
+
stream.read(4).unpack("N").first
|
148
|
+
end
|
149
|
+
|
150
|
+
def archive_write_int(stream, int)
|
151
|
+
stream.write([int].pack("N"))
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
# instance method to return archived hash
|
156
|
+
def as_archive
|
157
|
+
# force include_root_in_json
|
158
|
+
# allows archives to include multiple object types
|
159
|
+
if include_root_in_json
|
160
|
+
{id => as_json}
|
161
|
+
else
|
162
|
+
{id => {self.class.model_name.element => as_json}}
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
2
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
3
|
+
require 'rspec'
|
4
|
+
require 'active_model_archive'
|
5
|
+
|
6
|
+
# Requires supporting files with custom matchers and macros, etc,
|
7
|
+
# in ./support/ and its subdirectories.
|
8
|
+
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
|
9
|
+
|
10
|
+
RSpec.configure do |config|
|
11
|
+
config.mock_with :rspec
|
12
|
+
end
|
metadata
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: active_model_archive
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Grant Rodgers
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2011-03-09 00:00:00.000000000 -08:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: activesupport
|
17
|
+
requirement: &14070060 !ruby/object:Gem::Requirement
|
18
|
+
none: false
|
19
|
+
requirements:
|
20
|
+
- - ~>
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: '3'
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: *14070060
|
26
|
+
- !ruby/object:Gem::Dependency
|
27
|
+
name: yajl-ruby
|
28
|
+
requirement: &14069320 !ruby/object:Gem::Requirement
|
29
|
+
none: false
|
30
|
+
requirements:
|
31
|
+
- - ! '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: *14069320
|
37
|
+
- !ruby/object:Gem::Dependency
|
38
|
+
name: rspec
|
39
|
+
requirement: &14068620 !ruby/object:Gem::Requirement
|
40
|
+
none: false
|
41
|
+
requirements:
|
42
|
+
- - ~>
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
version: '2'
|
45
|
+
type: :development
|
46
|
+
prerelease: false
|
47
|
+
version_requirements: *14068620
|
48
|
+
description: Adds dump and restore functionality to activemodel classes
|
49
|
+
email:
|
50
|
+
- grantr@gmail.com
|
51
|
+
executables: []
|
52
|
+
extensions: []
|
53
|
+
extra_rdoc_files: []
|
54
|
+
files:
|
55
|
+
- .gitignore
|
56
|
+
- .rspec
|
57
|
+
- Gemfile
|
58
|
+
- Rakefile
|
59
|
+
- active_model_archive.gemspec
|
60
|
+
- lib/active_model_archive.rb
|
61
|
+
- lib/active_model_archive/version.rb
|
62
|
+
- spec/archive_spec.rb
|
63
|
+
- spec/spec_helper.rb
|
64
|
+
has_rdoc: true
|
65
|
+
homepage: ''
|
66
|
+
licenses: []
|
67
|
+
post_install_message:
|
68
|
+
rdoc_options: []
|
69
|
+
require_paths:
|
70
|
+
- lib
|
71
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
72
|
+
none: false
|
73
|
+
requirements:
|
74
|
+
- - ! '>='
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '0'
|
77
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
78
|
+
none: false
|
79
|
+
requirements:
|
80
|
+
- - ! '>='
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
requirements: []
|
84
|
+
rubyforge_project: active_model_archive
|
85
|
+
rubygems_version: 1.6.0
|
86
|
+
signing_key:
|
87
|
+
specification_version: 3
|
88
|
+
summary: Dump and restore activemodel objects
|
89
|
+
test_files:
|
90
|
+
- spec/archive_spec.rb
|
91
|
+
- spec/spec_helper.rb
|