bulky 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- data/MIT-LICENSE +20 -0
- data/README.md +30 -0
- data/Rakefile +28 -0
- data/app/controllers/bulky/updates_controller.rb +39 -0
- data/app/helpers/bulky/form_helper.rb +9 -0
- data/app/models/bulky/bulk_update.rb +18 -0
- data/app/models/bulky/updated_record.rb +11 -0
- data/app/views/bulky/updates/edit.html.haml +11 -0
- data/config/locales/en.yml +7 -0
- data/config/routes.rb +6 -0
- data/db/migrate/20120911145530_create_bulky_tables.rb +30 -0
- data/lib/bulky.rb +27 -0
- data/lib/bulky/engine.rb +7 -0
- data/lib/bulky/updater.rb +36 -0
- data/lib/bulky/version.rb +3 -0
- data/lib/tasks/bulky_tasks.rake +11 -0
- metadata +197 -0
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2012 YOURNAME
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# Bulky
|
2
|
+
|
3
|
+
## Installation
|
4
|
+
|
5
|
+
In your Gemfile
|
6
|
+
``` ruby
|
7
|
+
gem 'bulky', github: 'adamhunter/bulky'
|
8
|
+
```
|
9
|
+
|
10
|
+
Then in your shell prompt
|
11
|
+
```bash
|
12
|
+
bundle
|
13
|
+
rake bulky_engine:install:migrations
|
14
|
+
rake db:migrate
|
15
|
+
rake bulky:work # (starts the resque worker for bulky)
|
16
|
+
```
|
17
|
+
|
18
|
+
## Usage
|
19
|
+
|
20
|
+
Application
|
21
|
+
Add a form in `app/views/bulky/updates/edit.html.haml` to override the one provided.
|
22
|
+
|
23
|
+
|
24
|
+
Command Line
|
25
|
+
```ruby
|
26
|
+
# model, ids, arguments for update_attributes!
|
27
|
+
Bulky.enqueue_update(Account, [10,25], {"contact" => "Yes, please."})
|
28
|
+
```
|
29
|
+
|
30
|
+
This will enqueue the `Bulky::Updater` to update each account when the job is processed by Resque
|
data/Rakefile
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
#!/usr/bin/env rake
|
2
|
+
|
3
|
+
begin
|
4
|
+
require 'bundler/setup'
|
5
|
+
rescue LoadError
|
6
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
7
|
+
end
|
8
|
+
begin
|
9
|
+
require 'rdoc/task'
|
10
|
+
rescue LoadError
|
11
|
+
require 'rdoc/rdoc'
|
12
|
+
require 'rake/rdoctask'
|
13
|
+
RDoc::Task = Rake::RDocTask
|
14
|
+
end
|
15
|
+
|
16
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
17
|
+
rdoc.rdoc_dir = 'rdoc'
|
18
|
+
rdoc.title = 'Bulky'
|
19
|
+
rdoc.options << '--line-numbers'
|
20
|
+
rdoc.rdoc_files.include('README.rdoc')
|
21
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
22
|
+
end
|
23
|
+
|
24
|
+
|
25
|
+
|
26
|
+
|
27
|
+
Bundler::GemHelper.install_tasks
|
28
|
+
|
@@ -0,0 +1,39 @@
|
|
1
|
+
class Bulky::UpdatesController < ApplicationController
|
2
|
+
|
3
|
+
helper_method :model
|
4
|
+
|
5
|
+
def edit
|
6
|
+
end
|
7
|
+
|
8
|
+
def update
|
9
|
+
if params[:ids].blank?
|
10
|
+
redirect_to bulky_edit_path(model: params[:model]), alert: I18n.t('flash.alert.blank_ids') and return
|
11
|
+
end
|
12
|
+
|
13
|
+
unless params[:bulk].is_a?(Hash)
|
14
|
+
redirect_to bulky_edit_path(model: params[:model]), alert: I18n.t('flash.alert.bulk_not_hash') and return
|
15
|
+
end
|
16
|
+
|
17
|
+
Bulky.enqueue_update(model, ids, params[:bulk])
|
18
|
+
redirect_to bulky_edit_path(model: params[:model]), notice: I18n.t('flash.notice.enqueue_update')
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def model
|
24
|
+
@model ||= params[:model].classify.constantize
|
25
|
+
end
|
26
|
+
|
27
|
+
def ids
|
28
|
+
Bulky.parse_ids(params[:ids])
|
29
|
+
end
|
30
|
+
|
31
|
+
def params
|
32
|
+
@params ||= delete_blank(super)
|
33
|
+
end
|
34
|
+
|
35
|
+
def delete_blank(hash)
|
36
|
+
hash.delete_if { |k,v| v.empty? or Hash === v && delete_blank(v).empty? }
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
class Bulky::BulkUpdate < ActiveRecord::Base
|
2
|
+
self.table_name = 'bulky_bulk_updates'
|
3
|
+
|
4
|
+
serialize :updates, Hash
|
5
|
+
serialize :ids, Array
|
6
|
+
|
7
|
+
validates :ids, :updates, presence: true
|
8
|
+
|
9
|
+
has_many :updated_records, class_name: 'Bulky::UpdatedRecord', foreign_key: :bulk_update_id
|
10
|
+
|
11
|
+
validates_each :updates do |record, attr, value|
|
12
|
+
record.errors[:updates] << "must be a hash" unless Hash === value
|
13
|
+
end
|
14
|
+
|
15
|
+
validates_each :ids do |record, attr, value|
|
16
|
+
record.errors[:ids] << "must be an Array" unless Array === value
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
class Bulky::UpdatedRecord < ActiveRecord::Base
|
2
|
+
self.table_name = 'bulky_updated_records'
|
3
|
+
|
4
|
+
serialize :updatable_changes, Hash
|
5
|
+
|
6
|
+
# updatable_changes can be blank on a mass assignment security violation
|
7
|
+
validates :bulk_update_id, :updatable, presence: true
|
8
|
+
|
9
|
+
belongs_to :bulk_update, class_name: 'Bulky::BulkUpdate', foreign_key: :bulk_update_id
|
10
|
+
belongs_to :updatable, polymorphic: true
|
11
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
%h1 Bulky
|
2
|
+
|
3
|
+
= form_for :bulk, url: bulky_update_path(model: params[:model]), html: {method: :put} do |f|
|
4
|
+
%p
|
5
|
+
= f.select :attribute, whitelisted_attributes_collection(model), {include_blank: "Choose an Attribute"},
|
6
|
+
name: "attribute", onchange: "document.getElementById('bulk_value').name = 'bulk[' + this.value + ']'"
|
7
|
+
= f.text_field :value
|
8
|
+
%p
|
9
|
+
= f.text_area :ids, name: :ids
|
10
|
+
%p
|
11
|
+
= f.button :submit
|
data/config/routes.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
class CreateBulkyTables < ActiveRecord::Migration
|
2
|
+
def up
|
3
|
+
create_table :bulky_bulk_updates, force: true do |t|
|
4
|
+
t.text :ids, null: false
|
5
|
+
t.text :updates, null: false
|
6
|
+
t.integer :initiated_by_id
|
7
|
+
|
8
|
+
t.timestamps
|
9
|
+
end
|
10
|
+
add_index :bulky_bulk_updates, :initiated_by_id
|
11
|
+
|
12
|
+
create_table :bulky_updated_records, force: true do |t|
|
13
|
+
t.integer :bulk_update_id, null: false
|
14
|
+
t.integer :updatable_id, null: false
|
15
|
+
t.string :updatable_type, null: false
|
16
|
+
t.text :updatable_changes, null: false
|
17
|
+
t.string :error_message
|
18
|
+
t.text :error_backtrace
|
19
|
+
|
20
|
+
t.timestamps
|
21
|
+
end
|
22
|
+
add_index :bulky_updated_records, :bulk_update_id
|
23
|
+
add_index :bulky_updated_records, [:updatable_type, :updatable_id]
|
24
|
+
end
|
25
|
+
|
26
|
+
def down
|
27
|
+
drop_table :bulky_updated_records
|
28
|
+
drop_table :bulky_bulk_updates
|
29
|
+
end
|
30
|
+
end
|
data/lib/bulky.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
require "bulky/engine"
|
2
|
+
|
3
|
+
module Bulky
|
4
|
+
extend self
|
5
|
+
|
6
|
+
def enqueue_update(model, ids, updates)
|
7
|
+
bulk_update = log_bulk_update(ids, updates)
|
8
|
+
|
9
|
+
ids.each do |update_id|
|
10
|
+
Resque.enqueue(Bulky::Updater, model.name, update_id, bulk_update.id)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def parse_ids(ids)
|
15
|
+
ids.gsub("\n", ',').split(',').map(&:strip).reject(&:blank?)
|
16
|
+
end
|
17
|
+
|
18
|
+
def log_bulk_update(ids, updates)
|
19
|
+
Bulky::BulkUpdate.create! do |bu|
|
20
|
+
bu.ids = ids
|
21
|
+
bu.updates = updates
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
require 'bulky/updater'
|
data/lib/bulky/engine.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
module Bulky
|
2
|
+
class Updater
|
3
|
+
|
4
|
+
QUEUE = @queue = :bulky_updates
|
5
|
+
|
6
|
+
def self.perform(model_name, update_id, bulk_update_id)
|
7
|
+
model = model_name.constantize.find(update_id)
|
8
|
+
new(model, bulk_update_id).update!
|
9
|
+
end
|
10
|
+
|
11
|
+
def initialize(model, bulk_update_id)
|
12
|
+
@bulk_update = Bulky::BulkUpdate.find(bulk_update_id)
|
13
|
+
@model = model
|
14
|
+
@updates = @bulk_update.updates
|
15
|
+
end
|
16
|
+
|
17
|
+
def update!
|
18
|
+
@model.tap do
|
19
|
+
@log = @bulk_update.updated_records.build { |r| r.updatable = @model }
|
20
|
+
|
21
|
+
begin
|
22
|
+
@model.attributes = @updates
|
23
|
+
@log.updatable_changes = @model.changes
|
24
|
+
@model.save!
|
25
|
+
rescue => e
|
26
|
+
@log.error_message = e.message
|
27
|
+
@log.error_backtrace = e.backtrace.join("\n")
|
28
|
+
raise e
|
29
|
+
ensure
|
30
|
+
@log.save!
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
load "tasks/resque.rake"
|
2
|
+
|
3
|
+
namespace :bulky do
|
4
|
+
task :setup do
|
5
|
+
ENV['QUEUE'] = Bulky::Updater::QUEUE.to_s
|
6
|
+
ENV['BACKGROUND'] = true.to_s
|
7
|
+
end
|
8
|
+
|
9
|
+
desc "start a bulky queue worker (in the background)"
|
10
|
+
task :work => [:environment, :setup, :"resque:work"]
|
11
|
+
end
|
metadata
ADDED
@@ -0,0 +1,197 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: bulky
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.8.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Adam Hunter
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-09-26 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rails
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 3.2.8
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 3.2.8
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: resque
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ~>
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '1.22'
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '1.22'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: haml
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ~>
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '3.1'
|
54
|
+
type: :runtime
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '3.1'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: sqlite3
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: mysql2
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ~>
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: 0.3.11
|
86
|
+
type: :development
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ~>
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: 0.3.11
|
94
|
+
- !ruby/object:Gem::Dependency
|
95
|
+
name: rspec-rails
|
96
|
+
requirement: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ~>
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: 2.11.0
|
102
|
+
type: :development
|
103
|
+
prerelease: false
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ~>
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: 2.11.0
|
110
|
+
- !ruby/object:Gem::Dependency
|
111
|
+
name: capybara
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
113
|
+
none: false
|
114
|
+
requirements:
|
115
|
+
- - ~>
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: 1.1.2
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
none: false
|
122
|
+
requirements:
|
123
|
+
- - ~>
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: 1.1.2
|
126
|
+
- !ruby/object:Gem::Dependency
|
127
|
+
name: database_cleaner
|
128
|
+
requirement: !ruby/object:Gem::Requirement
|
129
|
+
none: false
|
130
|
+
requirements:
|
131
|
+
- - ! '>='
|
132
|
+
- !ruby/object:Gem::Version
|
133
|
+
version: 0.8.0
|
134
|
+
type: :development
|
135
|
+
prerelease: false
|
136
|
+
version_requirements: !ruby/object:Gem::Requirement
|
137
|
+
none: false
|
138
|
+
requirements:
|
139
|
+
- - ! '>='
|
140
|
+
- !ruby/object:Gem::Version
|
141
|
+
version: 0.8.0
|
142
|
+
description: Bulky allows you bulk update your ActiveRecord models. It will enqueue
|
143
|
+
the bulk update and run it through the model's lifecycle to ensure validation are
|
144
|
+
performed. Bulky also provides logging of bulk update success or failure.
|
145
|
+
email:
|
146
|
+
- adamhunter@me.com
|
147
|
+
executables: []
|
148
|
+
extensions: []
|
149
|
+
extra_rdoc_files: []
|
150
|
+
files:
|
151
|
+
- app/controllers/bulky/updates_controller.rb
|
152
|
+
- app/helpers/bulky/form_helper.rb
|
153
|
+
- app/models/bulky/bulk_update.rb
|
154
|
+
- app/models/bulky/updated_record.rb
|
155
|
+
- app/views/bulky/updates/edit.html.haml
|
156
|
+
- config/locales/en.yml
|
157
|
+
- config/routes.rb
|
158
|
+
- db/migrate/20120911145530_create_bulky_tables.rb
|
159
|
+
- lib/bulky/engine.rb
|
160
|
+
- lib/bulky/updater.rb
|
161
|
+
- lib/bulky/version.rb
|
162
|
+
- lib/bulky.rb
|
163
|
+
- lib/tasks/bulky_tasks.rake
|
164
|
+
- MIT-LICENSE
|
165
|
+
- Rakefile
|
166
|
+
- README.md
|
167
|
+
homepage: https://github.com/adamhuner/bulky
|
168
|
+
licenses: []
|
169
|
+
post_install_message:
|
170
|
+
rdoc_options: []
|
171
|
+
require_paths:
|
172
|
+
- lib
|
173
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
174
|
+
none: false
|
175
|
+
requirements:
|
176
|
+
- - ! '>='
|
177
|
+
- !ruby/object:Gem::Version
|
178
|
+
version: '0'
|
179
|
+
segments:
|
180
|
+
- 0
|
181
|
+
hash: 3963931930750602093
|
182
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
183
|
+
none: false
|
184
|
+
requirements:
|
185
|
+
- - ! '>='
|
186
|
+
- !ruby/object:Gem::Version
|
187
|
+
version: '0'
|
188
|
+
segments:
|
189
|
+
- 0
|
190
|
+
hash: 3963931930750602093
|
191
|
+
requirements: []
|
192
|
+
rubyforge_project:
|
193
|
+
rubygems_version: 1.8.24
|
194
|
+
signing_key:
|
195
|
+
specification_version: 3
|
196
|
+
summary: Bulk update your ActiveRecord models.
|
197
|
+
test_files: []
|