static-record 1.1.0 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.rdoc +74 -14
- data/lib/static_record.rb +2 -0
- data/lib/static_record/concerns/getters_setters_concern.rb +43 -0
- data/lib/static_record/concerns/query_building_concern.rb +28 -4
- data/lib/static_record/concerns/request_execution_concern.rb +57 -0
- data/lib/static_record/concerns/sqlite_storing_concern.rb +10 -3
- data/lib/static_record/models/base.rb +14 -26
- data/lib/static_record/models/query_interface/search_modifiers.rb +6 -0
- data/lib/static_record/models/querying.rb +15 -4
- data/lib/static_record/models/relation.rb +11 -46
- data/lib/static_record/version.rb +1 -1
- data/spec/models/relation_spec.rb +15 -6
- data/spec/models/static_record_reference_spec.rb +10 -0
- data/spec/test_app/app/models/article.rb +6 -1
- data/spec/test_app/app/models/articles/article_one.rb +2 -0
- data/spec/test_app/app/models/categories/category_one.rb +4 -0
- data/spec/test_app/app/models/categories/category_two.rb +4 -0
- data/spec/test_app/app/models/category.rb +7 -0
- metadata +12 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 54e3e66a230098bdc375ec07c84e7a7d266872df
|
4
|
+
data.tar.gz: 461d0be8afac337dc13b5cd68fa62729bf2c3842
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 835d40e2bf9e440641cf082d2ba9b9ba33ffbc9bca037e92714fffeaef83ff718c661cf26f74a5d14ca02fc2c43eb69cb35fd051a6000bc4c6bc70a7a72f961b
|
7
|
+
data.tar.gz: 5c6e71af8b1698e0bd03fceb0d9705f3d88815d9a143eac21586fdaa8e37ed78be71ac61cc820956b3d382fda5aab904437b8afc55d8e2b2ba9cb50ab9d15f03
|
data/README.rdoc
CHANGED
@@ -26,7 +26,7 @@ The 'require' part is important so that Rails autoloads the library correctly.
|
|
26
26
|
Create your base class inheriting from StaticRecord::Base.
|
27
27
|
|
28
28
|
class Article < StaticRecord::Base
|
29
|
-
# Declare in which
|
29
|
+
# Declare in which table Articles will be store (always in file db/static_records.sqlite3)
|
30
30
|
table :articles
|
31
31
|
|
32
32
|
# Declare at which path "article" files can be found
|
@@ -37,23 +37,24 @@ Create your base class inheriting from StaticRecord::Base.
|
|
37
37
|
primary_key :name
|
38
38
|
|
39
39
|
# You can specify default values for each column of your static records
|
40
|
-
# Just create a method with name: default_<attribute_name>
|
41
|
-
def default_author
|
40
|
+
# Just create a class method with name: default_<attribute_name>
|
41
|
+
def self.default_author
|
42
42
|
'Default author'
|
43
43
|
end
|
44
44
|
|
45
45
|
# You can override any attribute value
|
46
|
-
# Just create a method with name: override_<attribute_name>
|
47
|
-
def override_cover(cover)
|
46
|
+
# Just create a class method with name: override_<attribute_name>
|
47
|
+
def self.override_cover(cover)
|
48
48
|
return cover if cover.to_s.include?('/')
|
49
49
|
Rails.root.join('public', 'default_cover_path', cover)
|
50
50
|
end
|
51
51
|
|
52
52
|
# Specify which "article" attributes can be queried over with their types
|
53
|
-
columns name:
|
54
|
-
author:
|
55
|
-
rank:
|
56
|
-
cover:
|
53
|
+
columns name: :string,
|
54
|
+
author: :string,
|
55
|
+
rank: :integer,
|
56
|
+
cover: :string,
|
57
|
+
category :static_record
|
57
58
|
end
|
58
59
|
|
59
60
|
At each application startup, an SQLite3 database will be created to store this class' children.
|
@@ -63,6 +64,7 @@ Available column types are
|
|
63
64
|
- :integer
|
64
65
|
- :boolean
|
65
66
|
- :float
|
67
|
+
- :static_record
|
66
68
|
|
67
69
|
=== Child class
|
68
70
|
|
@@ -75,6 +77,10 @@ Create has many child class as you want.
|
|
75
77
|
attribute :rank, 2
|
76
78
|
attribute :cover, Rails.root.join('public', 'articles', 'one.jpg')
|
77
79
|
|
80
|
+
# Define the references to other StaticRecords
|
81
|
+
# These ones can be queried over after a jointure
|
82
|
+
reference :category, Category.find('Category One')
|
83
|
+
|
78
84
|
# Your class can be used as any other Ruby class
|
79
85
|
def initialize
|
80
86
|
@an_instance_variable
|
@@ -94,25 +100,51 @@ In your code, you can perform queries like this one:
|
|
94
100
|
|
95
101
|
Article.where(name: 'Article Two').or.where('rank >= 2').limit(2).offset(3)
|
96
102
|
|
97
|
-
I tried to implement as
|
103
|
+
I tried to implement as much of ActiveRecord's query interface as I could.
|
98
104
|
|
99
|
-
There is still a lot of work before everything is available, but I chose to release the
|
105
|
+
There is still a lot of work before everything is available, but I chose to release the gem nevertheless.
|
100
106
|
|
101
107
|
Here is a full list:
|
102
108
|
* where
|
103
|
-
*
|
104
|
-
*
|
109
|
+
* Article.where(name: 'Article One', author: 'The author')
|
110
|
+
* Article.where(name: ['Article One', 'Article Two'])
|
111
|
+
* Article.where(name: 'Article One').where(author: 'The author')
|
112
|
+
* Article.where("name = 'Article One'")
|
113
|
+
* Article.where("name = ?", 'Article One')
|
114
|
+
* Article.where("name = :name", name: 'Article One')
|
105
115
|
* find (only if a primary key has been set)
|
116
|
+
* Article.find('Article One')
|
106
117
|
* find_by
|
118
|
+
* Article.find_by(author: 'The author')
|
119
|
+
* joins
|
120
|
+
* Article.joins(:categories).where("categories.name = 'Category One'")
|
107
121
|
* not
|
122
|
+
* Article.where.not(author: 'The author')
|
108
123
|
* or
|
124
|
+
* Article.where(author: 'The author').or.where(author: 'Another author')
|
125
|
+
* Article.where.not(name: 'Article One').or.where.not(author: 'The author')
|
109
126
|
* all
|
127
|
+
* Article.where(author: 'The author').all
|
128
|
+
* Article.all
|
110
129
|
* take
|
130
|
+
* Article.take
|
131
|
+
* Article.take(3)
|
111
132
|
* first
|
133
|
+
* Article.first
|
134
|
+
* Article.first(3)
|
112
135
|
* last
|
136
|
+
* Article.last
|
137
|
+
* Article.last(3)
|
113
138
|
* limit
|
139
|
+
* Article.limit(5)
|
114
140
|
* offset
|
141
|
+
* Article.limit(3).offset(2)
|
115
142
|
* order
|
143
|
+
* Article.order(:rank) # Use ASC ordering
|
144
|
+
* Article.order([:rank, :name]) # Use ASC ordering for both columns
|
145
|
+
* Article.order(rank: :asc)
|
146
|
+
* Article.order(rank: :desc, name: :asc)
|
147
|
+
* Article.order("rank DESC")
|
116
148
|
|
117
149
|
== IDs
|
118
150
|
|
@@ -122,6 +154,8 @@ As the database is recreated at each application startup and IDs depend on the i
|
|
122
154
|
|
123
155
|
== References
|
124
156
|
|
157
|
+
=== ActiveRecord
|
158
|
+
|
125
159
|
A migration helper allows your ActiveRecord models to reference a StaticRecord.
|
126
160
|
|
127
161
|
In a migration, use:
|
@@ -153,6 +187,32 @@ If you don't want to name your column with the same name than your StaticRecord
|
|
153
187
|
# In the model
|
154
188
|
has_static_record :any_column_name, class_name: 'Article'
|
155
189
|
|
190
|
+
=== StaticRecord
|
191
|
+
|
192
|
+
You can also use 'reference' instead of 'attribute' to have your StaticRecords reference other ones.
|
193
|
+
|
194
|
+
You must use type :static_record when declaring your column!
|
195
|
+
|
196
|
+
class Article < StaticRecord::Base
|
197
|
+
table :articles
|
198
|
+
path Rails.root.join('app', 'models', 'articles', '**', '*.rb')
|
199
|
+
primary_key :name
|
200
|
+
|
201
|
+
columns name: :string,
|
202
|
+
category :static_record
|
203
|
+
end
|
204
|
+
|
205
|
+
class ArticleOne < Article
|
206
|
+
attribute :name, 'Article one'
|
207
|
+
reference :category, Category.find('Category One')
|
208
|
+
end
|
209
|
+
|
210
|
+
You can query them like this
|
211
|
+
|
212
|
+
Article.where(category: Category.find('Category One'))
|
213
|
+
Article.joins(Category).where("categories.name = 'Category One'")
|
214
|
+
Article.joins("categories").where("categories.name = 'Category One'")
|
215
|
+
|
156
216
|
== Questions?
|
157
217
|
|
158
218
|
If you have any question or doubt regarding StaticRecord which you cannot find the solution to in the documentation, you can send me an email. I'll try to answer in less than 24 hours.
|
@@ -165,4 +225,4 @@ If you find a bug please add an issue on GitHub or fork the project and send a p
|
|
165
225
|
|
166
226
|
- Better documentation
|
167
227
|
- Generators
|
168
|
-
-
|
228
|
+
- Improve jointures
|
data/lib/static_record.rb
CHANGED
@@ -1,8 +1,10 @@
|
|
1
1
|
require 'static_record/engine'
|
2
2
|
require 'static_record/exceptions'
|
3
3
|
|
4
|
+
require 'static_record/concerns/getters_setters_concern'
|
4
5
|
require 'static_record/concerns/query_building_concern'
|
5
6
|
require 'static_record/concerns/sqlite_storing_concern'
|
7
|
+
require 'static_record/concerns/request_execution_concern'
|
6
8
|
|
7
9
|
require 'static_record/models/query_interface/conditioners'
|
8
10
|
require 'static_record/models/query_interface/retrievers'
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module StaticRecord
|
2
|
+
# Contains StaticRecord::Base getters and setters
|
3
|
+
module GettersSettersConcern
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
module ClassMethods # :nodoc:
|
7
|
+
def primary_key(name)
|
8
|
+
err = StaticRecord::Base::RESERVED_ATTRIBUTES.include?("@@#{name}".to_sym)
|
9
|
+
raise StaticRecord::ReservedAttributeName, "#{name} is a reserved name" if err
|
10
|
+
class_variable_set('@@_primary_key', name)
|
11
|
+
end
|
12
|
+
|
13
|
+
def pkey
|
14
|
+
class_variable_defined?(:@@_primary_key) ? class_variable_get('@@_primary_key') : nil
|
15
|
+
end
|
16
|
+
|
17
|
+
def bound_static_tables
|
18
|
+
return {} unless class_variable_defined?(:@@_bound_static_tables)
|
19
|
+
class_variable_get('@@_bound_static_tables')
|
20
|
+
end
|
21
|
+
|
22
|
+
def bound_static_tables=(value)
|
23
|
+
class_variable_set('@@_bound_static_tables', value)
|
24
|
+
end
|
25
|
+
|
26
|
+
def table(store)
|
27
|
+
class_variable_set('@@_store', store.to_s)
|
28
|
+
end
|
29
|
+
|
30
|
+
def store
|
31
|
+
class_variable_get('@@_store')
|
32
|
+
end
|
33
|
+
|
34
|
+
def path(path)
|
35
|
+
class_variable_set('@@_path_pattern', path)
|
36
|
+
end
|
37
|
+
|
38
|
+
def path_pattern
|
39
|
+
class_variable_get('@@_path_pattern')
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -5,6 +5,7 @@ module StaticRecord
|
|
5
5
|
|
6
6
|
def build_query
|
7
7
|
sql = sql_select_from
|
8
|
+
sql += sql_joins unless @joins_clauses.empty?
|
8
9
|
sql += sql_where unless @where_clauses.empty?
|
9
10
|
sql += sql_order unless @order_by.empty?
|
10
11
|
sql += sql_limit_offset if @sql_limit
|
@@ -17,6 +18,13 @@ module StaticRecord
|
|
17
18
|
"SELECT #{@columns} FROM #{@table}"
|
18
19
|
end
|
19
20
|
|
21
|
+
def sql_joins
|
22
|
+
@joins_clauses.map do |joint|
|
23
|
+
" INNER JOIN #{joint} ON "\
|
24
|
+
"#{@store}.#{get_column_for_static_table(joint)} = #{joint}.klass"
|
25
|
+
end.join
|
26
|
+
end
|
27
|
+
|
20
28
|
def sql_where
|
21
29
|
" WHERE #{where_clause_builder}"
|
22
30
|
end
|
@@ -70,14 +78,14 @@ module StaticRecord
|
|
70
78
|
if value.is_a?(Array)
|
71
79
|
# ex: where(name: ['John', 'Jack'])
|
72
80
|
# use IN operator
|
73
|
-
value.map! { |v| v
|
81
|
+
value.map! { |v| cast(v) }
|
74
82
|
inverse = 'NOT ' if clause[:operator] == :not_eq
|
75
83
|
"#{key} #{inverse}IN (#{value.join(',')})"
|
76
84
|
else
|
77
85
|
# ex: where(name: 'John')
|
78
86
|
# use = operator
|
79
87
|
inverse = '!' if clause[:operator] == :not_eq
|
80
|
-
"#{key} #{inverse}=
|
88
|
+
"#{key} #{inverse}= #{cast(value)}"
|
81
89
|
end
|
82
90
|
end
|
83
91
|
parts.join(' AND ')
|
@@ -89,16 +97,32 @@ module StaticRecord
|
|
89
97
|
# Anon parameters
|
90
98
|
# ex: where("name = ? OR name = ?", 'John', 'Jack')
|
91
99
|
clause[:parameters].each do |param|
|
92
|
-
final_string.sub!(/\?/,
|
100
|
+
final_string.sub!(/\?/, cast(param))
|
93
101
|
end
|
94
102
|
elsif clause[:parameters].is_a?(Hash)
|
95
103
|
# Named parameters (placeholder condition)
|
96
104
|
# ex: where("name = :one OR name = :two", one: 'John', two: 'Smith')
|
97
105
|
clause[:parameters].each do |key, value|
|
98
|
-
final_string.sub!(":#{key}",
|
106
|
+
final_string.sub!(":#{key}", cast(value))
|
99
107
|
end
|
100
108
|
end
|
101
109
|
final_string
|
102
110
|
end
|
111
|
+
|
112
|
+
def cast(value)
|
113
|
+
return escape(value.class.name) if value.class < StaticRecord::Base
|
114
|
+
return value.to_s if value =~ /^[+|-]?\d+\.?\d*$/
|
115
|
+
escape(value)
|
116
|
+
end
|
117
|
+
|
118
|
+
def escape(value)
|
119
|
+
return "'#{value}'" unless value.include?('\'')
|
120
|
+
"\"#{value}\""
|
121
|
+
end
|
122
|
+
|
123
|
+
def get_column_for_static_table(table)
|
124
|
+
klass = @klass.constantize
|
125
|
+
klass.bound_static_tables[table.to_sym]
|
126
|
+
end
|
103
127
|
end
|
104
128
|
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module StaticRecord
|
2
|
+
module RequestExecutionConcern # :nodoc:
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
private
|
6
|
+
|
7
|
+
def exec_request(expectancy = :result_set)
|
8
|
+
return build_query if @only_sql
|
9
|
+
|
10
|
+
dbname = Rails.root.join('db', 'static_records.sqlite3').to_s
|
11
|
+
result = get_expected_result_from_database(dbname, expectancy)
|
12
|
+
cast_result(expectancy, result)
|
13
|
+
end
|
14
|
+
|
15
|
+
def get_expected_result_from_database(dbname, expectancy)
|
16
|
+
result = nil
|
17
|
+
|
18
|
+
begin
|
19
|
+
db = SQLite3::Database.open(dbname)
|
20
|
+
query = build_query
|
21
|
+
if expectancy == :integer
|
22
|
+
result = db.get_first_value(query)
|
23
|
+
else
|
24
|
+
statement = db.prepare(query)
|
25
|
+
result_set = statement.execute
|
26
|
+
result = result_set.map { |row| row[1].constantize.new }
|
27
|
+
end
|
28
|
+
rescue SQLite3::Exception => e
|
29
|
+
error, msg = handle_error(e, query)
|
30
|
+
ensure
|
31
|
+
statement.close if statement
|
32
|
+
db.close if db
|
33
|
+
end
|
34
|
+
raise error, msg if error
|
35
|
+
|
36
|
+
result
|
37
|
+
end
|
38
|
+
|
39
|
+
def handle_error(error, query)
|
40
|
+
msg = query ? "#{error} in #{query}" : error
|
41
|
+
[error, msg]
|
42
|
+
end
|
43
|
+
|
44
|
+
def cast_result(expectancy, result)
|
45
|
+
if expectancy == :result_set
|
46
|
+
case @result_type
|
47
|
+
when :array
|
48
|
+
result = [] if result.nil?
|
49
|
+
when :record
|
50
|
+
result = result.empty? ? nil : result.first
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
result
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -8,7 +8,7 @@ module StaticRecord
|
|
8
8
|
def create_store
|
9
9
|
cols = class_variable_get(:@@_columns)
|
10
10
|
begin
|
11
|
-
dbname = Rails.root.join('db',
|
11
|
+
dbname = Rails.root.join('db', 'static_records.sqlite3').to_s
|
12
12
|
SQLite3::Database.new(dbname)
|
13
13
|
db = SQLite3::Database.open(dbname)
|
14
14
|
db.execute("DROP TABLE IF EXISTS #{store}")
|
@@ -37,7 +37,7 @@ module StaticRecord
|
|
37
37
|
|
38
38
|
def column_type_to_sql(ctype)
|
39
39
|
case ctype.to_s
|
40
|
-
when 'string'
|
40
|
+
when 'string', 'static_record'
|
41
41
|
' TEXT'
|
42
42
|
else
|
43
43
|
" #{ctype.to_s.upcase}"
|
@@ -54,7 +54,14 @@ module StaticRecord
|
|
54
54
|
sqlized = [index.to_s, "'#{record}'"]
|
55
55
|
# model's attributes
|
56
56
|
sqlized += cols.map do |name, ctype|
|
57
|
-
ctype
|
57
|
+
case ctype
|
58
|
+
when :integer
|
59
|
+
attrs[name].to_s
|
60
|
+
when :static_record
|
61
|
+
"'#{attrs[name].class.name}'"
|
62
|
+
else
|
63
|
+
"'#{attrs[name]}'"
|
64
|
+
end
|
58
65
|
end
|
59
66
|
|
60
67
|
db.execute("INSERT INTO #{store} VALUES(#{sqlized.join(', ')})")
|
@@ -1,6 +1,7 @@
|
|
1
1
|
module StaticRecord
|
2
2
|
# Class that immutable model instances can inherit from
|
3
3
|
class Base
|
4
|
+
include StaticRecord::GettersSettersConcern
|
4
5
|
include StaticRecord::Querying
|
5
6
|
include StaticRecord::SqliteStoringConcern
|
6
7
|
|
@@ -8,14 +9,16 @@ module StaticRecord
|
|
8
9
|
:@@_columns,
|
9
10
|
:@@_primary_key,
|
10
11
|
:@@_path_pattern,
|
11
|
-
:@@_store
|
12
|
+
:@@_store,
|
13
|
+
:@@_bound_static_tables
|
12
14
|
].freeze
|
13
15
|
|
14
16
|
KNOWN_TYPES = [
|
15
17
|
:string,
|
16
18
|
:boolean,
|
17
19
|
:integer,
|
18
|
-
:float
|
20
|
+
:float,
|
21
|
+
:static_record
|
19
22
|
].freeze
|
20
23
|
|
21
24
|
def initialize
|
@@ -34,30 +37,15 @@ module StaticRecord
|
|
34
37
|
class_variable_set("@@#{name}", value)
|
35
38
|
end
|
36
39
|
|
37
|
-
def self.
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
def self.table(store)
|
48
|
-
class_variable_set('@@_store', store.to_s)
|
49
|
-
end
|
50
|
-
|
51
|
-
def self.store
|
52
|
-
class_variable_get('@@_store')
|
53
|
-
end
|
54
|
-
|
55
|
-
def self.path(path)
|
56
|
-
class_variable_set('@@_path_pattern', path)
|
57
|
-
end
|
58
|
-
|
59
|
-
def self.path_pattern
|
60
|
-
class_variable_get('@@_path_pattern')
|
40
|
+
def self.reference(name, value)
|
41
|
+
attribute(name, value)
|
42
|
+
unless value.class < StaticRecord::Base
|
43
|
+
err = 'Reference only accepts StaticRecords'
|
44
|
+
raise StaticRecord::ClassError, err
|
45
|
+
end
|
46
|
+
tables = superclass.bound_static_tables
|
47
|
+
tables[value.class.store.to_sym] = name
|
48
|
+
superclass.bound_static_tables = tables
|
61
49
|
end
|
62
50
|
|
63
51
|
def attributes
|
@@ -6,15 +6,16 @@ module StaticRecord
|
|
6
6
|
|
7
7
|
module ClassMethods # :nodoc:
|
8
8
|
def method_missing(method_sym, *arguments, &block)
|
9
|
-
|
10
|
-
|
9
|
+
params = relation_params
|
10
|
+
if Relation.new(nil, params).respond_to?(method_sym, true)
|
11
|
+
Relation.new(nil, params).send(method_sym, *arguments, &block)
|
11
12
|
else
|
12
13
|
super
|
13
14
|
end
|
14
15
|
end
|
15
16
|
|
16
17
|
def respond_to?(method_sym, include_private = false)
|
17
|
-
if Relation.new(nil,
|
18
|
+
if Relation.new(nil, relation_params).respond_to?(method_sym, true)
|
18
19
|
true
|
19
20
|
else
|
20
21
|
super
|
@@ -22,12 +23,22 @@ module StaticRecord
|
|
22
23
|
end
|
23
24
|
|
24
25
|
def respond_to_missing?(method_sym, include_private = false)
|
25
|
-
if Relation.new(nil,
|
26
|
+
if Relation.new(nil, relation_params).respond_to_missing?(method_sym, true)
|
26
27
|
true
|
27
28
|
else
|
28
29
|
super
|
29
30
|
end
|
30
31
|
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def relation_params
|
36
|
+
{
|
37
|
+
klass: name,
|
38
|
+
store: store,
|
39
|
+
primary_key: pkey
|
40
|
+
}
|
41
|
+
end
|
31
42
|
end
|
32
43
|
end
|
33
44
|
end
|
@@ -4,6 +4,7 @@ module StaticRecord
|
|
4
4
|
:sql_limit,
|
5
5
|
:sql_offset,
|
6
6
|
:where_clauses,
|
7
|
+
:joins_clauses,
|
7
8
|
:chain,
|
8
9
|
:result_type,
|
9
10
|
:order_by,
|
@@ -11,16 +12,19 @@ module StaticRecord
|
|
11
12
|
|
12
13
|
include StaticRecord::QueryInterface::Interface
|
13
14
|
include StaticRecord::QueryBuildingConcern
|
15
|
+
include StaticRecord::RequestExecutionConcern
|
14
16
|
|
15
17
|
def initialize(previous_node, params)
|
16
18
|
@store = params[:store]
|
17
19
|
@table = params[:store]
|
20
|
+
@klass = params[:klass]
|
18
21
|
@primary_key = params[:primary_key]
|
19
22
|
|
20
23
|
@columns = '*'
|
21
24
|
@sql_limit = nil
|
22
25
|
@sql_offset = nil
|
23
26
|
@where_clauses = []
|
27
|
+
@joins_clauses = []
|
24
28
|
@chain = :and
|
25
29
|
@result_type = :array
|
26
30
|
@order_by = []
|
@@ -31,7 +35,12 @@ module StaticRecord
|
|
31
35
|
|
32
36
|
def method_missing(method_sym, *arguments, &block)
|
33
37
|
if respond_to?(method_sym, true)
|
34
|
-
|
38
|
+
params = {
|
39
|
+
klass: @klass,
|
40
|
+
store: @store,
|
41
|
+
primary_key: @primary_key
|
42
|
+
}
|
43
|
+
Relation.new(self, params).send(method_sym, *arguments, &block)
|
35
44
|
elsif [].respond_to?(method_sym)
|
36
45
|
to_a.send(method_sym)
|
37
46
|
else
|
@@ -58,6 +67,7 @@ module StaticRecord
|
|
58
67
|
@sql_limit = relation.sql_limit
|
59
68
|
@sql_offset = relation.sql_offset
|
60
69
|
@where_clauses = relation.where_clauses.deep_dup
|
70
|
+
@joins_clauses = relation.joins_clauses.deep_dup
|
61
71
|
@chain = relation.chain
|
62
72
|
@result_type = relation.result_type
|
63
73
|
@order_by = relation.order_by.deep_dup
|
@@ -74,50 +84,5 @@ module StaticRecord
|
|
74
84
|
@where_clauses << clause
|
75
85
|
@chain = :and
|
76
86
|
end
|
77
|
-
|
78
|
-
def exec_request(expectancy = :result_set)
|
79
|
-
return build_query if @only_sql
|
80
|
-
|
81
|
-
dbname = Rails.root.join('db', "static_#{@store}.sqlite3").to_s
|
82
|
-
result = get_expected_result_from_database(dbname, expectancy)
|
83
|
-
cast_result(expectancy, result)
|
84
|
-
end
|
85
|
-
|
86
|
-
def get_expected_result_from_database(dbname, expectancy)
|
87
|
-
result = nil
|
88
|
-
|
89
|
-
begin
|
90
|
-
db = SQLite3::Database.open(dbname)
|
91
|
-
if expectancy == :integer
|
92
|
-
result = db.get_first_value(build_query)
|
93
|
-
else
|
94
|
-
statement = db.prepare(build_query)
|
95
|
-
result_set = statement.execute
|
96
|
-
result = result_set.map { |row| row[1].constantize.new }
|
97
|
-
end
|
98
|
-
rescue SQLite3::Exception => e
|
99
|
-
error = e
|
100
|
-
ensure
|
101
|
-
statement.close if statement
|
102
|
-
db.close if db
|
103
|
-
end
|
104
|
-
|
105
|
-
raise error if error
|
106
|
-
|
107
|
-
result
|
108
|
-
end
|
109
|
-
|
110
|
-
def cast_result(expectancy, result)
|
111
|
-
if expectancy == :result_set
|
112
|
-
case @result_type
|
113
|
-
when :array
|
114
|
-
result = [] if result.nil?
|
115
|
-
when :record
|
116
|
-
result = result.empty? ? nil : result.first
|
117
|
-
end
|
118
|
-
end
|
119
|
-
|
120
|
-
result
|
121
|
-
end
|
122
87
|
end
|
123
88
|
end
|
@@ -141,7 +141,7 @@ RSpec.describe StaticRecord::Relation, :type => :model do
|
|
141
141
|
|
142
142
|
it 'accepts array of values' do
|
143
143
|
expect(Article.where(name: ['Article One', 'Article Two']).to_a.size).to eql(2)
|
144
|
-
expect(Article.where(name: ['Article One', 'Article Two']).to_sql).to eql("SELECT * FROM articles WHERE name IN (
|
144
|
+
expect(Article.where(name: ['Article One', 'Article Two']).to_sql).to eql("SELECT * FROM articles WHERE name IN ('Article One','Article Two')")
|
145
145
|
end
|
146
146
|
|
147
147
|
it 'accepts strings' do
|
@@ -151,17 +151,17 @@ RSpec.describe StaticRecord::Relation, :type => :model do
|
|
151
151
|
|
152
152
|
it 'accepts strings followed by an anonymous parameters' do
|
153
153
|
expect(Article.where("name = ?", 'Article Two').first.class).to eql(ArticleTwo)
|
154
|
-
expect(Article.where("name = ?", 'Article Two').to_sql).to eql("SELECT * FROM articles WHERE name =
|
154
|
+
expect(Article.where("name = ?", 'Article Two').to_sql).to eql("SELECT * FROM articles WHERE name = 'Article Two'")
|
155
155
|
end
|
156
156
|
|
157
157
|
it 'accepts strings followed by several anonymous parameters' do
|
158
158
|
expect(Article.where("name = ? AND author = ?", 'Article Two', 'The author').first.class).to eql(ArticleTwo)
|
159
|
-
expect(Article.where("name = ? AND author = ?", 'Article Two', 'The author').to_sql).to eql("SELECT * FROM articles WHERE name =
|
159
|
+
expect(Article.where("name = ? AND author = ?", 'Article Two', 'The author').to_sql).to eql("SELECT * FROM articles WHERE name = 'Article Two' AND author = 'The author'")
|
160
160
|
end
|
161
161
|
|
162
162
|
it 'accepts strings followed by a hash of named parameters' do
|
163
163
|
expect(Article.where("name = :name AND author = :author", {name: 'Article Two', author: 'The author'}).first.class).to eql(ArticleTwo)
|
164
|
-
expect(Article.where("name = :name AND author = :author", {name: 'Article Two', author: 'The author'}).to_sql).to eql("SELECT * FROM articles WHERE name =
|
164
|
+
expect(Article.where("name = :name AND author = :author", {name: 'Article Two', author: 'The author'}).to_sql).to eql("SELECT * FROM articles WHERE name = 'Article Two' AND author = 'The author'")
|
165
165
|
end
|
166
166
|
end
|
167
167
|
|
@@ -178,7 +178,7 @@ RSpec.describe StaticRecord::Relation, :type => :model do
|
|
178
178
|
it 'is possible to chain where.not and where.not clauses' do
|
179
179
|
request = Article.where.not(author: ['The author', 'Me']).where.not(name: 'Article Two')
|
180
180
|
expect(request.last.class.name).to eql(ArticleThree.name)
|
181
|
-
expect(request.to_sql).to eql("SELECT * FROM articles WHERE author NOT IN (
|
181
|
+
expect(request.to_sql).to eql("SELECT * FROM articles WHERE author NOT IN ('The author','Me') AND name != 'Article Two'")
|
182
182
|
end
|
183
183
|
end
|
184
184
|
|
@@ -207,7 +207,7 @@ RSpec.describe StaticRecord::Relation, :type => :model do
|
|
207
207
|
|
208
208
|
it 'accepts array of values' do
|
209
209
|
expect(Article.find(['Article One', 'Article Two']).to_a.size).to eql(2)
|
210
|
-
expect(Article.see_sql_of.find(['Article One', 'Article Two'])).to eql("SELECT * FROM articles WHERE name IN (
|
210
|
+
expect(Article.see_sql_of.find(['Article One', 'Article Two'])).to eql("SELECT * FROM articles WHERE name IN ('Article One','Article Two')")
|
211
211
|
end
|
212
212
|
|
213
213
|
context 'one value' do
|
@@ -249,4 +249,13 @@ RSpec.describe StaticRecord::Relation, :type => :model do
|
|
249
249
|
expect(Article.where(author: 'Inexisting author').or.where(author: 'The author').to_sql).to eql("SELECT * FROM articles WHERE author = 'Inexisting author' OR author = 'The author'")
|
250
250
|
end
|
251
251
|
end
|
252
|
+
|
253
|
+
context '.joins' do
|
254
|
+
it 'allows to use the SQL JOIN clause' do
|
255
|
+
expect(Article.joins(:categories).where("categories.name = 'Category One'").first.class).to eql(ArticleOne)
|
256
|
+
expected_sql = "SELECT * FROM articles INNER JOIN categories ON articles.category = categories.klass"
|
257
|
+
expect(Article.joins(:categories).to_sql).to eql(expected_sql)
|
258
|
+
expect(Article.joins(Category).to_sql).to eql(expected_sql)
|
259
|
+
end
|
260
|
+
end
|
252
261
|
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'rails_helper'
|
3
|
+
|
4
|
+
RSpec.describe StaticRecord::Base, :type => :module do
|
5
|
+
it 'can query over a static record reference' do
|
6
|
+
category = Category.find('Category One')
|
7
|
+
article = Article.where(category: category)
|
8
|
+
expect(article.first.category.class).to eql(category.class)
|
9
|
+
end
|
10
|
+
end
|
@@ -11,6 +11,10 @@ class Article < StaticRecord::Base
|
|
11
11
|
3
|
12
12
|
end
|
13
13
|
|
14
|
+
def self.default_category
|
15
|
+
Category.find('Category Two')
|
16
|
+
end
|
17
|
+
|
14
18
|
def self.override_cover(cover)
|
15
19
|
unless cover.to_s.include?('/')
|
16
20
|
folder = Rails.root.join('public', 'articles', 'cover').itself
|
@@ -23,5 +27,6 @@ class Article < StaticRecord::Base
|
|
23
27
|
author: :string,
|
24
28
|
rank: :integer,
|
25
29
|
important: :boolean,
|
26
|
-
cover: :string
|
30
|
+
cover: :string,
|
31
|
+
category: :static_record
|
27
32
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: static-record
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Hugo Chevalier
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-01-
|
11
|
+
date: 2016-01-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -144,7 +144,9 @@ files:
|
|
144
144
|
- app/views/layouts/static_record/application.html.erb
|
145
145
|
- config/routes.rb
|
146
146
|
- lib/static_record.rb
|
147
|
+
- lib/static_record/concerns/getters_setters_concern.rb
|
147
148
|
- lib/static_record/concerns/query_building_concern.rb
|
149
|
+
- lib/static_record/concerns/request_execution_concern.rb
|
148
150
|
- lib/static_record/concerns/sqlite_storing_concern.rb
|
149
151
|
- lib/static_record/engine.rb
|
150
152
|
- lib/static_record/exceptions.rb
|
@@ -167,6 +169,7 @@ files:
|
|
167
169
|
- spec/models/default_value_spec.rb
|
168
170
|
- spec/models/querying_spec.rb
|
169
171
|
- spec/models/relation_spec.rb
|
172
|
+
- spec/models/static_record_reference_spec.rb
|
170
173
|
- spec/rails_helper.rb
|
171
174
|
- spec/spec_helper.rb
|
172
175
|
- spec/test_app/app/controllers/application_controller.rb
|
@@ -179,6 +182,9 @@ files:
|
|
179
182
|
- spec/test_app/app/models/articles/article_two.rb
|
180
183
|
- spec/test_app/app/models/badge.rb
|
181
184
|
- spec/test_app/app/models/badges/badge_one.rb
|
185
|
+
- spec/test_app/app/models/categories/category_one.rb
|
186
|
+
- spec/test_app/app/models/categories/category_two.rb
|
187
|
+
- spec/test_app/app/models/category.rb
|
182
188
|
- spec/test_app/app/models/role.rb
|
183
189
|
- spec/test_app/app/models/roles/role_one.rb
|
184
190
|
- spec/test_app/app/models/roles/role_two.rb
|
@@ -234,6 +240,7 @@ test_files:
|
|
234
240
|
- spec/models/default_value_spec.rb
|
235
241
|
- spec/models/querying_spec.rb
|
236
242
|
- spec/models/relation_spec.rb
|
243
|
+
- spec/models/static_record_reference_spec.rb
|
237
244
|
- spec/rails_helper.rb
|
238
245
|
- spec/spec_helper.rb
|
239
246
|
- spec/test_app/app/controllers/application_controller.rb
|
@@ -246,6 +253,9 @@ test_files:
|
|
246
253
|
- spec/test_app/app/models/articles/article_two.rb
|
247
254
|
- spec/test_app/app/models/badge.rb
|
248
255
|
- spec/test_app/app/models/badges/badge_one.rb
|
256
|
+
- spec/test_app/app/models/categories/category_one.rb
|
257
|
+
- spec/test_app/app/models/categories/category_two.rb
|
258
|
+
- spec/test_app/app/models/category.rb
|
249
259
|
- spec/test_app/app/models/role.rb
|
250
260
|
- spec/test_app/app/models/roles/role_one.rb
|
251
261
|
- spec/test_app/app/models/roles/role_two.rb
|