static-record 1.0.0.pre
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.rdoc +112 -0
- data/Rakefile +22 -0
- data/app/assets/javascripts/static_record/application.js +13 -0
- data/app/assets/stylesheets/static_record/application.css +15 -0
- data/app/controllers/static_record/application_controller.rb +4 -0
- data/app/helpers/static_record/application_helper.rb +4 -0
- data/app/models/concerns/query_building_concern.rb +103 -0
- data/app/models/concerns/sqlite_storing_concern.rb +61 -0
- data/app/models/static_record/base.rb +61 -0
- data/app/models/static_record/predicates.rb +100 -0
- data/app/models/static_record/querying.rb +25 -0
- data/app/models/static_record/relation.rb +110 -0
- data/app/views/layouts/static_record/application.html.erb +14 -0
- data/config/routes.rb +2 -0
- data/lib/static_record.rb +5 -0
- data/lib/static_record/engine.rb +5 -0
- data/lib/static_record/exceptions.rb +5 -0
- data/lib/static_record/version.rb +3 -0
- data/lib/tasks/static_record_tasks.rake +4 -0
- data/spec/models/static_record/base_spec.rb +10 -0
- data/spec/models/static_record/querying_spec.rb +8 -0
- data/spec/models/static_record/relation_spec.rb +242 -0
- data/spec/rails_helper.rb +15 -0
- data/spec/spec_helper.rb +18 -0
- data/spec/test_app/app/controllers/application_controller.rb +5 -0
- data/spec/test_app/app/helpers/application_helper.rb +2 -0
- data/spec/test_app/app/models/article.rb +6 -0
- data/spec/test_app/app/models/articles/article_four.rb +5 -0
- data/spec/test_app/app/models/articles/article_one.rb +5 -0
- data/spec/test_app/app/models/articles/article_three.rb +5 -0
- data/spec/test_app/app/models/articles/article_two.rb +5 -0
- data/spec/test_app/app/models/role.rb +5 -0
- data/spec/test_app/app/models/roles/role_one.rb +4 -0
- data/spec/test_app/config/application.rb +32 -0
- data/spec/test_app/config/boot.rb +5 -0
- data/spec/test_app/config/environment.rb +5 -0
- data/spec/test_app/config/environments/development.rb +41 -0
- data/spec/test_app/config/environments/production.rb +79 -0
- data/spec/test_app/config/environments/test.rb +42 -0
- data/spec/test_app/config/initializers/assets.rb +11 -0
- data/spec/test_app/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/test_app/config/initializers/cookies_serializer.rb +3 -0
- data/spec/test_app/config/initializers/filter_parameter_logging.rb +4 -0
- data/spec/test_app/config/initializers/inflections.rb +16 -0
- data/spec/test_app/config/initializers/mime_types.rb +4 -0
- data/spec/test_app/config/initializers/session_store.rb +3 -0
- data/spec/test_app/config/initializers/wrap_parameters.rb +14 -0
- data/spec/test_app/config/routes.rb +4 -0
- data/spec/test_app/db/schema.rb +16 -0
- metadata +199 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: a8eb89ed57ec1a0a6fbc6574e1c1614cda528447
|
4
|
+
data.tar.gz: ffffe730a97c3953064f7e2f517d840852ecf48e
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 0d93d7d7ebcc4b347ccad1863e4ce43d3a51688022b7e4b2292820292a3ff0eafa49f1713c1f12058da444517d6c1605ff7d0db57da3401bd630e1bab0efb731
|
7
|
+
data.tar.gz: 40483219ffbe67bd87118e3d9175b5be8c7b681a6f8909b9188f1efc887f5fedc05c6a7594bdde42714fb5140d67b6be56dc099d50cfdb8bd5116330f7901b53
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2016 Hugo Chevalier
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,112 @@
|
|
1
|
+
= StaticRecord
|
2
|
+
|
3
|
+
StaticRecord allows you to perform ActiveRecord-like queries over ruby files.
|
4
|
+
|
5
|
+
Those files act as immutable database records that only developers can alter.
|
6
|
+
|
7
|
+
You can use it when you need several files inheriting a base class.
|
8
|
+
|
9
|
+
== Installation
|
10
|
+
|
11
|
+
Add this to your Gemfile:
|
12
|
+
|
13
|
+
gem 'static-record'
|
14
|
+
|
15
|
+
and run the bundle install command.
|
16
|
+
|
17
|
+
== Getting Started
|
18
|
+
|
19
|
+
=== Base class
|
20
|
+
|
21
|
+
Create your base class inheriting from StaticRecord::Base.
|
22
|
+
|
23
|
+
class Article < StaticRecord::Base
|
24
|
+
# Declare in which SQLite3 file "articles" will be store (db/static_<table>.sqlite3)
|
25
|
+
table :articles
|
26
|
+
|
27
|
+
# Declare at which path "article" files can be found
|
28
|
+
path Rails.root.join('app', 'models', 'articles', '**', '*.rb')
|
29
|
+
|
30
|
+
# Optionnal, declare which column can be used as the primary key (must be unique)
|
31
|
+
# .find will only be availble if a primary key is defined
|
32
|
+
primary_key :name
|
33
|
+
|
34
|
+
# Specify which "article" attributes can be queried over
|
35
|
+
columns [:name, :author, :rank]
|
36
|
+
end
|
37
|
+
|
38
|
+
At each application startup, an SQLite3 database will be created to store this class' children.
|
39
|
+
|
40
|
+
=== Child class
|
41
|
+
|
42
|
+
Create has many child class as you want.
|
43
|
+
|
44
|
+
class ArticleOne < Article
|
45
|
+
# Define the attributes that will be available for your StaticRecord queries
|
46
|
+
attribute :name, 'Article One'
|
47
|
+
attribute :author, 'The author'
|
48
|
+
attribute :rank, '2'
|
49
|
+
|
50
|
+
# Your class can be used as any other Ruby class
|
51
|
+
def initialize
|
52
|
+
@an_instance_variable
|
53
|
+
super
|
54
|
+
end
|
55
|
+
|
56
|
+
def my_instance_method
|
57
|
+
end
|
58
|
+
|
59
|
+
def self.my_class_method
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
=== Queries
|
64
|
+
|
65
|
+
In your code, you can perform queries like this one:
|
66
|
+
|
67
|
+
Article.where(name: 'Article Two').or.where.not(author: ['Author 1', 'Author 2']).limit(2).offset(3)
|
68
|
+
|
69
|
+
I tried to implement as many SQL functions wrappers that ActiveRecord provides as I could.
|
70
|
+
|
71
|
+
There is still a lot of work before everything is available, but I chosed to release the 1.0.0.pre nevertheless.
|
72
|
+
|
73
|
+
Here is a full list:
|
74
|
+
* where
|
75
|
+
* supports Hash -> where(name: 'Name', author: 'Author')
|
76
|
+
* supports String -> where("name = 'Name'") or where("name = ?", 'Name') or where("name = :name", name: 'Name')
|
77
|
+
* find (requires a primary key has been set)
|
78
|
+
* find_by
|
79
|
+
* not
|
80
|
+
* or
|
81
|
+
* all
|
82
|
+
* take
|
83
|
+
* first
|
84
|
+
* last
|
85
|
+
* limit
|
86
|
+
* offset
|
87
|
+
* order
|
88
|
+
|
89
|
+
== IDs
|
90
|
+
|
91
|
+
Records are being assigned an ID in the SQLite3 database when inserted.
|
92
|
+
|
93
|
+
As the database is recreated at each application startup and IDs depend on the insertion order, I advise you to rely on another column if you want to hardcode a specific record somewhere in your app.
|
94
|
+
|
95
|
+
== Questions?
|
96
|
+
|
97
|
+
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.
|
98
|
+
|
99
|
+
== Bugs?
|
100
|
+
|
101
|
+
If you find a bug please add an issue on GitHub or fork the project and send a pull request.
|
102
|
+
|
103
|
+
== Development
|
104
|
+
|
105
|
+
As StaticRecord is in active development and a full list of feature is already scheduled, I won't be accepting any feature-oriented pull requests before the 1.0.0 release (hopefully before mi-January).
|
106
|
+
|
107
|
+
Here is what will be available soon:
|
108
|
+
- Better documentation
|
109
|
+
- Typed attributes
|
110
|
+
- Foreign keys
|
111
|
+
- Joins
|
112
|
+
- Generators
|
data/Rakefile
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
begin
|
2
|
+
require 'bundler/setup'
|
3
|
+
rescue LoadError
|
4
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
5
|
+
end
|
6
|
+
|
7
|
+
require 'rdoc/task'
|
8
|
+
|
9
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
10
|
+
rdoc.rdoc_dir = 'rdoc'
|
11
|
+
rdoc.title = 'StaticRecord'
|
12
|
+
rdoc.options << '--line-numbers'
|
13
|
+
rdoc.rdoc_files.include('README.rdoc')
|
14
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
15
|
+
end
|
16
|
+
|
17
|
+
APP_RAKEFILE = File.expand_path('../spec/test_app/Rakefile', __FILE__)
|
18
|
+
load 'rails/tasks/engine.rake'
|
19
|
+
|
20
|
+
load 'rails/tasks/statistics.rake'
|
21
|
+
|
22
|
+
Bundler::GemHelper.install_tasks
|
@@ -0,0 +1,13 @@
|
|
1
|
+
// This is a manifest file that'll be compiled into application.js, which will include all the files
|
2
|
+
// listed below.
|
3
|
+
//
|
4
|
+
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
|
5
|
+
// or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.
|
6
|
+
//
|
7
|
+
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
|
8
|
+
// compiled file.
|
9
|
+
//
|
10
|
+
// Read Sprockets README (https://github.com/sstephenson/sprockets#sprockets-directives) for details
|
11
|
+
// about supported directives.
|
12
|
+
//
|
13
|
+
//= require_tree .
|
@@ -0,0 +1,15 @@
|
|
1
|
+
/*
|
2
|
+
* This is a manifest file that'll be compiled into application.css, which will include all the files
|
3
|
+
* listed below.
|
4
|
+
*
|
5
|
+
* Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
|
6
|
+
* or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
|
7
|
+
*
|
8
|
+
* You're free to add application-wide styles to this file and they'll appear at the bottom of the
|
9
|
+
* compiled file so the styles you add here take precedence over styles defined in any styles
|
10
|
+
* defined in the other CSS/SCSS files in this directory. It is generally better to create a new
|
11
|
+
* file per style scope.
|
12
|
+
*
|
13
|
+
*= require_tree .
|
14
|
+
*= require_self
|
15
|
+
*/
|
@@ -0,0 +1,103 @@
|
|
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
|
@@ -0,0 +1,61 @@
|
|
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
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module StaticRecord
|
2
|
+
# Class that immutable model instances can inherit from
|
3
|
+
class Base
|
4
|
+
include StaticRecord::Querying
|
5
|
+
include StaticRecord::SqliteStoringConcern
|
6
|
+
|
7
|
+
RESERVED_ATTRIBUTES = [
|
8
|
+
:@@_columns,
|
9
|
+
:@@_primary_key,
|
10
|
+
:@@_path_pattern,
|
11
|
+
:@@_store
|
12
|
+
].freeze
|
13
|
+
|
14
|
+
def self.attribute(name, value)
|
15
|
+
err = RESERVED_ATTRIBUTES.include?("@@#{name}".to_sym)
|
16
|
+
raise StaticRecord::ReservedAttributeName, "#{name} is a reserved name" if err
|
17
|
+
class_variable_set("@@#{name}", value)
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.primary_key(name)
|
21
|
+
err = RESERVED_ATTRIBUTES.include?("@@#{name}".to_sym)
|
22
|
+
raise StaticRecord::ReservedAttributeName, "#{name} is a reserved name" if err
|
23
|
+
class_variable_set('@@_primary_key', name)
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.pkey
|
27
|
+
class_variable_defined?(:@@_primary_key) ? class_variable_get('@@_primary_key') : nil
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.table(store)
|
31
|
+
class_variable_set('@@_store', store.to_s)
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.store
|
35
|
+
class_variable_get('@@_store')
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.path(path)
|
39
|
+
class_variable_set('@@_path_pattern', path)
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.path_pattern
|
43
|
+
class_variable_get('@@_path_pattern')
|
44
|
+
end
|
45
|
+
|
46
|
+
def attributes
|
47
|
+
attrs = {}
|
48
|
+
klass = self.class
|
49
|
+
klass.class_variables.each do |var|
|
50
|
+
next if RESERVED_ATTRIBUTES.include?(var)
|
51
|
+
attrs[var.to_s.sub(/@@/, '').to_sym] = klass.class_variable_get(var)
|
52
|
+
end
|
53
|
+
attrs
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.columns(cols)
|
57
|
+
class_variable_set('@@_columns', cols)
|
58
|
+
create_store
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
module StaticRecord
|
2
|
+
# Contains ActiveRecord-like query predicates
|
3
|
+
module Predicates
|
4
|
+
private
|
5
|
+
|
6
|
+
def where(query = nil, *params)
|
7
|
+
params = [params] unless params.is_a?(Array) || params.is_a?(Hash)
|
8
|
+
params = params.first if params.size == 1 && params[0].is_a?(Hash)
|
9
|
+
add_subclause({ q: query }, params) if query
|
10
|
+
self
|
11
|
+
end
|
12
|
+
|
13
|
+
def not(query, *params)
|
14
|
+
params = [params] unless params.is_a?(Array) || params.is_a?(Hash)
|
15
|
+
params = params.first if params.size == 1 && params[0].is_a?(Hash)
|
16
|
+
add_subclause({ q: query, operator: :not_eq }, params)
|
17
|
+
self
|
18
|
+
end
|
19
|
+
|
20
|
+
def or
|
21
|
+
@chain = :or
|
22
|
+
self
|
23
|
+
end
|
24
|
+
|
25
|
+
def all
|
26
|
+
to_a
|
27
|
+
end
|
28
|
+
|
29
|
+
def find(value)
|
30
|
+
raise StaticRecord::NoPrimaryKey, 'No primary key have been set' if @primary_key.nil?
|
31
|
+
@result_type = :record unless value.is_a?(Array)
|
32
|
+
add_subclause(q: { :"#{@primary_key}" => value })
|
33
|
+
@sql_limit = 1 if @result_type == :record
|
34
|
+
|
35
|
+
res = to_a
|
36
|
+
return res if @only_sql
|
37
|
+
|
38
|
+
raise StaticRecord::RecordNotFound, "Couldn't find all #{@store.singularize.capitalize} with '#{@primary_key.to_s}' IN #{value}" if @result_type == :array && res.size != value.size
|
39
|
+
raise StaticRecord::RecordNotFound, "Couldn't find #{@store.singularize.capitalize} with '#{@primary_key.to_s}'=#{value}" if @result_type == :record && res.nil?
|
40
|
+
|
41
|
+
res
|
42
|
+
end
|
43
|
+
|
44
|
+
def find_by(query)
|
45
|
+
add_subclause(q: query)
|
46
|
+
take(1)
|
47
|
+
end
|
48
|
+
|
49
|
+
def take(amount = 1)
|
50
|
+
@sql_limit = amount
|
51
|
+
@result_type = :record if amount == 1
|
52
|
+
to_a
|
53
|
+
end
|
54
|
+
|
55
|
+
def first(amount = 1)
|
56
|
+
@order_by << { :"#{@primary_key}" => :asc } if @order_by.empty?
|
57
|
+
take(amount)
|
58
|
+
end
|
59
|
+
|
60
|
+
def last(amount = 1)
|
61
|
+
@order_by << { :"#{@primary_key}" => :desc } if @order_by.empty?
|
62
|
+
res = take(amount)
|
63
|
+
res.reverse! if res.is_a?(Array)
|
64
|
+
res
|
65
|
+
end
|
66
|
+
|
67
|
+
def limit(amount)
|
68
|
+
@sql_limit = amount
|
69
|
+
self
|
70
|
+
end
|
71
|
+
|
72
|
+
def offset(amount)
|
73
|
+
@sql_offset = amount
|
74
|
+
self
|
75
|
+
end
|
76
|
+
|
77
|
+
def order(ord)
|
78
|
+
@order_by << ord
|
79
|
+
self
|
80
|
+
end
|
81
|
+
|
82
|
+
def count
|
83
|
+
@columns = 'COUNT(*)'
|
84
|
+
exec_request(:integer)
|
85
|
+
end
|
86
|
+
|
87
|
+
def to_sql
|
88
|
+
build_query
|
89
|
+
end
|
90
|
+
|
91
|
+
def see_sql_of
|
92
|
+
@only_sql = true
|
93
|
+
self
|
94
|
+
end
|
95
|
+
|
96
|
+
def to_a
|
97
|
+
exec_request
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|