effective_developer 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
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: []