bulky 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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.
@@ -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
@@ -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,9 @@
1
+ module Bulky
2
+ module FormHelper
3
+
4
+ def whitelisted_attributes_collection(model)
5
+ model.accessible_attributes.select(&:present?).map {|a| [a.titleize, a]}
6
+ end
7
+
8
+ end
9
+ 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
@@ -0,0 +1,7 @@
1
+ en:
2
+ flash:
3
+ notice:
4
+ enqueue_update: "Bulk updated has been scheduled successfully."
5
+ alert:
6
+ blank_ids: "IDs cannot be blank."
7
+ bulk_not_hash: "Bulk update options are not in a reconized format (must be a hash)."
@@ -0,0 +1,6 @@
1
+ Rails.application.routes.draw do
2
+ namespace :bulky do
3
+ get ':model/edit', to: 'updates#edit', as: 'edit'
4
+ put ':model', to: 'updates#update', as: 'update'
5
+ end
6
+ end
@@ -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
@@ -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'
@@ -0,0 +1,7 @@
1
+ require 'haml'
2
+ require 'resque'
3
+
4
+ module Bulky
5
+ class Engine < ::Rails::Engine
6
+ end
7
+ end
@@ -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,3 @@
1
+ module Bulky
2
+ VERSION = "0.8.0"
3
+ 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: []