skeema 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.
- data/.gitignore +17 -0
- data/Gemfile +2 -0
- data/LICENSE +22 -0
- data/README.md +17 -0
- data/Rakefile +13 -0
- data/lib/skeema/pretender.rb +50 -0
- data/lib/skeema/regex.rb +60 -0
- data/lib/skeema/ripper.rb +38 -0
- data/lib/skeema/version.rb +3 -0
- data/skeema.gemspec +21 -0
- data/spec/fixtures/blog.rb +16 -0
- data/spec/fixtures/empty.rb +3 -0
- data/spec/skeema/pretender_spec.rb +14 -0
- data/spec/skeema/regex_spec.rb +43 -0
- data/spec/skeema/ripper_spec.rb +14 -0
- data/spec/spec_helper.rb +15 -0
- metadata +101 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 Solomon White
|
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,17 @@
|
|
1
|
+
# Skeema
|
2
|
+
|
3
|
+
Three different implementations of parsing ActiveRecord schema.rb files
|
4
|
+
|
5
|
+
## Examples:
|
6
|
+
|
7
|
+
# use the regex-based implementation
|
8
|
+
Skeema::Regex.parse('path/to/schema.rb')
|
9
|
+
# => { 'authors' => ['name'], 'posts' => ['author_id', 'title', 'body'] }
|
10
|
+
|
11
|
+
# use the instance_eval-based implementation
|
12
|
+
Skeema::Pretender.parse('path/to/schema.rb')
|
13
|
+
# => { :authors => [:name], :posts => [:author_id, :title, :body] }
|
14
|
+
|
15
|
+
# use the ripper-based implementation
|
16
|
+
Skeema::Ripper.parse('path/to/schema.rb')
|
17
|
+
# => { :authors => [:name], :posts => [:author_id, :title, :body] }
|
data/Rakefile
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'bundler/gem_tasks'
|
2
|
+
require 'rspec/core/rake_task'
|
3
|
+
|
4
|
+
desc "Run specs"
|
5
|
+
task :spec do
|
6
|
+
RSpec::Core::RakeTask.new(:spec) do |t|
|
7
|
+
t.rspec_opts = %w{--colour --format progress}
|
8
|
+
t.pattern = 'spec/**/*_spec.rb'
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
desc "Default: run specs."
|
13
|
+
task :default => :spec
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
class Schema
|
3
|
+
def create_table(table_name, options={}, &block)
|
4
|
+
table = Table.new(table_name)
|
5
|
+
yield table if block_given?
|
6
|
+
self.class.add_table(table.name, table.columns)
|
7
|
+
end
|
8
|
+
|
9
|
+
def method_missing(*args)
|
10
|
+
# ignore add_index, etc.
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.define(*args, &block)
|
14
|
+
new.instance_eval(&block)
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.add_table(table_name, columns)
|
18
|
+
@schema ||= {}
|
19
|
+
@schema[table_name] = columns
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.schema
|
23
|
+
@schema || {}
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
class Table
|
28
|
+
attr_reader :name, :columns
|
29
|
+
|
30
|
+
def initialize(name)
|
31
|
+
@name = name
|
32
|
+
@columns = []
|
33
|
+
end
|
34
|
+
|
35
|
+
def method_missing(data_type, *column_names_and_options)
|
36
|
+
column_names_and_options.pop if column_names_and_options.last.is_a?(Hash)
|
37
|
+
@columns += column_names_and_options
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
module Skeema
|
43
|
+
class Pretender
|
44
|
+
def self.parse(filename)
|
45
|
+
require filename
|
46
|
+
|
47
|
+
ActiveRecord::Schema.schema
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
data/lib/skeema/regex.rb
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
module Skeema
|
2
|
+
class Regex
|
3
|
+
attr_reader :schema
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@schema = {}
|
7
|
+
end
|
8
|
+
|
9
|
+
def parse(filename)
|
10
|
+
File.open(filename).each_line { |line| process_line(line) }
|
11
|
+
self
|
12
|
+
end
|
13
|
+
|
14
|
+
def process_line(line)
|
15
|
+
in_table? ? process_line_in_table(line) : process_line_outside(line)
|
16
|
+
end
|
17
|
+
|
18
|
+
def process_line_in_table(line)
|
19
|
+
append_columns extract_column_names(line)
|
20
|
+
end_table if line.strip == "end"
|
21
|
+
end
|
22
|
+
|
23
|
+
def process_line_outside(line)
|
24
|
+
table_name = extract_table_name(line)
|
25
|
+
start_table(table_name) if table_name
|
26
|
+
end
|
27
|
+
|
28
|
+
def extract_table_name(line)
|
29
|
+
line.scan(/create_table :(\S+)/).flatten.first
|
30
|
+
end
|
31
|
+
|
32
|
+
def extract_column_names(line)
|
33
|
+
return [] unless line =~ /^\s*t\.(integer|string|text|boolean|date)/
|
34
|
+
without_options = line.split(/(\w+:|:\w+\s*=>)/).first
|
35
|
+
without_options.gsub!(/t\.\w+\s+/, '')
|
36
|
+
without_options.split(/\,\s*/).map{|c| c.gsub(/:/, '').strip }
|
37
|
+
end
|
38
|
+
|
39
|
+
def append_columns(columns)
|
40
|
+
@schema[@table] += columns
|
41
|
+
end
|
42
|
+
|
43
|
+
def start_table(table_name)
|
44
|
+
@table = table_name
|
45
|
+
@schema[@table] = []
|
46
|
+
end
|
47
|
+
|
48
|
+
def end_table
|
49
|
+
@table = nil
|
50
|
+
end
|
51
|
+
|
52
|
+
def in_table?
|
53
|
+
! @table.nil?
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.parse(filename)
|
57
|
+
new.parse(filename).schema
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'ripper'
|
2
|
+
|
3
|
+
module Skeema
|
4
|
+
class Ripper
|
5
|
+
def parse(filename)
|
6
|
+
sexp = ::Ripper.sexp IO.read(filename)
|
7
|
+
|
8
|
+
# yay, magic!
|
9
|
+
schema_commands = sexp[1][0][2][2]
|
10
|
+
@schema = schema_commands.inject({}) do |s, command|
|
11
|
+
if command.length > 1
|
12
|
+
command_name = command[1][1][1]
|
13
|
+
|
14
|
+
if command_name == "create_table"
|
15
|
+
table_name = command[1][2][1][0][1][1][1].to_sym
|
16
|
+
columns = command[2][2].each.map do |col_command|
|
17
|
+
col_command[4][1][0][1][1][1].to_sym
|
18
|
+
end
|
19
|
+
|
20
|
+
s.merge!(table_name => columns)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
s
|
25
|
+
end
|
26
|
+
|
27
|
+
self
|
28
|
+
end
|
29
|
+
|
30
|
+
def schema
|
31
|
+
@schema
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.parse(filename)
|
35
|
+
new.parse(filename).schema
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
data/skeema.gemspec
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/skeema/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["Solomon White"]
|
6
|
+
gem.email = ["rubysolo@gmail.com"]
|
7
|
+
gem.description = %q{Skeema}
|
8
|
+
gem.summary = %q{Skeema is an experiment in different methods of parsing ActiveRecord schema.rb files}
|
9
|
+
gem.homepage = "https://github.com/rubysolo/skeema"
|
10
|
+
|
11
|
+
gem.add_development_dependency 'pry-nav'
|
12
|
+
gem.add_development_dependency 'rake'
|
13
|
+
gem.add_development_dependency 'rspec'
|
14
|
+
|
15
|
+
gem.files = `git ls-files`.split($\)
|
16
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
17
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
18
|
+
gem.name = "skeema"
|
19
|
+
gem.require_paths = ["lib"]
|
20
|
+
gem.version = Skeema::VERSION
|
21
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
ActiveRecord::Schema.define do
|
2
|
+
create_table :authors do |t|
|
3
|
+
t.string :name, :null => false
|
4
|
+
end
|
5
|
+
|
6
|
+
add_index :authors, :name, :unique
|
7
|
+
|
8
|
+
create_table :posts do |t|
|
9
|
+
t.integer :author_id, :null => false
|
10
|
+
t.string :subject
|
11
|
+
t.text :body
|
12
|
+
t.boolean :private, :default => false
|
13
|
+
end
|
14
|
+
|
15
|
+
add_index :posts, :author_id
|
16
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'skeema/pretender'
|
3
|
+
|
4
|
+
describe Skeema::Pretender do
|
5
|
+
it 'parses an empty schema' do
|
6
|
+
described_class.parse(fixture_path "empty.rb").should eq({})
|
7
|
+
end
|
8
|
+
|
9
|
+
it 'extracts table info from schema.rb' do
|
10
|
+
schema = described_class.parse(fixture_path "blog.rb")
|
11
|
+
schema.keys.sort.should eq [:authors, :posts]
|
12
|
+
schema[:posts].should eq [:author_id, :subject, :body, :private]
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'skeema/regex'
|
3
|
+
|
4
|
+
describe Skeema::Regex do
|
5
|
+
it 'extracts table name from create_table statement' do
|
6
|
+
subject.extract_table_name('create_table :authors do |t|').should eq 'authors'
|
7
|
+
end
|
8
|
+
|
9
|
+
it 'returns nil as table name for non-matching statements' do
|
10
|
+
subject.extract_table_name('t.string :subject').should be_nil
|
11
|
+
subject.extract_table_name('add_index :posts, :author_id').should be_nil
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'extracts column names from column definitions' do
|
15
|
+
subject.extract_column_names('t.string :subject').should eq %w( subject )
|
16
|
+
subject.extract_column_names('t.string :subject, :abstract').should eq %w( subject abstract )
|
17
|
+
subject.extract_column_names('t.string :subject, :abstract, :null => false').should eq %w( subject abstract )
|
18
|
+
subject.extract_column_names('t.string :subject, :abstract, null: false').should eq %w( subject abstract )
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'detects start of new table definition' do
|
22
|
+
subject.should_receive(:start_table).with('authors')
|
23
|
+
subject.process_line('create_table :authors do |t|')
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'detects end of table definition' do
|
27
|
+
subject.stub(:in_table?).and_return(true)
|
28
|
+
subject.stub(:append_columns)
|
29
|
+
|
30
|
+
subject.should_receive(:end_table)
|
31
|
+
subject.process_line(' end')
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'parses an empty schema' do
|
35
|
+
described_class.parse("spec/fixtures/empty.rb").should eq({})
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'extracts table info from schema.rb' do
|
39
|
+
schema = described_class.parse("spec/fixtures/blog.rb")
|
40
|
+
schema.keys.sort.should eq %w( authors posts )
|
41
|
+
schema['posts'].should eq %w( author_id subject body private )
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'skeema/ripper'
|
3
|
+
|
4
|
+
describe Skeema::Ripper do
|
5
|
+
it 'parses an empty schema' do
|
6
|
+
described_class.parse(fixture_path "empty.rb").should eq({})
|
7
|
+
end
|
8
|
+
|
9
|
+
it 'extracts table info from schema.rb' do
|
10
|
+
schema = described_class.parse(fixture_path "blog.rb")
|
11
|
+
schema.keys.sort.should eq [:authors, :posts]
|
12
|
+
schema[:posts].should eq [:author_id, :subject, :body, :private]
|
13
|
+
end
|
14
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
|
3
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '../lib'))
|
4
|
+
|
5
|
+
require 'pry-nav'
|
6
|
+
require 'rspec'
|
7
|
+
require 'rspec/autorun'
|
8
|
+
|
9
|
+
def fixture_path(filename)
|
10
|
+
File.join(File.dirname(__FILE__), "fixtures/#{ filename }")
|
11
|
+
end
|
12
|
+
|
13
|
+
RSpec.configure do |config|
|
14
|
+
config.mock_with :rspec
|
15
|
+
end
|
metadata
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: skeema
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Solomon White
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-04-18 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: pry-nav
|
16
|
+
requirement: &70335173036980 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *70335173036980
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: rake
|
27
|
+
requirement: &70335173050880 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ! '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0'
|
33
|
+
type: :development
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *70335173050880
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: rspec
|
38
|
+
requirement: &70335173050300 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ! '>='
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
44
|
+
type: :development
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *70335173050300
|
47
|
+
description: Skeema
|
48
|
+
email:
|
49
|
+
- rubysolo@gmail.com
|
50
|
+
executables: []
|
51
|
+
extensions: []
|
52
|
+
extra_rdoc_files: []
|
53
|
+
files:
|
54
|
+
- .gitignore
|
55
|
+
- Gemfile
|
56
|
+
- LICENSE
|
57
|
+
- README.md
|
58
|
+
- Rakefile
|
59
|
+
- lib/skeema/pretender.rb
|
60
|
+
- lib/skeema/regex.rb
|
61
|
+
- lib/skeema/ripper.rb
|
62
|
+
- lib/skeema/version.rb
|
63
|
+
- skeema.gemspec
|
64
|
+
- spec/fixtures/blog.rb
|
65
|
+
- spec/fixtures/empty.rb
|
66
|
+
- spec/skeema/pretender_spec.rb
|
67
|
+
- spec/skeema/regex_spec.rb
|
68
|
+
- spec/skeema/ripper_spec.rb
|
69
|
+
- spec/spec_helper.rb
|
70
|
+
homepage: https://github.com/rubysolo/skeema
|
71
|
+
licenses: []
|
72
|
+
post_install_message:
|
73
|
+
rdoc_options: []
|
74
|
+
require_paths:
|
75
|
+
- lib
|
76
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
77
|
+
none: false
|
78
|
+
requirements:
|
79
|
+
- - ! '>='
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: '0'
|
82
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
83
|
+
none: false
|
84
|
+
requirements:
|
85
|
+
- - ! '>='
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
version: '0'
|
88
|
+
requirements: []
|
89
|
+
rubyforge_project:
|
90
|
+
rubygems_version: 1.8.11
|
91
|
+
signing_key:
|
92
|
+
specification_version: 3
|
93
|
+
summary: Skeema is an experiment in different methods of parsing ActiveRecord schema.rb
|
94
|
+
files
|
95
|
+
test_files:
|
96
|
+
- spec/fixtures/blog.rb
|
97
|
+
- spec/fixtures/empty.rb
|
98
|
+
- spec/skeema/pretender_spec.rb
|
99
|
+
- spec/skeema/regex_spec.rb
|
100
|
+
- spec/skeema/ripper_spec.rb
|
101
|
+
- spec/spec_helper.rb
|