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 ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
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
@@ -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
@@ -0,0 +1,3 @@
1
+ module Skeema
2
+ VERSION = "0.0.1"
3
+ 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,3 @@
1
+ ActiveRecord::Schema.define(:version => 20380119000001) do
2
+ end
3
+
@@ -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
@@ -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