taupe 0.5.3
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.
- checksums.yaml +7 -0
- data/.rubocop.yml +10 -0
- data/Gemfile +6 -0
- data/LICENSE +21 -0
- data/README.md +176 -0
- data/Rakefile +28 -0
- data/lib/taupe/cache/memcached.rb +40 -0
- data/lib/taupe/cache/redis.rb +41 -0
- data/lib/taupe/cache.rb +95 -0
- data/lib/taupe/core.rb +74 -0
- data/lib/taupe/database/mysql.rb +70 -0
- data/lib/taupe/database/postgresql.rb +89 -0
- data/lib/taupe/database/sqlite.rb +73 -0
- data/lib/taupe/database.rb +138 -0
- data/lib/taupe/model/table.rb +192 -0
- data/lib/taupe/model/validate.rb +50 -0
- data/lib/taupe/model.rb +47 -0
- data/lib/taupe.rb +26 -0
- data/spec/database_spec.rb +59 -0
- data/spec/model_spec.rb +56 -0
- data/spec/taupe_spec.rb +14 -0
- data/spec/validator_spec.rb +43 -0
- data/taupe.gemspec +22 -0
- metadata +66 -0
@@ -0,0 +1,73 @@
|
|
1
|
+
# File: sqlite.rb
|
2
|
+
# Time-stamp: <2014-09-11 16:29:19 pierre>
|
3
|
+
# Copyright (C) 2014 Pierre Lecocq
|
4
|
+
# Description: Taupe library sqlite driver class
|
5
|
+
|
6
|
+
module Taupe
|
7
|
+
class Database
|
8
|
+
# Sqlite database driver
|
9
|
+
class SqliteDriver
|
10
|
+
# Accessors
|
11
|
+
attr_accessor :connection
|
12
|
+
|
13
|
+
# Constructor
|
14
|
+
# @param dsn [Hash] The data source name
|
15
|
+
def initialize(dsn)
|
16
|
+
db = File.expand_path(dsn)
|
17
|
+
fail "Database #{db} not found" unless File.exist? db
|
18
|
+
@connection = SQLite3::Database.new db
|
19
|
+
@connection.results_as_hash = true
|
20
|
+
end
|
21
|
+
|
22
|
+
# Execute a single query
|
23
|
+
# @param query [String] The query to execute
|
24
|
+
# @return [Object]
|
25
|
+
def exec(query)
|
26
|
+
@connection.execute query
|
27
|
+
end
|
28
|
+
|
29
|
+
# Fetch objects from database
|
30
|
+
# @param query [String] The query to fetch
|
31
|
+
# @return [Array, Object]
|
32
|
+
def fetch(query)
|
33
|
+
exec(query).map(&:symbolize_keys)
|
34
|
+
end
|
35
|
+
|
36
|
+
# Get last inserted id
|
37
|
+
# @return [Integer]
|
38
|
+
def last_id
|
39
|
+
@connection.last_insert_row_id.to_i
|
40
|
+
end
|
41
|
+
|
42
|
+
# Guess schema of a table
|
43
|
+
# @param table [String] The table name
|
44
|
+
# @return [Hash]
|
45
|
+
def guess_schema(table)
|
46
|
+
results = {}
|
47
|
+
|
48
|
+
query = format('pragma table_info(%s)', table)
|
49
|
+
|
50
|
+
fetch(query).each do |values|
|
51
|
+
type = Taupe::Validate.standardize_sql_type values[:type]
|
52
|
+
|
53
|
+
results[values[:name].to_sym] = {
|
54
|
+
type: type,
|
55
|
+
null: values[:notnull] == 0,
|
56
|
+
primary_key: values[:pk] == 1
|
57
|
+
}
|
58
|
+
end
|
59
|
+
|
60
|
+
results
|
61
|
+
end
|
62
|
+
|
63
|
+
# Escape a string
|
64
|
+
# @param str [String]
|
65
|
+
# @return [String]
|
66
|
+
def escape(str)
|
67
|
+
# Sqlite3 does not implement this kind of thing
|
68
|
+
# Use prepare statements instead
|
69
|
+
str
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,138 @@
|
|
1
|
+
# File: database.rb
|
2
|
+
# Time-stamp: <2014-09-11 14:52:41 pierre>
|
3
|
+
# Copyright (C) 2014 Pierre Lecocq
|
4
|
+
# Description: Taupe library database class
|
5
|
+
|
6
|
+
require 'taupe/database/postgresql'
|
7
|
+
require 'taupe/database/mysql'
|
8
|
+
require 'taupe/database/sqlite'
|
9
|
+
|
10
|
+
module Taupe
|
11
|
+
# Database class
|
12
|
+
# Manage database connection and serve as query proxy
|
13
|
+
class Database
|
14
|
+
# Includes
|
15
|
+
include Accessorized
|
16
|
+
|
17
|
+
# Custom accessors
|
18
|
+
# Accessible via _name and _name=
|
19
|
+
single_accessor :type, :host, :port, :username, :password, :database
|
20
|
+
|
21
|
+
# Accessors
|
22
|
+
attr_accessor :instance, :driver
|
23
|
+
|
24
|
+
# Constructor
|
25
|
+
# @param block [Proc] A given block
|
26
|
+
def initialize(&block)
|
27
|
+
instance_eval(&block)
|
28
|
+
end
|
29
|
+
|
30
|
+
# Setup the Database instance
|
31
|
+
# @param block [Proc] A given block
|
32
|
+
def self.setup(&block)
|
33
|
+
@instance = new(&block)
|
34
|
+
setup_defaults
|
35
|
+
driver_factory
|
36
|
+
end
|
37
|
+
|
38
|
+
# Get the database type
|
39
|
+
# @return [Symbol]
|
40
|
+
def self.type
|
41
|
+
@instance._type
|
42
|
+
end
|
43
|
+
|
44
|
+
# Get the database name
|
45
|
+
# @return [Symbol]
|
46
|
+
def self.database
|
47
|
+
@instance._database
|
48
|
+
end
|
49
|
+
|
50
|
+
# Setup default values
|
51
|
+
def self.setup_defaults
|
52
|
+
case @instance._type
|
53
|
+
when :pg, :pgsql, :postgres, :postgresql
|
54
|
+
Taupe.require_gem 'pg', 'PostgreSQL database engine'
|
55
|
+
@instance._type = :postgresql
|
56
|
+
@instance._host ||= :localhost
|
57
|
+
@instance._port ||= 5432
|
58
|
+
when :mysql, :mysql2
|
59
|
+
Taupe.require_gem 'mysql2', 'MySQL database engine'
|
60
|
+
@instance._type = :mysql
|
61
|
+
@instance._host ||= :localhost
|
62
|
+
@instance._port ||= 3306
|
63
|
+
when :sqlite, :sqlite3
|
64
|
+
Taupe.require_gem 'sqlite3', 'SQLite database engine'
|
65
|
+
@instance._type = :sqlite
|
66
|
+
@instance._database ||= File.expand_path('~/.taupe.db')
|
67
|
+
else
|
68
|
+
fail 'Unknown database type'
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# Get the data source name
|
73
|
+
# @return [Hash]
|
74
|
+
def self.dsn
|
75
|
+
case @instance._type
|
76
|
+
when :postgresql
|
77
|
+
{
|
78
|
+
host: @instance._host.to_s,
|
79
|
+
user: @instance._username.to_s,
|
80
|
+
password: @instance._password.to_s,
|
81
|
+
dbname: @instance._database.to_s
|
82
|
+
}
|
83
|
+
when :mysql
|
84
|
+
{
|
85
|
+
host: @instance._host.to_s,
|
86
|
+
username: @instance._username.to_s,
|
87
|
+
password: @instance._password.to_s,
|
88
|
+
database: @instance._database.to_s
|
89
|
+
}
|
90
|
+
when :sqlite
|
91
|
+
@instance._database.to_s
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
# Setup the connection driver
|
96
|
+
def self.driver_factory
|
97
|
+
cname = "Taupe::Database::#{@instance._type.capitalize}Driver"
|
98
|
+
klass = cname.split('::').reduce(Object) { |a, e| a.const_get e }
|
99
|
+
@instance.driver = klass.new dsn
|
100
|
+
end
|
101
|
+
|
102
|
+
# Guess schema of a table
|
103
|
+
# @param table [String] The table name
|
104
|
+
# @return [Hash]
|
105
|
+
def self.guess_schema(table)
|
106
|
+
@instance.driver.guess_schema table
|
107
|
+
end
|
108
|
+
|
109
|
+
# Execute a single query
|
110
|
+
# @param query [String] The query to execute
|
111
|
+
# @return [Object]
|
112
|
+
def self.exec(query)
|
113
|
+
@instance.driver.exec query
|
114
|
+
end
|
115
|
+
|
116
|
+
# Fetch objects from database
|
117
|
+
# @param query [String] The query to fetch
|
118
|
+
# @param single [Boolean] Must return one or more results?
|
119
|
+
# @return [Array, Object]
|
120
|
+
def self.fetch(query, single = false)
|
121
|
+
results = @instance.driver.fetch query
|
122
|
+
single ? results[0] : results
|
123
|
+
end
|
124
|
+
|
125
|
+
# Fetch last inserted id
|
126
|
+
# @return [Integer]
|
127
|
+
def self.last_id
|
128
|
+
@instance.driver.last_id
|
129
|
+
end
|
130
|
+
|
131
|
+
# Escape a string
|
132
|
+
# @param str [String]
|
133
|
+
# @return [String]
|
134
|
+
def self.escape(str)
|
135
|
+
@instance.driver.escape str
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
@@ -0,0 +1,192 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# File: table.rb
|
4
|
+
# Time-stamp: <2014-09-11 16:28:23 pierre>
|
5
|
+
# Copyright (C) 2014 Pierre Lecocq
|
6
|
+
# Description: Taupe library model table class
|
7
|
+
|
8
|
+
module Taupe
|
9
|
+
class Model
|
10
|
+
# Model table base class
|
11
|
+
class Table
|
12
|
+
# Set instance variables
|
13
|
+
class << self
|
14
|
+
attr_accessor :_cname, :_table, :_columns
|
15
|
+
attr_accessor :_pkey, :_values, :_pkey_id, :_cache_key
|
16
|
+
end
|
17
|
+
|
18
|
+
# Load a new object
|
19
|
+
# @param pkey_id [Numeric]
|
20
|
+
# @param cache_key [String]
|
21
|
+
def self.load(pkey_id = nil, cache_key = nil)
|
22
|
+
full_cname = "Taupe::Model::#{@_cname}"
|
23
|
+
klass = full_cname.split('::').reduce(Object) { |a, e| a.const_get e }
|
24
|
+
|
25
|
+
options = {
|
26
|
+
pkey_id: pkey_id,
|
27
|
+
cache_key: cache_key,
|
28
|
+
values: nil
|
29
|
+
}
|
30
|
+
|
31
|
+
klass.new @_table, @_columns, options
|
32
|
+
end
|
33
|
+
|
34
|
+
# Load a new object from existing values
|
35
|
+
# @param values [Hash]
|
36
|
+
# @param pkey_id [Numeric]
|
37
|
+
# @param cache_key [String]
|
38
|
+
def self.load_from_hash(values, pkey_id = nil, cache_key = nil)
|
39
|
+
full_cname = "Taupe::Model::#{@_cname}"
|
40
|
+
klass = full_cname.split('::').reduce(Object) { |a, e| a.const_get e }
|
41
|
+
|
42
|
+
options = {
|
43
|
+
pkey_id: pkey_id,
|
44
|
+
cache_key: cache_key,
|
45
|
+
values: values
|
46
|
+
}
|
47
|
+
|
48
|
+
klass.new @_table, @_columns, options
|
49
|
+
end
|
50
|
+
|
51
|
+
# Constructor
|
52
|
+
# @param table [String]
|
53
|
+
# @param columns [Hash]
|
54
|
+
# @param options [Hash]
|
55
|
+
def initialize(table, columns = nil, options = {})
|
56
|
+
columns = Taupe::Database.guess_schema(table) if columns.nil?
|
57
|
+
|
58
|
+
@_table = table
|
59
|
+
@_columns = columns
|
60
|
+
@_pkey = nil
|
61
|
+
@_pkey_id = options[:pkey_id] || nil
|
62
|
+
@_cache_key = options[:cache_key] || nil
|
63
|
+
@_values = options[:values] || {}
|
64
|
+
|
65
|
+
@_pkey = columns.select { |_k, v| v[:primary_key] == true }.first[0]
|
66
|
+
fail "Primary key undefined for model #{table}" if @_pkey.nil?
|
67
|
+
|
68
|
+
if @_values.empty?
|
69
|
+
return if @_pkey_id.nil?
|
70
|
+
retrieve_from_database unless retrieve_from_cache
|
71
|
+
else
|
72
|
+
@_pkey_id = @_values[@_pkey] if @_pkey_id.nil?
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# Retrieve data from cache
|
77
|
+
# @return [Boolean]
|
78
|
+
def retrieve_from_cache
|
79
|
+
retrieved = false
|
80
|
+
return retrieved if @_cache_key.nil?
|
81
|
+
|
82
|
+
data = Taupe::Cache.get(@_cache_key) || nil
|
83
|
+
unless data.nil? || data.empty?
|
84
|
+
@_values = data
|
85
|
+
retrieved = true
|
86
|
+
end
|
87
|
+
|
88
|
+
retrieved
|
89
|
+
end
|
90
|
+
|
91
|
+
# Retrieve data from database
|
92
|
+
def retrieve_from_database
|
93
|
+
query = "SELECT * FROM #{@_table} WHERE #{@_pkey} = #{@_pkey_id}"
|
94
|
+
result = Taupe::Database.fetch(query, true)
|
95
|
+
|
96
|
+
return nil if result.nil? || result.empty?
|
97
|
+
|
98
|
+
result.map do |k, v|
|
99
|
+
@_values[k.to_sym] = v unless k.is_a? Numeric
|
100
|
+
end
|
101
|
+
|
102
|
+
Taupe::Cache.set @_cache_key, @_values unless @_cache_key.nil?
|
103
|
+
end
|
104
|
+
|
105
|
+
# Save the model object
|
106
|
+
# @param with_validations [Boolean]
|
107
|
+
def save(with_validations = true)
|
108
|
+
Taupe::Validate.check(@_values, @_columns) if with_validations
|
109
|
+
|
110
|
+
if @_pkey_id.nil?
|
111
|
+
query = "
|
112
|
+
INSERT INTO #{@_table}
|
113
|
+
(#{@_values.keys.map(&:to_s).join(', ')})
|
114
|
+
VALUES
|
115
|
+
(#{@_values.values.map { |e| "'" + e.to_s + "'" }.join(', ')})
|
116
|
+
"
|
117
|
+
|
118
|
+
Taupe::Database.exec query
|
119
|
+
@_pkey_id = Taupe::Database.last_id
|
120
|
+
else
|
121
|
+
joined_values = @_values.map { |k, v| "#{k} = '#{v}'" }.join(', ')
|
122
|
+
query = "
|
123
|
+
UPDATE #{@_table} SET #{joined_values}
|
124
|
+
WHERE #{@_pkey} = #{@_pkey_id}
|
125
|
+
"
|
126
|
+
|
127
|
+
Taupe::Database.exec query
|
128
|
+
end
|
129
|
+
|
130
|
+
Taupe::Cache.delete @_cache_key unless @_cache_key.nil?
|
131
|
+
end
|
132
|
+
|
133
|
+
# Delete the model object
|
134
|
+
def delete
|
135
|
+
fail 'Can not delete an unsaved model object' if @_pkey_id.nil?
|
136
|
+
|
137
|
+
query = "DELETE FROM #{@_table} WHERE #{@_pkey} = #{@_pkey_id}"
|
138
|
+
|
139
|
+
Taupe::Database.exec query
|
140
|
+
|
141
|
+
Taupe::Cache.delete @_cache_key unless @_cache_key.nil?
|
142
|
+
end
|
143
|
+
|
144
|
+
# Is the object empty?
|
145
|
+
# @return [Boolean]
|
146
|
+
def empty?
|
147
|
+
@_values.empty?
|
148
|
+
end
|
149
|
+
|
150
|
+
# Method missing
|
151
|
+
def method_missing(m, *args, &block)
|
152
|
+
return @_pkey_id if m.to_sym == @_pkey.to_sym
|
153
|
+
return @_values[m] if @_values.key? m
|
154
|
+
|
155
|
+
ms = m.to_s
|
156
|
+
if ms.include? '='
|
157
|
+
ms = ms[0..-2]
|
158
|
+
if @_columns.include? ms.to_sym
|
159
|
+
@_values[ms.to_sym] = args[0]
|
160
|
+
return true
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
super
|
165
|
+
end
|
166
|
+
|
167
|
+
# Execute a single query
|
168
|
+
# @param query [String] The query to execute
|
169
|
+
# @return [Object]
|
170
|
+
def self.exec(query)
|
171
|
+
Taupe::Database.exec query
|
172
|
+
end
|
173
|
+
|
174
|
+
# Fetch objects from database
|
175
|
+
# @param query [String] The query to fetch
|
176
|
+
# @param single [Boolean] Must return one or more results?
|
177
|
+
# @return [Array, Object]
|
178
|
+
def self.fetch(query, single = false)
|
179
|
+
results = []
|
180
|
+
data = Taupe::Database.fetch(query)
|
181
|
+
|
182
|
+
if data
|
183
|
+
data.each do |h|
|
184
|
+
results << load_from_hash(h)
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
single ? results[0] : results
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# File: validate.rb
|
2
|
+
# Time-stamp: <2014-09-11 16:13:29 pierre>
|
3
|
+
# Copyright (C) 2014 Pierre Lecocq
|
4
|
+
# Description: Taupe library validate class
|
5
|
+
|
6
|
+
module Taupe
|
7
|
+
# Validator class
|
8
|
+
class Validate
|
9
|
+
# Check data integrity
|
10
|
+
# @param values [Hash]
|
11
|
+
# @param definitions [Hash]
|
12
|
+
def self.check(values, definitions)
|
13
|
+
errors = []
|
14
|
+
definitions.each do |name, props|
|
15
|
+
can_be_null = props[:null] || true
|
16
|
+
if values.include?(name)
|
17
|
+
value = values[name]
|
18
|
+
expected_type = props[:type] || String
|
19
|
+
unless value.is_a? expected_type
|
20
|
+
errors << "#{name} (#{value.class.name}) must be a #{expected_type}"
|
21
|
+
end
|
22
|
+
else
|
23
|
+
errors << "#{name} can not be null" unless can_be_null
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
fail errors.join(' - ') unless errors.empty?
|
28
|
+
|
29
|
+
values
|
30
|
+
end
|
31
|
+
|
32
|
+
# Transform a SQL type into a standard type
|
33
|
+
def self.standardize_sql_type(sql_type)
|
34
|
+
standard_type = nil
|
35
|
+
case sql_type.to_s.downcase
|
36
|
+
when 'integer', 'int', 'int(11)', 'bigint', 'smallint', 'tinyint'
|
37
|
+
standard_type = Integer
|
38
|
+
when 'float'
|
39
|
+
standard_type = Float
|
40
|
+
when 'date', 'time', 'datetime', 'timestamp',
|
41
|
+
'timestamp wit time zone', 'timestamp without time zone'
|
42
|
+
standard_type = Time
|
43
|
+
else
|
44
|
+
standard_type = String
|
45
|
+
end
|
46
|
+
|
47
|
+
standard_type
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
data/lib/taupe/model.rb
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
# File: model.rb
|
2
|
+
# Time-stamp: <2014-09-11 13:44:58 pierre>
|
3
|
+
# Copyright (C) 2014 Pierre Lecocq
|
4
|
+
# Description: Taupe library model class
|
5
|
+
|
6
|
+
require 'taupe/model/table'
|
7
|
+
require 'taupe/model/validate'
|
8
|
+
|
9
|
+
module Taupe
|
10
|
+
# Model class
|
11
|
+
class Model
|
12
|
+
# Includes
|
13
|
+
include Accessorized
|
14
|
+
|
15
|
+
# Custom accessors
|
16
|
+
# Accessible via _name and _name=
|
17
|
+
single_accessor :table
|
18
|
+
stacked_accessor :column
|
19
|
+
|
20
|
+
# Accessors
|
21
|
+
attr_accessor :instance
|
22
|
+
|
23
|
+
# Setup the Cache instance
|
24
|
+
# @param block [Proc] A given block
|
25
|
+
def self.setup(&block)
|
26
|
+
@instance = new(&block)
|
27
|
+
end
|
28
|
+
|
29
|
+
# Constructor
|
30
|
+
# @param block [Proc] A given block
|
31
|
+
def initialize(&block)
|
32
|
+
instance_eval(&block)
|
33
|
+
_write_class_code
|
34
|
+
end
|
35
|
+
|
36
|
+
# Build the table related class
|
37
|
+
def _write_class_code
|
38
|
+
cname = @table.to_s.split('_').map(&:capitalize).join
|
39
|
+
klass = Taupe::Model.const_set cname, Class.new(Taupe::Model::Table)
|
40
|
+
klass._cname = cname
|
41
|
+
klass._table = @table
|
42
|
+
klass._columns = @_column_stack
|
43
|
+
end
|
44
|
+
|
45
|
+
private :_write_class_code
|
46
|
+
end
|
47
|
+
end
|
data/lib/taupe.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# File: taupe.rb
|
2
|
+
# Time-stamp: <2014-09-11 16:27:47 pierre>
|
3
|
+
# Copyright (C) 2014 Pierre Lecocq
|
4
|
+
# Description: Taupe library main file
|
5
|
+
|
6
|
+
$LOAD_PATH.unshift File.join(File.dirname(__FILE__))
|
7
|
+
|
8
|
+
require 'taupe/core'
|
9
|
+
require 'taupe/database'
|
10
|
+
require 'taupe/cache'
|
11
|
+
require 'taupe/model'
|
12
|
+
|
13
|
+
# Main Taupe module
|
14
|
+
module Taupe
|
15
|
+
# Current version constant in the form major.minor.patch
|
16
|
+
VERSION = [0, 5, 3].join('.')
|
17
|
+
|
18
|
+
# Require a gem
|
19
|
+
# @param gem_name [String] the gem name
|
20
|
+
# @param description [String] a description of the gem
|
21
|
+
def self.require_gem(gem_name, description)
|
22
|
+
require gem_name
|
23
|
+
rescue LoadError
|
24
|
+
raise format('To use %s, install the "%s" gem', description, gem_name)
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# File: database_spec.rb
|
2
|
+
# Time-stamp: <2014-08-01 12:00:00 pierre>
|
3
|
+
# Copyright (C) 2014 Pierre Lecocq
|
4
|
+
# Description: Taupe library database tests file
|
5
|
+
|
6
|
+
require_relative '../lib/taupe'
|
7
|
+
|
8
|
+
describe Taupe::Database do
|
9
|
+
# Before hook
|
10
|
+
before :all do
|
11
|
+
require 'sqlite3'
|
12
|
+
path = File.expand_path('/tmp/taupe-test.db')
|
13
|
+
File.delete path if File.exist? path
|
14
|
+
File.new(path, 'w')
|
15
|
+
end
|
16
|
+
|
17
|
+
# Setup method
|
18
|
+
describe '#setup' do
|
19
|
+
it 'should setup the database driver' do
|
20
|
+
database = Taupe::Database.setup do
|
21
|
+
type :sqlite
|
22
|
+
database File.expand_path('/tmp/taupe-test.db')
|
23
|
+
end
|
24
|
+
|
25
|
+
expect(database).to be_a Taupe::Database::SqliteDriver
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# Query method
|
30
|
+
describe '#query' do
|
31
|
+
it 'should execute some direct queries and return true' do
|
32
|
+
queries = []
|
33
|
+
queries << %Q(CREATE TABLE article(
|
34
|
+
article_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
35
|
+
title text NOT NULL,
|
36
|
+
content text,
|
37
|
+
state INTEGER NOT NULL default 1,
|
38
|
+
creation DATETIME DEFAULT CURRENT_TIMESTAMP);)
|
39
|
+
queries << %Q(INSERT INTO article (title, content, state) VALUES (
|
40
|
+
'Article one', 'This is the first article', 1);)
|
41
|
+
queries << %Q(INSERT INTO article (title, content, state) VALUES (
|
42
|
+
'Article two', 'This is the second article', 0);)
|
43
|
+
queries.each do |q|
|
44
|
+
expect(Taupe::Database.exec(q)).to eql []
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# Fetch method
|
50
|
+
describe '#fetch' do
|
51
|
+
it 'should fetch two database entries' do
|
52
|
+
q = 'SELECT * FROM article'
|
53
|
+
results = Taupe::Database.fetch(q)
|
54
|
+
expect(results).to be_a Array
|
55
|
+
expect(results[0]).to be_a Hash
|
56
|
+
expect(results.length).to eql 2
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
data/spec/model_spec.rb
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
# File: model_spec.rb
|
2
|
+
# Time-stamp: <2014-09-11 16:03:43 pierre>
|
3
|
+
# Copyright (C) 2014 Pierre Lecocq
|
4
|
+
# Description: Taupe library model tests file
|
5
|
+
|
6
|
+
require_relative '../lib/taupe'
|
7
|
+
|
8
|
+
describe Taupe::Model do
|
9
|
+
|
10
|
+
# Setup method
|
11
|
+
describe '#setup' do
|
12
|
+
it 'should setup the Article model' do
|
13
|
+
model = Taupe::Model.setup do
|
14
|
+
table :article
|
15
|
+
column :article_id, { type: Integer, :primary_key => true }
|
16
|
+
column :title, { type: String, :null => false }
|
17
|
+
column :content, { type: String }
|
18
|
+
column :state, { type: Integer }
|
19
|
+
column :creation, { type: Date, :locked => true }
|
20
|
+
end
|
21
|
+
expect(model).to be_a Taupe::Model
|
22
|
+
|
23
|
+
model_object = Taupe::Model::Article.load
|
24
|
+
expect(model_object).to be_a Taupe::Model::Article
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# Fetch method
|
29
|
+
describe '#fetch' do
|
30
|
+
it 'should fetch data from Article' do
|
31
|
+
query = "SELECT * FROM article"
|
32
|
+
results = Taupe::Model::Article.fetch(query)
|
33
|
+
|
34
|
+
expect(results).to be_a Array
|
35
|
+
expect(results.length).to eql 2
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# Query method
|
40
|
+
describe '#query and #save' do
|
41
|
+
it 'should execute an insert into Article' do
|
42
|
+
model_object = Taupe::Model::Article.load
|
43
|
+
model_object.title = 'This is a new article'
|
44
|
+
model_object.content = 'This is a new article content'
|
45
|
+
model_object.state = 1
|
46
|
+
model_object.save
|
47
|
+
|
48
|
+
query = "SELECT * FROM article"
|
49
|
+
results = Taupe::Model::Article.fetch(query)
|
50
|
+
|
51
|
+
expect(results).to be_a Array
|
52
|
+
expect(results.length).to eql 3
|
53
|
+
expect(results.last.title).to eql 'This is a new article'
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
data/spec/taupe_spec.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
# File: taupe_spec.rb
|
2
|
+
# Time-stamp: <2014-08-01 12:00:00 pierre>
|
3
|
+
# Copyright (C) 2014 Pierre Lecocq
|
4
|
+
# Description: Taupe library main tests file
|
5
|
+
|
6
|
+
require_relative '../lib/taupe'
|
7
|
+
|
8
|
+
describe Taupe do
|
9
|
+
describe 'VERSION' do
|
10
|
+
it 'should return 0.5.3' do
|
11
|
+
expect(Taupe::VERSION).to eql '0.5.3'
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# File: validator_spec.rb
|
2
|
+
# Time-stamp: <2014-09-11 15:56:04 pierre>
|
3
|
+
# Copyright (C) 2014 Pierre Lecocq
|
4
|
+
# Description: Taupe library validator tests file
|
5
|
+
|
6
|
+
describe Taupe::Validate do
|
7
|
+
describe '#check' do
|
8
|
+
it 'should succeed' do
|
9
|
+
values = {
|
10
|
+
:article_id => 1,
|
11
|
+
:title => 'This is an article',
|
12
|
+
:amount => 3.0
|
13
|
+
}
|
14
|
+
|
15
|
+
definitions = {
|
16
|
+
:article_id => { :type => Integer, :primary_key => true },
|
17
|
+
:amount => { :type => Float },
|
18
|
+
:title => { :type => String, :null => false },
|
19
|
+
:content => { :type => String }
|
20
|
+
}
|
21
|
+
|
22
|
+
expect(Taupe::Validate.check(values, definitions)).to eql values
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'should send a failure' do
|
26
|
+
values = {
|
27
|
+
:article_id => '1',
|
28
|
+
:title => 'This is an article',
|
29
|
+
:content => 'and the content',
|
30
|
+
:amount => 3.0
|
31
|
+
}
|
32
|
+
|
33
|
+
definitions = {
|
34
|
+
:article_id => { :type => Integer, :primary_key => true },
|
35
|
+
:amount => { :type => Float },
|
36
|
+
:title => { :type => String, :null => false },
|
37
|
+
:content => { :type => String }
|
38
|
+
}
|
39
|
+
|
40
|
+
expect{ Taupe::Validate.check(values, definitions) }.to raise_error
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|