effective_developer 0.0.2

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: c29066e84e43bb85a6bbb3b355537784ca8a8634
4
+ data.tar.gz: aa3035ca68b854b7f2b0416ca59b92680a07ae72
5
+ SHA512:
6
+ metadata.gz: 412c6fe2018b272ef2a3208a25d3a5d9477414f1b02aa15718fcece18e3d82957cc12740078423f672ad4fcd6ed1a9b4eebb3964bbed108fd81e53cebe867746
7
+ data.tar.gz: 730983166244c70d25a519f97a69a5717bfbeff1f681d74c0cd824460bf6ecd5a3dfe34a37ba37848083e788ffec4f4a5f6e42644cc2f12d23f36936c5e6a25f
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2016 Code and Effect Inc.
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,121 @@
1
+ # Effective Developer
2
+
3
+ This is a small gem with some developer quality of life scripts.
4
+
5
+ ## Getting Started
6
+
7
+ Add to your Gemfile:
8
+
9
+ ```ruby
10
+ group :development
11
+ gem 'effective_developer'
12
+ end
13
+ ```
14
+
15
+ Run the bundle command to install it:
16
+
17
+ ```console
18
+ bundle install
19
+ ```
20
+
21
+ ## gem_release
22
+
23
+ A command line shell script that quickly bumps the version of any ruby gem.
24
+
25
+ It checks for any uncommitted files, updates the gem's `version.rb` with the given version, makes a single file `git commit` with a tag and message, then runs `git push origin master`, `gem build` and `gem push` to rubygems.
26
+
27
+ To use with any gem, add the following folder to your `PATH` (~/.bashrc or ~/.profile):
28
+
29
+ ```console
30
+ export PATH="$PATH:$HOME/effective_developer/bin"
31
+ ```
32
+
33
+ Once included in your `PATH`, `gem_release` should be run from the root directory of any ruby gem.
34
+
35
+ To print the current gem version:
36
+
37
+ ```console
38
+ > gem_release
39
+ ```
40
+
41
+ To release a new gem version:
42
+
43
+ ```console
44
+ > gem_release 1.0.0
45
+ ```
46
+
47
+ ## CSV Importer
48
+
49
+ Extend a class from `Effective::CSVImporter` to quickly build a csv importer.
50
+
51
+ Put your importer in `lib/csv_importers/users_importer.rb` and the data in `lib/csv_importers/data/users.csv`. Both filenames should be pluralized.
52
+
53
+ A rake command will be dynamically created `rake import:users`.
54
+
55
+ ### Required Methods
56
+
57
+ The importer requires two instance methods be defined:
58
+
59
+ - `def columns` a hash of names to columns. The constants `A` == 0, `B` == 1, upto `AT` == 45 are defined to work better with spreadsheets.
60
+ - `def process_row` will be run for each row of data. Any exceptions will be caught and logged as errors.
61
+
62
+ Inside the script, there are a few helpful functions:
63
+
64
+ - `col(:email)` will return the normalized value in that column.
65
+ - `last_row_col(:email)` will return the normalized value for this column in the previous row.
66
+ - `raw_col(:email)` will return the raw value in that column
67
+
68
+ ```ruby
69
+ module CsvImporters
70
+ class UsersImporter < Effective::CSVImporter
71
+ def columns
72
+ {
73
+ email: A,
74
+ first_name: B,
75
+ last_name: C,
76
+ admin?: D
77
+ }
78
+ end
79
+
80
+ def process_row
81
+ User.new(email: col(:email), first_name: col(:first_name), last_name: col(:last_name), admin: col(:admin?)).save!
82
+ end
83
+
84
+ end
85
+ end
86
+ ```
87
+
88
+ When using `col()` or `last_row_col()` helpers, the value is normalized.
89
+
90
+ - Any column that ends with a `?` will be converted into a boolean.
91
+ - Any column that ends with `_at` will be converted into a DateTime.
92
+ - Any column that ends with `_on` will be converted into a Date.
93
+ - Override `def normalize` to add your own.
94
+
95
+ ```ruby
96
+ def normalize(column, value)
97
+ if column == :first_name
98
+ value.upcase
99
+ else
100
+ super
101
+ end
102
+ end
103
+ ```
104
+
105
+ Override `before_import()` or `after_import()` to run code before or after the import.
106
+
107
+ ## License
108
+
109
+ MIT License. Copyright [Code and Effect Inc.](http://www.codeandeffect.com/)
110
+
111
+ Code and Effect is the product arm of [AgileStyle](http://www.agilestyle.com/), an Edmonton-based shop that specializes in building custom web applications with Ruby on Rails.
112
+
113
+
114
+ ## Contributing
115
+
116
+ 1. Fork it
117
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
118
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
119
+ 4. Push to the branch (`git push origin my-new-feature`)
120
+ 5. Bonus points for test coverage
121
+ 6. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env rake
2
+ begin
3
+ require 'bundler/setup'
4
+ rescue LoadError
5
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
6
+ end
7
+
8
+ Dir['lib/tasks/**/*.rake'].each { |ext| load ext } if defined?(Rake)
@@ -0,0 +1,17 @@
1
+ unless defined?(Effective::AccessDenied)
2
+ module Effective
3
+ class AccessDenied < StandardError
4
+ attr_reader :action, :subject
5
+
6
+ def initialize(message = nil, action = nil, subject = nil)
7
+ @message = message
8
+ @action = action
9
+ @subject = subject
10
+ end
11
+
12
+ def to_s
13
+ @message || I18n.t(:'unauthorized.default', :default => 'Access Denied')
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,131 @@
1
+ require 'csv'
2
+
3
+ module Effective
4
+ class CSVImporter
5
+ attr_reader :current_row, :last_row
6
+
7
+ A=0;B=1;C=2;D=3;E=4;F=5;G=6;H=7;I=8;J=9;K=10;L=11;M=12;N=13;
8
+ O=14;P=15;Q=16;R=17;S=18;T=19;U=20;V=21;W=22;X=23;Y=24;Z=25;
9
+ AA=26;AB=27;AC=28;AD=29;AE=30;AF=31;AG=32;AH=33;AI=34;AJ=35;
10
+ AK=36;AL=37;AM=38;AN=39;AO=40;AP=41;AQ=42;AR=43;AS=44;AT=45;
11
+
12
+ def initialize(csv_file, has_header_row = true)
13
+ @csv_file = csv_file.to_s
14
+ @has_header_row = has_header_row
15
+ end
16
+
17
+ def csv_file
18
+ @csv_file
19
+ end
20
+
21
+ def import!
22
+ log "Importing #{csv_file.split('/').last.sub('.csv', '')}"
23
+
24
+ @errors_count = 0
25
+
26
+ before_import()
27
+ with_each_row { process_row }
28
+ after_import()
29
+
30
+ log "Import complete (#{@errors_count} errors in #{@has_header_row ? @current_row_number-1 : @current_row_number} rows)"
31
+ end
32
+
33
+ def with_each_row(&block)
34
+ @current_row_number = (@has_header_row ? 2 : 1)
35
+
36
+ CSV.foreach(csv_file, headers: @has_header_row) do |row|
37
+ @current_row = row
38
+
39
+ begin
40
+ yield
41
+ print colorize('.', :green)
42
+ rescue => e
43
+ error(e.message)
44
+ puts row
45
+ puts e.backtrace.first(3)
46
+ end
47
+
48
+ @current_row_number += 1
49
+ @last_row = row
50
+ end
51
+ end
52
+
53
+ def columns
54
+ raise "Please define a method 'def columns' returning a Hash of {id: A, name: B}"
55
+ end
56
+
57
+ def process_row
58
+ raise "Please define a method 'def process_row' to process your row"
59
+ end
60
+
61
+ # Override me if you need some before/after hooks
62
+ def before_import ; end
63
+ def after_import ; end
64
+
65
+ # Normalize the value based on column name
66
+ def normalize(column, value)
67
+ column = column.to_s
68
+ value = value.to_s
69
+
70
+ if column.ends_with?('?') # Boolean
71
+ ::ActiveRecord::ConnectionAdapters::Column::TRUE_VALUES.include?(value)
72
+ elsif column.ends_with?('_at') # DateTime
73
+ Time.zone.parse(value) rescue nil
74
+ elsif column.ends_with?('_on') # Date
75
+ Time.zone.parse(value).beginning_of_day rescue nil
76
+ else
77
+ value.presence || ''.freeze
78
+ end
79
+ end
80
+
81
+ def log(message)
82
+ puts "\n#{message}";
83
+ end
84
+
85
+ def error(message)
86
+ @errors_count += 1
87
+ puts "\n#{colorize('Error', :red)} (.csv line #{@current_row_number}) #{message}"
88
+ end
89
+
90
+ def warn(message)
91
+ puts "\n#{colorize('Warning', :yellow)} (.csv line #{@current_row_number}) #{message}"
92
+ end
93
+
94
+ protected
95
+
96
+ def col(column)
97
+ raise "unknown column :#{column} passed to col()" unless columns.key?(column) || column.kind_of?(Integer)
98
+
99
+ value = current_row[columns[column] || column].try(:strip).presence
100
+
101
+ normalize(column, value)
102
+ end
103
+
104
+ def raw_col(column)
105
+ raise "unknown column :#{column} passed to raw_col()" unless columns.key?(column) || column.kind_of?(Integer)
106
+ current_row[columns[column] || column]
107
+ end
108
+
109
+ def last_row_col(column)
110
+ raise "unknown column :#{column} passed to last_row_col()" unless columns.key?(column) || column.kind_of?(Integer)
111
+
112
+ value = last_row[columns[column] || column].try(:strip).presence
113
+
114
+ normalize(column, value)
115
+ end
116
+
117
+ def colorize(text, color)
118
+ code = case color
119
+ when :cyan ; 36
120
+ when :magenta ; 35
121
+ when :blue ; 34
122
+ when :yellow ; 33
123
+ when :green ; 32
124
+ when :red ; 31
125
+ end
126
+
127
+ "\e[#{code}m#{text}\e[0m"
128
+ end
129
+
130
+ end
131
+ end
@@ -0,0 +1,4 @@
1
+ # EffectiveDeveloper Rails Engine
2
+
3
+ EffectiveDeveloper.setup do |config|
4
+ end
@@ -0,0 +1,11 @@
1
+ module EffectiveDeveloper
2
+ class Engine < ::Rails::Engine
3
+ engine_name 'effective_developer'
4
+
5
+ # Set up our default configuration options.
6
+ initializer 'effective_developer.defaults', before: :load_config_initializers do |app|
7
+ # Set up our defaults, as per our initializer template
8
+ eval File.read("#{config.root}/config/effective_developer.rb")
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,3 @@
1
+ module EffectiveDeveloper
2
+ VERSION = '0.0.2'.freeze
3
+ end
@@ -0,0 +1,17 @@
1
+ require 'effective_developer/engine'
2
+ require 'effective_developer/version'
3
+
4
+ module EffectiveDeveloper
5
+
6
+ def self.setup
7
+ yield self
8
+ end
9
+
10
+ def self.authorized?(controller, action, resource)
11
+ if authorization_method.respond_to?(:call) || authorization_method.kind_of?(Symbol)
12
+ raise Effective::AccessDenied.new() unless (controller || self).instance_exec(controller, action, resource, &authorization_method)
13
+ end
14
+ true
15
+ end
16
+
17
+ end
@@ -0,0 +1,16 @@
1
+ module EffectiveDeveloper
2
+ module Generators
3
+ class InstallGenerator < Rails::Generators::Base
4
+ include Rails::Generators::Migration
5
+
6
+ desc 'Creates an EffectiveDeveloper initializer in your application.'
7
+
8
+ source_root File.expand_path(('../' * 4), __FILE__)
9
+
10
+ def copy_initializer
11
+ template 'config/effective_developer.rb', 'config/initializers/effective_developer.rb'
12
+ end
13
+
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,46 @@
1
+ # effective_csv_importer 1.0
2
+
3
+ # Creates one rake task per importer model, as well as a `rake import:all` task.
4
+
5
+ # Usage:
6
+
7
+ # Put your importer in /lib/csv_importers/posts.rb
8
+ # Put your csv data in /lib/csv_importers/data/posts.csv
9
+ # Both filenames should be pluralized
10
+
11
+ # rake import:posts (one task created per model)
12
+ # rake import:all
13
+
14
+ namespace :import do
15
+ Dir['lib/csv_importers/*.rb'].each do |file|
16
+ importer = file.sub('lib/csv_importers/', '').sub('_importer.rb', '')
17
+ csv_file = "lib/csv_importers/data/#{importer}.csv"
18
+ next unless File.exists?(csv_file)
19
+
20
+ # Create a rake task to import this file
21
+ desc "Import #{importer} from #{csv_file}"
22
+
23
+ task importer => :environment do
24
+ klass = "CsvImporters::#{importer.classify.pluralize}Importer".safe_constantize
25
+ raise "unable to constantize CsvImporters::#{importer.classify.pluralize}Importer for #{file}" unless klass
26
+
27
+ klass.new(csv_file).import!
28
+ end
29
+ end
30
+ end
31
+
32
+ # This task is kind of naive, because some imports could be order dependent. Use at your own risk.
33
+ namespace :import do
34
+ desc "Import all from /lib/csv_importers/*.rb"
35
+
36
+ task :all => :environment do
37
+ Dir['lib/csv_importers/*.rb'].each do |file|
38
+ importer = file.sub('lib/csv_importers/', '').sub('_importer.rb', '')
39
+ csv_file = "lib/csv_importers/data/#{importer}.csv"
40
+ next unless File.exists?(csv_file)
41
+
42
+ Rake::Task["import:#{importer}"].invoke
43
+ end
44
+ end
45
+
46
+ end
@@ -0,0 +1,66 @@
1
+ namespace :pg do
2
+ # Creates a new backup on heroku, downloads that backup to latest.dump, and then calls pg:load
3
+ #
4
+ # bundle exec rake pg:pull
5
+ # bundle exec rake pg:pull[staging]
6
+ desc 'Pulls a newly captured backup from heroku (--remote heroku by default) and calls pg:load'
7
+ task :pull, [:remote] => :environment do |t, args|
8
+ args.with_defaults(:remote => 'heroku')
9
+
10
+ puts "=== Pulling remote '#{args.remote}' database into latest.dump"
11
+
12
+ Bundler.with_clean_env do
13
+ unless system("heroku pg:backups capture --remote #{args.remote}")
14
+ puts "Error capturing backup"
15
+ exit
16
+ end
17
+
18
+ if system("curl -o latest.dump `heroku pg:backups public-url --remote #{args.remote}`")
19
+ puts "Downloading database completed"
20
+ else
21
+ puts "Error downloading database"
22
+ exit
23
+ end
24
+ end
25
+
26
+ Rake::Task['pg:load'].invoke
27
+ end
28
+
29
+ # Drops and re-creates the local database then initializes database with latest.dump
30
+ #
31
+ # bundle exec rake pg:load => Will replace the current database with latest.dump
32
+ # bundle exec rake pg:load[something.dump] => Will replace the current database with latest.dump
33
+ desc 'Loads a postgresql .dump file into the development database (latest.dump by default)'
34
+ task :load, [:file_name] => :environment do |t, args|
35
+ args.with_defaults(:file_name => 'latest.dump')
36
+ db = ActiveRecord::Base.configurations
37
+
38
+ puts "=== Loading #{args.file_name} into local '#{db[Rails.env]['database']}' database"
39
+
40
+ Rake::Task['db:drop'].invoke
41
+ Rake::Task['db:create'].invoke
42
+
43
+ if system("pg_restore --no-acl --no-owner -h localhost -U #{db[Rails.env]['username']} -d #{db[Rails.env]['database']} #{args.file_name}")
44
+ puts "Loading database completed"
45
+ else
46
+ puts "Error loading database"
47
+ end
48
+ end
49
+
50
+ # bundle exec rake pg:save => Will dump the database to latest.dump
51
+ # bundle exec rake pg:save[something.dump] => Will dump the database to something.dump
52
+ desc 'Saves the development database to a postgresql .dump file (latest.dump by default)'
53
+ task :save, [:file_name] => :environment do |t, args|
54
+ args.with_defaults(:file_name => 'latest.dump')
55
+ db = ActiveRecord::Base.configurations
56
+
57
+ puts "=== Saving local '#{db[Rails.env]['database']}' database to #{args.file_name}"
58
+
59
+ if system("pg_dump -Fc --no-acl --no-owner -h localhost -U '#{db[Rails.env]['username']}' '#{db[Rails.env]['database']}' > #{args.file_name}")
60
+ puts "Saving database completed"
61
+ else
62
+ puts "Error saving postgres database"
63
+ end
64
+ end
65
+
66
+ end
metadata ADDED
@@ -0,0 +1,70 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: effective_developer
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ platform: ruby
6
+ authors:
7
+ - Code and Effect
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-04-26 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 3.2.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 3.2.0
27
+ description: Provides some quality of life developer tools.
28
+ email:
29
+ - info@codeandeffect.com
30
+ executables: []
31
+ extensions: []
32
+ extra_rdoc_files: []
33
+ files:
34
+ - MIT-LICENSE
35
+ - README.md
36
+ - Rakefile
37
+ - app/models/effective/access_denied.rb
38
+ - app/models/effective/csv_importer.rb
39
+ - config/effective_developer.rb
40
+ - lib/effective_developer.rb
41
+ - lib/effective_developer/engine.rb
42
+ - lib/effective_developer/version.rb
43
+ - lib/generators/effective_developer/install_generator.rb
44
+ - lib/tasks/effective_csv_importer.rake
45
+ - lib/tasks/pg_pull.rake
46
+ homepage: https://github.com/code-and-effect/effective_developer
47
+ licenses:
48
+ - MIT
49
+ metadata: {}
50
+ post_install_message:
51
+ rdoc_options: []
52
+ require_paths:
53
+ - lib
54
+ required_ruby_version: !ruby/object:Gem::Requirement
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ version: '0'
59
+ required_rubygems_version: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ version: '0'
64
+ requirements: []
65
+ rubyforge_project:
66
+ rubygems_version: 2.4.5.1
67
+ signing_key:
68
+ specification_version: 4
69
+ summary: Provides some quality of life developer tools.
70
+ test_files: []