active_archiver 0.1.2 → 0.1.3

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
  SHA1:
3
- metadata.gz: bcef705dfea7f0005f7b7eb59cce1cd6cd934704
4
- data.tar.gz: d1f50fd1bf33c9ae919a94716dd9a70334d31508
3
+ metadata.gz: e1aa60b9ed13c4fcec274ab0ba54ae740c8c6761
4
+ data.tar.gz: 27d6f63c609c7da615408b3d6fecacdc81011f39
5
5
  SHA512:
6
- metadata.gz: 1f257ece53dd33750e37b8201878b6a59baf1d10df61420a4f9b9e3dc8d64c12f9b1d6bc03a70b9f63398b639f4b5222dab457aabfafe822d6f0212b2d423411
7
- data.tar.gz: 6f841954a95e45166e5b7f6004685f61f80b947ca67aea2b614c1cdb98b71b13d2ea8b1986552b698d458392a4071140725959d72fcee8b9f77fbff362aaba89
6
+ metadata.gz: 99ad9501c2e3ae9f11633b3a5eae1eb2e69ce7367fdb129aeb3d91ec9612071063ffc4f7f00e50def88baa114dfccc1f5057336f0dd6e2a198713ea84fbb6bfd
7
+ data.tar.gz: 6d880ec17fe0b46e38cc97556b531f80fe73a7a526a44c3d653735e584d162060a5b51527a1ad6046fcedd9661586ef4e70e1d854bfd2828ecd4663f462895a3
data/.gitignore ADDED
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.3.1
5
+ before_install: gem install bundler -v 1.13.2
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in active_archiver.gemspec
4
+ gemspec
data/README.md ADDED
@@ -0,0 +1,111 @@
1
+ # ActiveArchiver
2
+
3
+ Provide export / import to ActiveRecord and support CarrierWave.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'active_archiver', github:'zaru/active_archiver'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ ## Usage
18
+
19
+ ### sample code
20
+
21
+ ```
22
+ export = User.find(1).export
23
+
24
+ User.find(1).destroy
25
+ User.find(1)
26
+ => ActiveRecord::RecordNotFound: Couldn't find User with 'id'=1
27
+
28
+ User.import(export)
29
+ User.find(1)
30
+ => #<User id: 1, ...>
31
+ ```
32
+
33
+ ### export
34
+
35
+ Returns the specified model object as a hash. Images uploaded with CarrierWave are encoded in Base64.
36
+
37
+ ```ruby
38
+ export = User.last.export
39
+
40
+ => {:attributes=>
41
+ {"id"=>3,
42
+ "last_name"=>"hoge",
43
+ "first_name"=>"piyo",
44
+ "email"=>"hogepiyo@example.com",
45
+ "created_at"=>Sun, 06 Nov 2016 21:04:11 JST +09:00,
46
+ "updated_at"=>Tue, 22 Nov 2016 12:16:28 JST +09:00},
47
+ :associations=>[]}
48
+ ```
49
+
50
+ If you want to export related models as well
51
+
52
+ ```ruby
53
+ export = User.last.export(includes: [:photos])
54
+
55
+ => {:attributes=>
56
+ {"id"=>3,
57
+ "last_name"=>"hoge",
58
+ "first_name"=>"piyo",
59
+ "email"=>"hogepiyo@example.com",
60
+ "created_at"=>Sun, 06 Nov 2016 21:04:11 JST +09:00,
61
+ "updated_at"=>Tue, 22 Nov 2016 12:16:28 JST +09:00},
62
+ :associations=>
63
+ [{:model_name=>"Photo",
64
+ :association_name=>:photos,
65
+ :attributes=>
66
+ {"id"=>57,
67
+ "user_id"=>3,
68
+ "image"=>
69
+ {:url=>"https://example.com/uploads/hoge.png",
70
+ :file_name=>"hoge.png",
71
+ :blob=>
72
+ "data:image/png;base64,iVBORw...
73
+ ```
74
+
75
+ To specify a nested model.
76
+
77
+ ```
78
+ class User < ApplicationRecord
79
+ has_many :photos
80
+ has_many :pets
81
+ end
82
+
83
+ class Pet < ApplicationRecord
84
+ has_many :pet_photos
85
+ end
86
+ ```
87
+
88
+ ```
89
+ export = User.last.export(includes: [:photos, pets:[:pet_photos]])
90
+ ```
91
+
92
+ ### import
93
+
94
+ ```
95
+ User.import(export)
96
+ ```
97
+
98
+ ### archive
99
+
100
+ Write the temporary file.
101
+
102
+ ```
103
+ Hoge.find(1).archive
104
+
105
+ => #<File:/tmp/20161125-12571-ak10w5.json (closed)>
106
+ ```
107
+
108
+ ## Contributing
109
+
110
+ Bug reports and pull requests are welcome on GitHub at https://github.com/zaru/active_archiver.
111
+
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'active_archiver/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "active_archiver"
8
+ spec.version = ActiveArchiver::VERSION
9
+ spec.authors = ["zaru"]
10
+ spec.email = ["zarutofu@gmail.com"]
11
+
12
+ spec.summary = %q{Provide export / import to ActiveRecord}
13
+ spec.description = %q{Provide export / import to ActiveRecord}
14
+ spec.homepage = "https://github.com/zaru/active_archiver"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
17
+ spec.bindir = "exe"
18
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency "activerecord", ">= 4"
22
+ spec.add_dependency "activesupport", ">= 4"
23
+
24
+ spec.add_development_dependency "bundler", "~> 1.13"
25
+ spec.add_development_dependency "rake", "~> 10.0"
26
+ spec.add_development_dependency "rspec", "~> 3.0"
27
+ end
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "active_archiver"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,11 @@
1
+ require "active_archiver/version"
2
+ require "carrierwave/serialization"
3
+ require "active_archiver/base64_decode"
4
+ require "active_archiver/active_record_ext"
5
+
6
+ module ActiveArchiver
7
+ end
8
+
9
+ ActiveSupport.on_load :active_record do
10
+ ActiveRecord::Base.include(ActiveArchiver::ActiveRecordExt)
11
+ end
@@ -0,0 +1,99 @@
1
+ module ActiveArchiver
2
+ module ActiveRecordExt
3
+
4
+ def self.included(model)
5
+ model.extend ClassMethods
6
+ end
7
+
8
+ def export(includes: [])
9
+ CarrierWave::Uploader::Base.active_archiver_blob_data = true
10
+ hash = {}
11
+ hash[:attributes] = self.serializable_hash
12
+ hash[:associations] = []
13
+ hash = recursive_export(hash, self, includes)
14
+ CarrierWave::Uploader::Base.active_archiver_blob_data = false
15
+ hash
16
+ end
17
+
18
+ def archive(includes: [], basename: ["", ".json"], dir: Dir.tmpdir)
19
+ Tempfile.open(basename, dir) do |fp|
20
+ fp.puts export(includes: includes).to_json
21
+ fp
22
+ end
23
+ end
24
+
25
+ private
26
+
27
+ def recursive_export(hash, receiver, models)
28
+ case models
29
+ when Array
30
+ models.each do |v|
31
+ recursive_export(hash, receiver, v)
32
+ end
33
+ when Hash
34
+ models.each do |k,v|
35
+ recursive_export(hash, receiver, k)
36
+ recursive_export(hash, receiver.send(k), v)
37
+ end
38
+ else
39
+ if receiver.respond_to?(:class_name)
40
+ receiver.each do |rec|
41
+ hash = set_data(hash, rec, models)
42
+ end
43
+ else
44
+ hash = set_data(hash, receiver, models)
45
+ end
46
+ end
47
+
48
+ hash
49
+ end
50
+
51
+ def set_data(hash, receiver, models)
52
+ if receiver.send(models).respond_to?(:class_name)
53
+ model_name = receiver.send(models).class_name
54
+ receiver.send(models).all.each do |r|
55
+ hash[:associations] << data_struct(model_name, models, r.serializable_hash)
56
+ end
57
+ else
58
+ model_name = receiver.send(models).class.class_name
59
+ hash[:associations] << data_struct(model_name, models, receiver.send(models).serializable_hash)
60
+ end
61
+ hash
62
+ end
63
+
64
+ def data_struct(model_name, association_name, attributes)
65
+ {
66
+ model_name: model_name,
67
+ association_name: association_name,
68
+ attributes: attributes
69
+ }
70
+ end
71
+
72
+ module ClassMethods
73
+ def import(hash, validate: false)
74
+ hash = hash.with_indifferent_access
75
+ obj = self.find_or_initialize_by(id: hash["attributes"]["id"])
76
+ obj.attributes = hash["attributes"]
77
+ obj = restore_image(obj, hash)
78
+ obj.save(validate: validate)
79
+
80
+ hash["associations"].each do |r|
81
+ r_obj = Object.const_get(r["model_name"]).find_or_initialize_by(id: r["attributes"]["id"])
82
+ r_obj.attributes = r["attributes"]
83
+ r_obj = restore_image(r_obj, r)
84
+ r_obj.save(validate: validate)
85
+ end
86
+ end
87
+
88
+ private
89
+
90
+ def restore_image(obj, data)
91
+ blobs = data["attributes"].select{|k,v| v.is_a?(Hash) && v.has_key?("blob") }
92
+ blobs.each do |key, blob|
93
+ obj.send("#{key}=", ActiveArchiver::Base64Decode.create_from_canvas_base64(blob["blob"], blob["file_name"]))
94
+ end
95
+ obj
96
+ end
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,28 @@
1
+ module ActiveArchiver
2
+ class Base64Decode < StringIO
3
+ def self.create_from_canvas_base64(str, file_name)
4
+ return nil if str.nil?
5
+ head, data = str.split(",", 2)
6
+ return nil if data.nil?
7
+ _, mime_type = head.split(/:|;/)
8
+ bin = Base64.decode64(data)
9
+
10
+ self.new(bin, mime_type, file_name)
11
+ end
12
+
13
+ def initialize(blob, content_type, file_name)
14
+ super(blob)
15
+ @content_type = content_type
16
+ @file_name = file_name
17
+ self
18
+ end
19
+
20
+ def original_filename
21
+ @file_name
22
+ end
23
+
24
+ def content_type
25
+ @content_type
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,3 @@
1
+ module ActiveArchiver
2
+ VERSION = "0.1.3"
3
+ end
@@ -0,0 +1,30 @@
1
+ module CarrierWave
2
+ module Uploader
3
+ class Base
4
+
5
+ @@active_archiver_blob_data = false
6
+
7
+ class << self
8
+ def active_archiver_blob_data=(flag)
9
+ @@active_archiver_blob_data = flag
10
+ end
11
+ end
12
+
13
+ def serializable_hash(options = nil)
14
+ if @@active_archiver_blob_data
15
+ {
16
+ url: url,
17
+ file_name: File.basename(url),
18
+ blob: "data:#{content_type};base64,#{Base64.strict_encode64(read)}"
19
+ }.merge Hash[versions.map { |name, version| [name, {
20
+ url: version.url,
21
+ file_name: File.basename(version.url),
22
+ blob: "data:#{version.content_type};base64,#{Base64.strict_encode64(version.read)}"
23
+ }] }]
24
+ else
25
+ super
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
metadata CHANGED
@@ -1,12 +1,12 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: active_archiver
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - zaru
8
8
  autorequire:
9
- bindir: bin
9
+ bindir: exe
10
10
  cert_chain: []
11
11
  date: 2016-12-05 00:00:00.000000000 Z
12
12
  dependencies:
@@ -86,7 +86,21 @@ email:
86
86
  executables: []
87
87
  extensions: []
88
88
  extra_rdoc_files: []
89
- files: []
89
+ files:
90
+ - ".gitignore"
91
+ - ".rspec"
92
+ - ".travis.yml"
93
+ - Gemfile
94
+ - README.md
95
+ - Rakefile
96
+ - active_archiver.gemspec
97
+ - bin/console
98
+ - bin/setup
99
+ - lib/active_archiver.rb
100
+ - lib/active_archiver/active_record_ext.rb
101
+ - lib/active_archiver/base64_decode.rb
102
+ - lib/active_archiver/version.rb
103
+ - lib/carrierwave/serialization.rb
90
104
  homepage: https://github.com/zaru/active_archiver
91
105
  licenses: []
92
106
  metadata: {}