static-record 1.0.0.pre → 1.0.0.pre.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.rdoc +4 -1
- data/lib/static_record.rb +8 -0
- data/lib/static_record/concerns/query_building_concern.rb +105 -0
- data/lib/static_record/concerns/sqlite_storing_concern.rb +63 -0
- data/{app/models/static_record → lib/static_record/models}/base.rb +0 -0
- data/{app/models/static_record → lib/static_record/models}/predicates.rb +0 -0
- data/{app/models/static_record → lib/static_record/models}/querying.rb +0 -0
- data/{app/models/static_record → lib/static_record/models}/relation.rb +0 -0
- data/lib/static_record/version.rb +1 -1
- metadata +7 -7
- data/app/models/concerns/query_building_concern.rb +0 -103
- data/app/models/concerns/sqlite_storing_concern.rb +0 -61
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a561fe2ae524c134408db76ff95bad7e8b5f11c9
|
4
|
+
data.tar.gz: c0a00cf2f7342dfd97d5ebe4e18c650ba50abc81
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5f9aaf4e4bdeb208ea94137af0d1a2f0833792636c05d01aff82ccfb070e61e162e43decf9836bd1bc89e6c1b01b589b6eab058d8a74c2deb1837b506e4048f2
|
7
|
+
data.tar.gz: 9e62c0b5d76b45aec821bc2d588256bd49bf3cece1d322185c9eafb769263234b2a5a90abcdc14ba2f890f00d955d9e340579dc64dca91ff6f15fb067f4343d3
|
data/README.rdoc
CHANGED
@@ -10,10 +10,13 @@ You can use it when you need several files inheriting a base class.
|
|
10
10
|
|
11
11
|
Add this to your Gemfile:
|
12
12
|
|
13
|
-
gem '
|
13
|
+
gem 'static_record', require: 'static_record'
|
14
14
|
|
15
15
|
and run the bundle install command.
|
16
16
|
|
17
|
+
The 'require' part is important so that Rails autoloads the library correctly.
|
18
|
+
|
19
|
+
|
17
20
|
== Getting Started
|
18
21
|
|
19
22
|
=== Base class
|
data/lib/static_record.rb
CHANGED
@@ -1,5 +1,13 @@
|
|
1
1
|
require 'static_record/engine'
|
2
2
|
require 'static_record/exceptions'
|
3
3
|
|
4
|
+
require 'static_record/concerns/query_building_concern'
|
5
|
+
require 'static_record/concerns/sqlite_storing_concern'
|
6
|
+
|
7
|
+
require 'static_record/models/predicates'
|
8
|
+
require 'static_record/models/querying'
|
9
|
+
require 'static_record/models/relation'
|
10
|
+
require 'static_record/models/base'
|
11
|
+
|
4
12
|
module StaticRecord
|
5
13
|
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
module StaticRecord
|
2
|
+
# Helps building SQL queries
|
3
|
+
module QueryBuildingConcern
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
def build_query
|
7
|
+
sql = sql_select_from
|
8
|
+
sql += sql_where unless @where_clauses.empty?
|
9
|
+
sql += sql_order unless @order_by.empty?
|
10
|
+
sql += sql_limit_offset if @sql_limit
|
11
|
+
sql
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def sql_select_from
|
17
|
+
sql = "SELECT #{@columns} FROM #{@table}"
|
18
|
+
end
|
19
|
+
|
20
|
+
def sql_where
|
21
|
+
" WHERE #{where_clause_builder}"
|
22
|
+
end
|
23
|
+
|
24
|
+
def sql_order
|
25
|
+
ord_sql = ''
|
26
|
+
@order_by.each do |ord|
|
27
|
+
ord_sql += ord_sql.empty? ? ' ORDER BY' : ', '
|
28
|
+
case ord.class.name
|
29
|
+
when Hash.name
|
30
|
+
ord_sql += ord.map { |k, v| " #{@table}.#{k.to_s} #{v.to_s.upcase}" }.join(',')
|
31
|
+
when Array.name
|
32
|
+
ord_sql += ord.map { |sym| " #{@table}.#{sym.to_s} ASC" }.join(',')
|
33
|
+
when Symbol.name
|
34
|
+
ord_sql += " #{@table}.#{ord.to_s} ASC"
|
35
|
+
when String.name
|
36
|
+
ord_sql += " #{ord}"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
ord_sql
|
40
|
+
end
|
41
|
+
|
42
|
+
def sql_limit_offset
|
43
|
+
sql = " LIMIT #{@sql_limit}"
|
44
|
+
sql += " OFFSET #{@sql_offset}" if @sql_offset
|
45
|
+
sql
|
46
|
+
end
|
47
|
+
|
48
|
+
def where_clause_builder
|
49
|
+
params = []
|
50
|
+
@where_clauses.map do |clause|
|
51
|
+
subquery = clause[:q]
|
52
|
+
if subquery.is_a?(Hash)
|
53
|
+
params << where_clause_from_hash(clause, subquery)
|
54
|
+
elsif subquery.is_a?(String)
|
55
|
+
params << where_clause_from_string(clause, subquery)
|
56
|
+
end
|
57
|
+
|
58
|
+
if params.size > 1
|
59
|
+
joint = clause[:chain] == :or ? 'OR' : 'AND'
|
60
|
+
params = [params.join(" #{joint} ")]
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
params.first
|
65
|
+
end
|
66
|
+
|
67
|
+
def where_clause_from_hash(clause, subquery)
|
68
|
+
parts = subquery.keys.map do |key|
|
69
|
+
value = subquery[key]
|
70
|
+
part = ''
|
71
|
+
if value.is_a?(Array)
|
72
|
+
# ex: where(name: ['John', 'Jack'])
|
73
|
+
# use IN operator
|
74
|
+
value.map! { |v| v =~ /^\d+$/ ? v : "\"#{v}\"" }
|
75
|
+
inverse = 'NOT ' if clause[:operator] == :not_eq
|
76
|
+
part = "#{key.to_s} #{inverse}IN (#{value.join(',')})"
|
77
|
+
else
|
78
|
+
# ex: where(name: 'John')
|
79
|
+
# use = operator
|
80
|
+
inverse = '!' if clause[:operator] == :not_eq
|
81
|
+
part = "#{key.to_s} #{inverse}= '#{value}'"
|
82
|
+
end
|
83
|
+
end
|
84
|
+
parts.join(' AND ')
|
85
|
+
end
|
86
|
+
|
87
|
+
def where_clause_from_string(clause, subquery)
|
88
|
+
final_string = subquery
|
89
|
+
if clause[:parameters].is_a?(Array)
|
90
|
+
# Anon parameters
|
91
|
+
# ex: where("name = ? OR name = ?", 'John', 'Jack')
|
92
|
+
clause[:parameters].each do |param|
|
93
|
+
final_string.sub!(/\?/, "\"#{param}\"")
|
94
|
+
end
|
95
|
+
elsif clause[:parameters].is_a?(Hash)
|
96
|
+
# Named parameters (placeholder condition)
|
97
|
+
# ex: where("name = :one OR name = :two", one: 'John', two: 'Smith')
|
98
|
+
clause[:parameters].each do |key, value|
|
99
|
+
final_string.sub!(":#{key}", "\"#{value}\"")
|
100
|
+
end
|
101
|
+
end
|
102
|
+
final_string
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module StaticRecord
|
2
|
+
# Reads ruby files whose path matches path pattern and store them
|
3
|
+
# as records in an SQLite3 database
|
4
|
+
module SqliteStoringConcern
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
module ClassMethods # :nodoc:
|
8
|
+
def create_store
|
9
|
+
columns = class_variable_get(:@@_columns)
|
10
|
+
begin
|
11
|
+
dbname = Rails.root.join('db', "static_#{store}.sqlite3").to_s
|
12
|
+
SQLite3::Database.new(dbname)
|
13
|
+
db = SQLite3::Database.open(dbname)
|
14
|
+
db.execute("DROP TABLE IF EXISTS #{store}")
|
15
|
+
create_table(db, columns)
|
16
|
+
load_records.each_with_index do |record, index|
|
17
|
+
insert_into_database(db, record, index, columns)
|
18
|
+
end
|
19
|
+
rescue SQLite3::Exception => e
|
20
|
+
puts 'Exception occurred', e
|
21
|
+
ensure
|
22
|
+
db.close if db
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def create_table(db, columns)
|
29
|
+
cols = columns.map { |c| c.to_s + ' TEXT' }.join(', ')
|
30
|
+
sql = "CREATE TABLE #{store}(id INTEGER PRIMARY KEY, klass TEXT, #{cols})"
|
31
|
+
db.execute(sql)
|
32
|
+
end
|
33
|
+
|
34
|
+
def insert_into_database(db, record, index, columns)
|
35
|
+
attrs = record.constantize.new.attributes
|
36
|
+
sqlized = [index.to_s, "'#{record}'"] # id, klass
|
37
|
+
sqlized += columns.map { |c| "'#{attrs[c]}'" } # model's attributes
|
38
|
+
db.execute("INSERT INTO #{store} VALUES(#{sqlized.join(', ')})")
|
39
|
+
end
|
40
|
+
|
41
|
+
def load_records
|
42
|
+
records = []
|
43
|
+
Dir.glob(path_pattern) do |filepath|
|
44
|
+
klass = get_class_from_file(filepath)
|
45
|
+
if klass
|
46
|
+
require filepath
|
47
|
+
records << klass
|
48
|
+
end
|
49
|
+
end
|
50
|
+
records
|
51
|
+
end
|
52
|
+
|
53
|
+
def get_class_from_file(filepath)
|
54
|
+
klass = nil
|
55
|
+
File.open(filepath) do |file|
|
56
|
+
match = file.grep(/class\s+([a-zA-Z0-9_]+)/)
|
57
|
+
klass = match.first.chomp.gsub(/class\s+/, '').split(' ')[0] if match
|
58
|
+
end
|
59
|
+
klass
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: static-record
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.0.pre
|
4
|
+
version: 1.0.0.pre.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Hugo Chevalier
|
@@ -99,17 +99,17 @@ files:
|
|
99
99
|
- app/assets/stylesheets/static_record/application.css
|
100
100
|
- app/controllers/static_record/application_controller.rb
|
101
101
|
- app/helpers/static_record/application_helper.rb
|
102
|
-
- app/models/concerns/query_building_concern.rb
|
103
|
-
- app/models/concerns/sqlite_storing_concern.rb
|
104
|
-
- app/models/static_record/base.rb
|
105
|
-
- app/models/static_record/predicates.rb
|
106
|
-
- app/models/static_record/querying.rb
|
107
|
-
- app/models/static_record/relation.rb
|
108
102
|
- app/views/layouts/static_record/application.html.erb
|
109
103
|
- config/routes.rb
|
110
104
|
- lib/static_record.rb
|
105
|
+
- lib/static_record/concerns/query_building_concern.rb
|
106
|
+
- lib/static_record/concerns/sqlite_storing_concern.rb
|
111
107
|
- lib/static_record/engine.rb
|
112
108
|
- lib/static_record/exceptions.rb
|
109
|
+
- lib/static_record/models/base.rb
|
110
|
+
- lib/static_record/models/predicates.rb
|
111
|
+
- lib/static_record/models/querying.rb
|
112
|
+
- lib/static_record/models/relation.rb
|
113
113
|
- lib/static_record/version.rb
|
114
114
|
- lib/tasks/static_record_tasks.rake
|
115
115
|
- spec/models/static_record/base_spec.rb
|
@@ -1,103 +0,0 @@
|
|
1
|
-
# Helps building SQL queries
|
2
|
-
module QueryBuildingConcern
|
3
|
-
extend ActiveSupport::Concern
|
4
|
-
|
5
|
-
def build_query
|
6
|
-
sql = sql_select_from
|
7
|
-
sql += sql_where unless @where_clauses.empty?
|
8
|
-
sql += sql_order unless @order_by.empty?
|
9
|
-
sql += sql_limit_offset if @sql_limit
|
10
|
-
sql
|
11
|
-
end
|
12
|
-
|
13
|
-
private
|
14
|
-
|
15
|
-
def sql_select_from
|
16
|
-
sql = "SELECT #{@columns} FROM #{@table}"
|
17
|
-
end
|
18
|
-
|
19
|
-
def sql_where
|
20
|
-
" WHERE #{where_clause_builder}"
|
21
|
-
end
|
22
|
-
|
23
|
-
def sql_order
|
24
|
-
ord_sql = ''
|
25
|
-
@order_by.each do |ord|
|
26
|
-
ord_sql += ord_sql.empty? ? ' ORDER BY' : ', '
|
27
|
-
case ord.class.name
|
28
|
-
when Hash.name
|
29
|
-
ord_sql += ord.map { |k, v| " #{@table}.#{k.to_s} #{v.to_s.upcase}" }.join(',')
|
30
|
-
when Array.name
|
31
|
-
ord_sql += ord.map { |sym| " #{@table}.#{sym.to_s} ASC" }.join(',')
|
32
|
-
when Symbol.name
|
33
|
-
ord_sql += " #{@table}.#{ord.to_s} ASC"
|
34
|
-
when String.name
|
35
|
-
ord_sql += " #{ord}"
|
36
|
-
end
|
37
|
-
end
|
38
|
-
ord_sql
|
39
|
-
end
|
40
|
-
|
41
|
-
def sql_limit_offset
|
42
|
-
sql = " LIMIT #{@sql_limit}"
|
43
|
-
sql += " OFFSET #{@sql_offset}" if @sql_offset
|
44
|
-
sql
|
45
|
-
end
|
46
|
-
|
47
|
-
def where_clause_builder
|
48
|
-
params = []
|
49
|
-
@where_clauses.map do |clause|
|
50
|
-
subquery = clause[:q]
|
51
|
-
if subquery.is_a?(Hash)
|
52
|
-
params << where_clause_from_hash(clause, subquery)
|
53
|
-
elsif subquery.is_a?(String)
|
54
|
-
params << where_clause_from_string(clause, subquery)
|
55
|
-
end
|
56
|
-
|
57
|
-
if params.size > 1
|
58
|
-
joint = clause[:chain] == :or ? 'OR' : 'AND'
|
59
|
-
params = [params.join(" #{joint} ")]
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
|
-
params.first
|
64
|
-
end
|
65
|
-
|
66
|
-
def where_clause_from_hash(clause, subquery)
|
67
|
-
parts = subquery.keys.map do |key|
|
68
|
-
value = subquery[key]
|
69
|
-
part = ''
|
70
|
-
if value.is_a?(Array)
|
71
|
-
# ex: where(name: ['John', 'Jack'])
|
72
|
-
# use IN operator
|
73
|
-
value.map! { |v| v =~ /^\d+$/ ? v : "\"#{v}\"" }
|
74
|
-
inverse = 'NOT ' if clause[:operator] == :not_eq
|
75
|
-
part = "#{key.to_s} #{inverse}IN (#{value.join(',')})"
|
76
|
-
else
|
77
|
-
# ex: where(name: 'John')
|
78
|
-
# use = operator
|
79
|
-
inverse = '!' if clause[:operator] == :not_eq
|
80
|
-
part = "#{key.to_s} #{inverse}= '#{value}'"
|
81
|
-
end
|
82
|
-
end
|
83
|
-
parts.join(' AND ')
|
84
|
-
end
|
85
|
-
|
86
|
-
def where_clause_from_string(clause, subquery)
|
87
|
-
final_string = subquery
|
88
|
-
if clause[:parameters].is_a?(Array)
|
89
|
-
# Anon parameters
|
90
|
-
# ex: where("name = ? OR name = ?", 'John', 'Jack')
|
91
|
-
clause[:parameters].each do |param|
|
92
|
-
final_string.sub!(/\?/, "\"#{param}\"")
|
93
|
-
end
|
94
|
-
elsif clause[:parameters].is_a?(Hash)
|
95
|
-
# Named parameters (placeholder condition)
|
96
|
-
# ex: where("name = :one OR name = :two", one: 'John', two: 'Smith')
|
97
|
-
clause[:parameters].each do |key, value|
|
98
|
-
final_string.sub!(":#{key}", "\"#{value}\"")
|
99
|
-
end
|
100
|
-
end
|
101
|
-
final_string
|
102
|
-
end
|
103
|
-
end
|
@@ -1,61 +0,0 @@
|
|
1
|
-
# Reads ruby files whose path matches path pattern and store them
|
2
|
-
# as records in an SQLite3 database
|
3
|
-
module SqliteStoringConcern
|
4
|
-
extend ActiveSupport::Concern
|
5
|
-
|
6
|
-
module ClassMethods # :nodoc:
|
7
|
-
def create_store
|
8
|
-
columns = class_variable_get(:@@_columns)
|
9
|
-
begin
|
10
|
-
dbname = Rails.root.join('db', "static_#{store}.sqlite3").to_s
|
11
|
-
SQLite3::Database.new(dbname)
|
12
|
-
db = SQLite3::Database.open(dbname)
|
13
|
-
db.execute("DROP TABLE IF EXISTS #{store}")
|
14
|
-
create_table(db, columns)
|
15
|
-
load_records.each_with_index do |record, index|
|
16
|
-
insert_into_database(db, record, index, columns)
|
17
|
-
end
|
18
|
-
rescue SQLite3::Exception => e
|
19
|
-
puts 'Exception occurred', e
|
20
|
-
ensure
|
21
|
-
db.close if db
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
private
|
26
|
-
|
27
|
-
def create_table(db, columns)
|
28
|
-
cols = columns.map { |c| c.to_s + ' TEXT' }.join(', ')
|
29
|
-
sql = "CREATE TABLE #{store}(id INTEGER PRIMARY KEY, klass TEXT, #{cols})"
|
30
|
-
db.execute(sql)
|
31
|
-
end
|
32
|
-
|
33
|
-
def insert_into_database(db, record, index, columns)
|
34
|
-
attrs = record.constantize.new.attributes
|
35
|
-
sqlized = [index.to_s, "'#{record}'"] # id, klass
|
36
|
-
sqlized += columns.map { |c| "'#{attrs[c]}'" } # model's attributes
|
37
|
-
db.execute("INSERT INTO #{store} VALUES(#{sqlized.join(', ')})")
|
38
|
-
end
|
39
|
-
|
40
|
-
def load_records
|
41
|
-
records = []
|
42
|
-
Dir.glob(path_pattern) do |filepath|
|
43
|
-
klass = get_class_from_file(filepath)
|
44
|
-
if klass
|
45
|
-
require filepath
|
46
|
-
records << klass
|
47
|
-
end
|
48
|
-
end
|
49
|
-
records
|
50
|
-
end
|
51
|
-
|
52
|
-
def get_class_from_file(filepath)
|
53
|
-
klass = nil
|
54
|
-
File.open(filepath) do |file|
|
55
|
-
match = file.grep(/class\s+([a-zA-Z0-9_]+)/)
|
56
|
-
klass = match.first.chomp.gsub(/class\s+/, '').split(' ')[0] if match
|
57
|
-
end
|
58
|
-
klass
|
59
|
-
end
|
60
|
-
end
|
61
|
-
end
|