arel_converter 0.0.1
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 +7 -0
- data/.gitignore +17 -0
- data/.rspec +2 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +124 -0
- data/Rakefile +8 -0
- data/TODO.md +6 -0
- data/arel_converter.gemspec +28 -0
- data/bin/arel_convert +7 -0
- data/lib/arel_converter.rb +25 -0
- data/lib/arel_converter/active_record_finder.rb +19 -0
- data/lib/arel_converter/association.rb +21 -0
- data/lib/arel_converter/base.rb +76 -0
- data/lib/arel_converter/command.rb +50 -0
- data/lib/arel_converter/formatter.rb +46 -0
- data/lib/arel_converter/replacement.rb +21 -0
- data/lib/arel_converter/scope.rb +22 -0
- data/lib/arel_converter/translators/association.rb +71 -0
- data/lib/arel_converter/translators/base.rb +49 -0
- data/lib/arel_converter/translators/finder.rb +28 -0
- data/lib/arel_converter/translators/options.rb +172 -0
- data/lib/arel_converter/translators/scope.rb +28 -0
- data/lib/arel_converter/version.rb +3 -0
- data/spec/fixtures/grep_matching.rb +38 -0
- data/spec/fixtures/my/base_fixture.rb +0 -0
- data/spec/fixtures/my/files/controller.rb +0 -0
- data/spec/fixtures/my/files/model.rb +0 -0
- data/spec/fixtures/my/files/not_source.txt +0 -0
- data/spec/fixtures/my/files/source.rb +0 -0
- data/spec/lib/arel_converter/active_record_finder_spec.rb +26 -0
- data/spec/lib/arel_converter/association_spec.rb +36 -0
- data/spec/lib/arel_converter/base_spec.rb +130 -0
- data/spec/lib/arel_converter/command_spec.rb +7 -0
- data/spec/lib/arel_converter/replacement_spec.rb +22 -0
- data/spec/lib/arel_converter/scope_spec.rb +40 -0
- data/spec/lib/arel_converter/translators/association_spec.rb +110 -0
- data/spec/lib/arel_converter/translators/finder_spec.rb +88 -0
- data/spec/lib/arel_converter/translators/options_spec.rb +104 -0
- data/spec/lib/arel_converter/translators/scope_spec.rb +130 -0
- data/spec/spec_helper.rb +20 -0
- metadata +186 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 47763feba6e20dbcb12a2980e34418cc44807028
|
4
|
+
data.tar.gz: 700c28866ae2ead78afe2cbea76814e3974cb568
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: da48e77635fe41e943295a766e8ef381daf2b6f6f0bbe307f982abe1daf73a208a2bdda8ea84a8ea4846f28ce9c13424935a3dca7ff7f2081a33fe19cf97ea0b
|
7
|
+
data.tar.gz: c68ee8ea1d2fe292ed6192b82cfbeef192eede810115a4afd1ee6a768d0b25ff8b7f2147cabf3385c5abb430dbcc2b5967f6d68c0f25753cfd2e95ca73546184
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Peer Allan
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,124 @@
|
|
1
|
+
# ArelConverter
|
2
|
+
|
3
|
+
This gem add the 'arel_convert' command to your system. When run against
|
4
|
+
a directory (or a single file) it will find and convert scopes,
|
5
|
+
associations and ActiveRecord finders into Rails 4 compatible Arel
|
6
|
+
syntax using Ruby 1.9 array syntax where appropriate. As you may expect
|
7
|
+
it works best under >=1.9 (development and testing was done under 2.0).
|
8
|
+
|
9
|
+
Here are some examples of what is converted,
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
scope :active, :conditions => {:active => true}, :order => 'created_at'
|
13
|
+
# becomes
|
14
|
+
scope :active, -> { where(active: true).order('created_at') }
|
15
|
+
|
16
|
+
Model.find(:all, :conditions => ['name = ?', params[:term]], :limit => 5)
|
17
|
+
# becomes
|
18
|
+
Model.where('name = ?', params[:term]).limit(5)
|
19
|
+
|
20
|
+
has_many :articles, :class_name => "Post", :order => 'updated_at DESC'
|
21
|
+
# becomes
|
22
|
+
has_many :articles, -> { order('updated_at DESC') }, class_name: "Post"
|
23
|
+
```
|
24
|
+
|
25
|
+
The converters use Ruby2Ruby and RubyParser to convert to and translate
|
26
|
+
s-expressions. This ensures the code is converter back into valid Ruby
|
27
|
+
code. String parsing just doesn't work as well.
|
28
|
+
|
29
|
+
There are 3 converters,
|
30
|
+
|
31
|
+
### Scope
|
32
|
+
|
33
|
+
Converts 'scope' statements into the updated Rails 4 syntax, i.e.
|
34
|
+
wrapped in a lambda, with pre-Arel options converted to Arel
|
35
|
+
|
36
|
+
### Associations
|
37
|
+
|
38
|
+
Updates associations to use the updated Rails 4 syntax, i.e. wrapped in
|
39
|
+
lambdas where necessary. Assocation types handled are,
|
40
|
+
|
41
|
+
* belongs_to
|
42
|
+
* has_one
|
43
|
+
* has_many
|
44
|
+
* has_and_belongs_to_many
|
45
|
+
|
46
|
+
### Finders
|
47
|
+
|
48
|
+
Updates old ActiveRecord (pre Rails 3) syntax into Arel syntax of
|
49
|
+
chained Relations. This handles the following, including chained calls,
|
50
|
+
|
51
|
+
* Object.find(:all
|
52
|
+
* Object.find(:first
|
53
|
+
* Object.find.*:conditions
|
54
|
+
* Object.all(
|
55
|
+
* Object.first(
|
56
|
+
|
57
|
+
|
58
|
+
## Installation
|
59
|
+
|
60
|
+
Add this line to your application's Gemfile:
|
61
|
+
|
62
|
+
gem 'arel_converter'
|
63
|
+
|
64
|
+
And then execute:
|
65
|
+
|
66
|
+
$ bundle
|
67
|
+
|
68
|
+
Or install it yourself as:
|
69
|
+
|
70
|
+
$ gem install arel_converter
|
71
|
+
|
72
|
+
## Usage
|
73
|
+
|
74
|
+
$ arel_convert [all,scope,association,finder] [PATH]
|
75
|
+
|
76
|
+
## Notes/Warnings/Recommendations
|
77
|
+
|
78
|
+
As with any software that could potentially update a wide swath of your
|
79
|
+
codebase in one (terrible) moment, it is recommended that you are
|
80
|
+
operating under some form of source control or have a reliable backup.
|
81
|
+
|
82
|
+
The converters doesn't play well with multiline code blocks. This means
|
83
|
+
that the parser will encounter a Exception (likely SyntaxError) when
|
84
|
+
processing the first line. This is because the matching code is a simple
|
85
|
+
grep of the file. Therefore, it would only grab the first line of a
|
86
|
+
multiline statement. The good news is that these exceptions will show up
|
87
|
+
in the results.
|
88
|
+
|
89
|
+
If your tests/specs are good then everything should still pass. If your
|
90
|
+
tests/specs are coupled to ActiveRecord finders then alot of them are
|
91
|
+
going to break. The converters make no attempt to update tests/specs at
|
92
|
+
this time. I've toyed with it and didn't get very far.
|
93
|
+
|
94
|
+
|
95
|
+
## Contributing
|
96
|
+
|
97
|
+
1. Fork it
|
98
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
99
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
100
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
101
|
+
5. Create new Pull Request
|
102
|
+
|
103
|
+
# License
|
104
|
+
|
105
|
+
### This code is free to use under the terms of the MIT license.
|
106
|
+
|
107
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
108
|
+
a copy of this software and associated documentation files (the
|
109
|
+
"Software"), to deal in the Software without restriction, including
|
110
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
111
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
112
|
+
permit persons to whom the Software is furnished to do so, subject to
|
113
|
+
the following conditions:
|
114
|
+
|
115
|
+
The above copyright notice and this permission notice shall be included
|
116
|
+
in all copies or substantial portions of the Software.
|
117
|
+
|
118
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
119
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
120
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
121
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
122
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
123
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
124
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
data/TODO.md
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'arel_converter/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "arel_converter"
|
8
|
+
spec.version = ArelConverter::VERSION
|
9
|
+
spec.authors = ["Peer Allan"]
|
10
|
+
spec.email = ["peer.allan@canadadrugs.com"]
|
11
|
+
spec.description = %q{Converts existing AR finder syntax to AREL}
|
12
|
+
spec.summary = %q{Converts AR finders, scopes and association arguments to AREL syntax}
|
13
|
+
spec.homepage = ""
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_dependency('ruby2ruby')
|
22
|
+
spec.add_dependency('ruby_parser')
|
23
|
+
spec.add_dependency('logging')
|
24
|
+
|
25
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
26
|
+
spec.add_development_dependency "rake"
|
27
|
+
spec.add_development_dependency "rspec"
|
28
|
+
end
|
data/bin/arel_convert
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
require "arel_converter/version"
|
2
|
+
require 'ruby2ruby'
|
3
|
+
require 'ruby_parser'
|
4
|
+
require 'logging'
|
5
|
+
|
6
|
+
$:.unshift(File.dirname(__FILE__))
|
7
|
+
|
8
|
+
require File.join('arel_converter', 'base')
|
9
|
+
require File.join('arel_converter', 'command')
|
10
|
+
require File.join('arel_converter', 'formatter')
|
11
|
+
require File.join('arel_converter', 'active_record_finder')
|
12
|
+
require File.join('arel_converter', 'scope')
|
13
|
+
require File.join('arel_converter', 'association')
|
14
|
+
require File.join('arel_converter', 'replacement')
|
15
|
+
|
16
|
+
# Translators
|
17
|
+
require File.join('arel_converter', 'translators', 'base')
|
18
|
+
require File.join('arel_converter', 'translators', 'options')
|
19
|
+
require File.join('arel_converter', 'translators', 'scope')
|
20
|
+
require File.join('arel_converter', 'translators', 'finder')
|
21
|
+
require File.join('arel_converter', 'translators', 'association')
|
22
|
+
|
23
|
+
module ArelConverter
|
24
|
+
|
25
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module ArelConverter
|
2
|
+
class ActiveRecordFinder < Base
|
3
|
+
|
4
|
+
def grep_matches_in_file(file)
|
5
|
+
raw_ar_finders = ''
|
6
|
+
["find(:all", "find(:first", "find.*:conditions =>", '\.all(', '\.first('].each do |v|
|
7
|
+
raw_ar_finders += `grep -hr '#{v}' #{file}`
|
8
|
+
end
|
9
|
+
|
10
|
+
raw_ar_finders.split("\n")
|
11
|
+
end
|
12
|
+
|
13
|
+
def process_line(finder)
|
14
|
+
ArelConverter::Translator::Finder.translate(finder)
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module ArelConverter
|
2
|
+
class Association < Base
|
3
|
+
|
4
|
+
def grep_matches_in_file(file)
|
5
|
+
raw_named_scopes = `grep -hr "^\s*has_\\|belongs_to" #{file}`
|
6
|
+
raw_named_scopes.split("\n")
|
7
|
+
end
|
8
|
+
|
9
|
+
def process_line(line)
|
10
|
+
ArelConverter::Translator::Association.translate(line)
|
11
|
+
end
|
12
|
+
|
13
|
+
def verify_line(line)
|
14
|
+
parser = RubyParser.new
|
15
|
+
sexp = parser.process(line)
|
16
|
+
sexp.shift == :call
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
module ArelConverter
|
2
|
+
class Base
|
3
|
+
|
4
|
+
attr_accessor :options
|
5
|
+
|
6
|
+
def initialize(path, options = {})
|
7
|
+
@path = path
|
8
|
+
@options = {dry_run: false}.merge(options)
|
9
|
+
end
|
10
|
+
|
11
|
+
def run!
|
12
|
+
File.directory?(@path) ? parse_directory(@path) : parse_file(@path)
|
13
|
+
end
|
14
|
+
|
15
|
+
def parse_directory(path)
|
16
|
+
Dir[File.join(path, '**/*.rb')].each do |file|
|
17
|
+
begin
|
18
|
+
parse_file(file)
|
19
|
+
rescue => e
|
20
|
+
Formatter.alert(file, [])
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def parse_file(file)
|
26
|
+
|
27
|
+
lines_to_process = grep_matches_in_file(file)
|
28
|
+
|
29
|
+
return if lines_to_process.empty?
|
30
|
+
|
31
|
+
replacements = process_lines(lines_to_process)
|
32
|
+
|
33
|
+
unless (replacements.nil? || replacements.empty?)
|
34
|
+
Formatter.alert(file, replacements)
|
35
|
+
update_file(file, replacements) unless @options[:dry_run]
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def process_lines(lines)
|
40
|
+
lines.map do |line|
|
41
|
+
r = Replacement.new(line)
|
42
|
+
begin
|
43
|
+
next unless verify_line(line)
|
44
|
+
r.new_content = process_line(line)
|
45
|
+
rescue SyntaxError => e
|
46
|
+
r.error = "SyntaxError when evaluating options for #{line}"
|
47
|
+
rescue Exception => e
|
48
|
+
r.error = "#{e.class} #{e.message} when evaluating options for \"#{line}\"\n#{e.backtrace.first}"
|
49
|
+
end
|
50
|
+
r
|
51
|
+
end.compact
|
52
|
+
end
|
53
|
+
|
54
|
+
def update_file(file, line_replacements)
|
55
|
+
contents = File.read(file)
|
56
|
+
line_replacements.each do |r|
|
57
|
+
contents.gsub!(r.old_content, r.new_content) if r.valid?
|
58
|
+
end
|
59
|
+
|
60
|
+
File.open(file, 'w') do |f|
|
61
|
+
f.puts contents
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def grep_matches_in_file(file)
|
66
|
+
[] # abstract method overriden by subclasses
|
67
|
+
end
|
68
|
+
|
69
|
+
protected
|
70
|
+
|
71
|
+
def verify_line(line)
|
72
|
+
true
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
|
3
|
+
module ArelConverter
|
4
|
+
|
5
|
+
class Command
|
6
|
+
|
7
|
+
attr_accessor :options
|
8
|
+
|
9
|
+
def initialize(*args)
|
10
|
+
args << '--help' if args.empty?
|
11
|
+
@translators = ['scope','finder','association']
|
12
|
+
@options = {}
|
13
|
+
parse_argv(*args)
|
14
|
+
end
|
15
|
+
|
16
|
+
def run!
|
17
|
+
if @translators.include?('association')
|
18
|
+
puts "== Checking Associations"
|
19
|
+
ArelConverter::Association.new(options[:path], options).run!
|
20
|
+
end
|
21
|
+
|
22
|
+
if @translators.include?('scope')
|
23
|
+
puts "\n== Checking Scopes"
|
24
|
+
ArelConverter::Scope.new(options[:path], options).run!
|
25
|
+
end
|
26
|
+
|
27
|
+
if @translators.include?('finder')
|
28
|
+
puts "\n== Checking Finders"
|
29
|
+
ArelConverter::ActiveRecordFinder.new(options[:path], options).run!
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def parse_argv(*args)
|
36
|
+
OptionParser.new do |opts|
|
37
|
+
opts.banner = "Usage: arel_convert [options] [PATH]"
|
38
|
+
opts.on('-t', '--translators [scope,finder,association]', Array, 'Specify specific translators') { |list| @translators = list }
|
39
|
+
opts.on('--dry-run', 'Run a simulated update, files will not be updated' ) { |v| options[:dry_run] = true }
|
40
|
+
opts.on('-h', '--help', 'Display this screen' ) do
|
41
|
+
puts opts
|
42
|
+
exit 0
|
43
|
+
end
|
44
|
+
end.parse!(args)
|
45
|
+
options[:path] = args.empty? ? '.' : args.shift
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module ArelConverter
|
2
|
+
class Formatter
|
3
|
+
|
4
|
+
# Terminal colors, borrowed from Thor
|
5
|
+
CLEAR = "\e[0m"
|
6
|
+
BOLD = "\e[1m"
|
7
|
+
RED = "\e[31m"
|
8
|
+
YELLOW = "\e[33m"
|
9
|
+
CYAN = "\e[36m"
|
10
|
+
WHITE = "\e[37m"
|
11
|
+
|
12
|
+
# Show an upgrade alert to the user
|
13
|
+
def self.alert(title, culprits, errors=nil)
|
14
|
+
if RbConfig::CONFIG['host_os'].downcase =~ /mswin|windows|mingw/
|
15
|
+
Formatter.basic_alert(title, culprits)
|
16
|
+
else
|
17
|
+
Formatter.color_alert(title, culprits)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# Show an upgrade alert to the user. If we're on Windows, we can't
|
22
|
+
# use terminal colors, hence this method.
|
23
|
+
def self.basic_alert(title, culprits)
|
24
|
+
puts "** " + title
|
25
|
+
Array(culprits).each do |c|
|
26
|
+
puts c.valid? ? " FROM: #{c.old_content}\n TO: #{c.new_content}\n" :
|
27
|
+
"** ERROR - #{c.error}"
|
28
|
+
end
|
29
|
+
puts
|
30
|
+
end
|
31
|
+
|
32
|
+
# Show a colorful alert to the user
|
33
|
+
def self.color_alert(file, culprits )
|
34
|
+
puts "#{RED}#{BOLD}#{file}#{CLEAR}"
|
35
|
+
Array(culprits).each do |c|
|
36
|
+
puts c.valid? ? "#{YELLOW} FROM: #{c.old_content}\n TO: #{c.new_content}\n" :
|
37
|
+
"#{CYAN}#{BOLD} - #{c.error}#{CLEAR}"
|
38
|
+
|
39
|
+
end
|
40
|
+
ensure
|
41
|
+
puts "#{CLEAR}"
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|