kabuki-heresy 0.1.0
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/LICENSE +20 -0
- data/README.rdoc +50 -0
- data/VERSION.yml +4 -0
- data/lib/heresy-with-gems.rb +5 -0
- data/lib/heresy.rb +18 -0
- data/lib/heresy/formatting/json.rb +28 -0
- data/lib/heresy/index.rb +135 -0
- data/lib/heresy/model.rb +188 -0
- data/lib/heresy/schema.rb +48 -0
- data/lib/heresy/schema/column.rb +34 -0
- data/lib/heresy/schema/fields.rb +66 -0
- data/test/fields_test.rb +88 -0
- data/test/formatting/json_test.rb +15 -0
- data/test/index_test.rb +74 -0
- data/test/model_test.rb +78 -0
- data/test/schema_test.rb +43 -0
- data/test/test_helper.rb +51 -0
- metadata +114 -0
data/LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
Copyright (c) 2009 kabuki
|
|
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,50 @@
|
|
|
1
|
+
= heresy
|
|
2
|
+
|
|
3
|
+
Heresy is a schema free wrapper around your database, heavily inspired by both CouchDB and FriendFeed[1]. You create Heresy models that work with generic schema-less tables.
|
|
4
|
+
|
|
5
|
+
class Entry < Heresy::Model
|
|
6
|
+
fields do |f|
|
|
7
|
+
f.string :title
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
Entry.schema.create # create the table in the database
|
|
12
|
+
|
|
13
|
+
This class will use a table that looks like this:
|
|
14
|
+
|
|
15
|
+
create_table :entries do |t|
|
|
16
|
+
t.primary_key :id, :int
|
|
17
|
+
t.column :uuid, :binary
|
|
18
|
+
t.column :updated_at, :timestamp
|
|
19
|
+
t.column :body, :blob
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
Now you can create and retrieve models:
|
|
23
|
+
|
|
24
|
+
@entry = Entry.new :title => 'testing'
|
|
25
|
+
@entry.save
|
|
26
|
+
|
|
27
|
+
@entry = Entry.find('some_entry_uuid')
|
|
28
|
+
@entry.body = "updated"
|
|
29
|
+
@entry.save
|
|
30
|
+
|
|
31
|
+
UUIDs are automatically generated and used as the main ID for each record.
|
|
32
|
+
|
|
33
|
+
1: http://bret.appspot.com/entry/how-friendfeed-uses-mysql
|
|
34
|
+
|
|
35
|
+
== TODO
|
|
36
|
+
|
|
37
|
+
- Indexes
|
|
38
|
+
- Associations
|
|
39
|
+
- Timezone conversions
|
|
40
|
+
|
|
41
|
+
== NOT TODO
|
|
42
|
+
|
|
43
|
+
- Validations
|
|
44
|
+
- Callbacks
|
|
45
|
+
|
|
46
|
+
Use Validateable, ActiveSupport::Callbacks, ActiveModel, etc
|
|
47
|
+
|
|
48
|
+
== Copyright
|
|
49
|
+
|
|
50
|
+
Copyright (c) 2009 kabuki. See LICENSE for details.
|
data/VERSION.yml
ADDED
data/lib/heresy.rb
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
$LOAD_PATH << File.join(File.dirname(__FILE__))
|
|
2
|
+
|
|
3
|
+
%w(array/extract_options).each { |lib| require "active_support/core_ext/#{lib}" }
|
|
4
|
+
class Array #:nodoc:
|
|
5
|
+
include ActiveSupport::CoreExtensions::Array::ExtractOptions
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
%w(basic_object time_with_zone values/time_zone inflector core_ext/object core_ext/duplicable core_ext/blank core_ext/class core_ext/module core_ext/date core_ext/numeric core_ext/time duration).each { |lib| require "active_support/#{lib}" }
|
|
9
|
+
%w(uuid sequel_core tzinfo heresy/schema heresy/schema/column heresy/schema/fields heresy/index heresy/model).each { |lib| require lib }
|
|
10
|
+
|
|
11
|
+
module Heresy
|
|
12
|
+
class << self
|
|
13
|
+
attr_accessor :time_zone
|
|
14
|
+
attr_accessor :db
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
Time.zone = self.time_zone = "UTC"
|
|
18
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
require 'json'
|
|
2
|
+
require 'zlib'
|
|
3
|
+
require 'stringio'
|
|
4
|
+
|
|
5
|
+
module Heresy
|
|
6
|
+
module Formatting
|
|
7
|
+
module Json
|
|
8
|
+
DATE_REGEX = /\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z/
|
|
9
|
+
|
|
10
|
+
extend self
|
|
11
|
+
def encode(body)
|
|
12
|
+
s = StringIO.new
|
|
13
|
+
z = Zlib::GzipWriter.new(s)
|
|
14
|
+
z.write body.to_json
|
|
15
|
+
z.close
|
|
16
|
+
s.string
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def decode(body)
|
|
20
|
+
s = StringIO.new(body)
|
|
21
|
+
z = Zlib::GzipReader.new(s)
|
|
22
|
+
hash = JSON.parse(z.read)
|
|
23
|
+
z.close
|
|
24
|
+
hash
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
data/lib/heresy/index.rb
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
module Heresy
|
|
2
|
+
# This class is responsible for defining the index tables that models use.
|
|
3
|
+
# Since model data is stored as an encoded string, it is impossible to perform
|
|
4
|
+
# any searches or queries on it. Therefore, separate tables with these data
|
|
5
|
+
# relationships must be created.
|
|
6
|
+
#
|
|
7
|
+
# @index = Index.new(Heresy::Model.db, :entry_titles, :entry_id)
|
|
8
|
+
# @index.field :title, :varchar, :size => 255
|
|
9
|
+
# @index.create
|
|
10
|
+
#
|
|
11
|
+
# This will create a simple table that looks something like this:
|
|
12
|
+
#
|
|
13
|
+
# create_table :entry_titles do |t|
|
|
14
|
+
# t.column :entry_id, :binary
|
|
15
|
+
# t.column :title, :varchar
|
|
16
|
+
# t.primary_key [:title, :entry_id]
|
|
17
|
+
# end
|
|
18
|
+
#
|
|
19
|
+
# You can then fill the index as models are saved:
|
|
20
|
+
#
|
|
21
|
+
# @entry.save
|
|
22
|
+
# @index << {:entry_id => @entry.uuid, :title => @entry.title}
|
|
23
|
+
#
|
|
24
|
+
# Once the index is filled, you can perform searches:
|
|
25
|
+
#
|
|
26
|
+
# uuids = @index.find(:title => "Test")
|
|
27
|
+
# entries = Entry.find(uuids)
|
|
28
|
+
#
|
|
29
|
+
class Index
|
|
30
|
+
attr_accessor :name, :key
|
|
31
|
+
attr_reader :fields, :field_names, :uuid_fields
|
|
32
|
+
|
|
33
|
+
def initialize(db, name, key)
|
|
34
|
+
@db = db
|
|
35
|
+
@name = name
|
|
36
|
+
@key = Heresy::Schema::Column.new(key, :uuid, :unique => true)
|
|
37
|
+
@fields = []
|
|
38
|
+
@field_names = {}
|
|
39
|
+
@uuid_fields = [@key.name]
|
|
40
|
+
yield self if block_given?
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Inserts the given data into the index. The columns should probably
|
|
44
|
+
# match the Index's schema, or you'll probably see an error.
|
|
45
|
+
#
|
|
46
|
+
# @index << {:entry_id => @entry.uuid, :title => @entry.title}
|
|
47
|
+
#
|
|
48
|
+
def insert(data)
|
|
49
|
+
@uuid_fields.each do |name|
|
|
50
|
+
if data.key?(name)
|
|
51
|
+
data[name] = data[name].to_a.pack("H*")
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
dataset << data
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
alias << insert
|
|
58
|
+
|
|
59
|
+
# Clears all data from the index.
|
|
60
|
+
def clear
|
|
61
|
+
dataset.delete
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Counts the number of indexed entries.
|
|
65
|
+
def count
|
|
66
|
+
dataset.count
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Returns all the UUIDs from a given query. The query is first passed to #filter.
|
|
70
|
+
def find(params)
|
|
71
|
+
filter(params).to_a.map! { |row| row[@key.name].unpack("H*").first }
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Filters the Sequel dataset object with the given query. The resulting dataset
|
|
75
|
+
# can be modified further to introduce ordering or more advanced filtering if desired.
|
|
76
|
+
def filter(options)
|
|
77
|
+
dataset.filter(options).select(@key.name)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Index schema methods
|
|
81
|
+
|
|
82
|
+
# Adds a field to the index. By default, a :uuid type is used. All
|
|
83
|
+
# model-to-model associations are done via UUID.
|
|
84
|
+
def field(name, type = :uuid, options = {})
|
|
85
|
+
col = Heresy::Schema::Column.new(name, type, options)
|
|
86
|
+
@fields << col
|
|
87
|
+
@uuid_fields << col.name if col.type == :uuid
|
|
88
|
+
@field_names[name] = col
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# Index database creation/deletion
|
|
92
|
+
|
|
93
|
+
# Checks if the Index table exists in the database.
|
|
94
|
+
def exists?
|
|
95
|
+
@db.table_exists?(@name)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# Creates the Index table
|
|
99
|
+
def create
|
|
100
|
+
to_sql.each { |sql| @db.execute_ddl(sql) }
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# Drops the index table
|
|
104
|
+
def drop
|
|
105
|
+
@db.drop_table @name
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# Returns the SQL that will be used to create the Index table.
|
|
109
|
+
def to_sql
|
|
110
|
+
@db.create_table_sql_list @name, *generate.create_info
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def inspect
|
|
114
|
+
"<Heresy::Index:#{@name} #{@fields.map { |f| f.name } * ", "}>"
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
protected
|
|
118
|
+
def dataset
|
|
119
|
+
@dataset ||= @db[@name].select(@key.name)
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def generate
|
|
123
|
+
gen = Sequel::Schema::Generator.new(@db)
|
|
124
|
+
@key.create(gen)
|
|
125
|
+
keys = []
|
|
126
|
+
@fields.each do |field|
|
|
127
|
+
keys << field.name
|
|
128
|
+
field.create(gen)
|
|
129
|
+
end
|
|
130
|
+
keys << @key.name
|
|
131
|
+
gen.primary_key keys
|
|
132
|
+
gen
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
end
|
data/lib/heresy/model.rb
ADDED
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
module Heresy
|
|
2
|
+
# This class is responsible for storing and retrieving the model data in a schema-free manner.
|
|
3
|
+
class Model
|
|
4
|
+
class << self
|
|
5
|
+
attr_writer :db, :schema, :formatter
|
|
6
|
+
|
|
7
|
+
# Accesses the database schema for this model. The basic schema will look something like this:
|
|
8
|
+
#
|
|
9
|
+
# create_table :entries do |t|
|
|
10
|
+
# t.primary_key :id, :int
|
|
11
|
+
# t.column :uuid, :binary
|
|
12
|
+
# t.column :updated_at, :timestamp
|
|
13
|
+
# t.column :body, :blob
|
|
14
|
+
# end
|
|
15
|
+
#
|
|
16
|
+
# You can modify any of those from the schema directly.
|
|
17
|
+
#
|
|
18
|
+
# class Entry < Heresy::Model
|
|
19
|
+
# schema :custom_table_name do |s|
|
|
20
|
+
# s.id.type = :medium_int
|
|
21
|
+
# s.id.options[:size] = 11
|
|
22
|
+
# s.body.type = :medium_blob
|
|
23
|
+
# end
|
|
24
|
+
# end
|
|
25
|
+
#
|
|
26
|
+
# The types are any valid type from the Sequel ruby library. There are a few convenience types, however.
|
|
27
|
+
# (See Heresy::Schema::Column)
|
|
28
|
+
#
|
|
29
|
+
# All models have the same 4 database fields:
|
|
30
|
+
# - an auto-incremented ID and updated_at timestamp
|
|
31
|
+
# - a 32 character hex UUID that gets stored as a 16 character binary string. This is how items are retrieved.
|
|
32
|
+
# - a BLOB body character for storing the model's data.
|
|
33
|
+
#
|
|
34
|
+
def schema(table_name = name.demodulize.underscore.pluralize)
|
|
35
|
+
@schema ||= Heresy::Schema.new(db, table_name.to_sym)
|
|
36
|
+
yield @schema if block_given?
|
|
37
|
+
@schema
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Yields a Schema::Fields object for specifying fields for this model. These fields get assembled into
|
|
41
|
+
# a hash and stored in the BODY attribute of the schema.
|
|
42
|
+
#
|
|
43
|
+
# By default, fields are all strings. Other types such as integers and dates can be specified for automatic
|
|
44
|
+
# conversions.
|
|
45
|
+
#
|
|
46
|
+
# class Entry < Heresy::Model
|
|
47
|
+
# fields do |f|
|
|
48
|
+
# f.string :title
|
|
49
|
+
# f.integer :comments_count
|
|
50
|
+
# f.datetime :published_at
|
|
51
|
+
# end
|
|
52
|
+
# end
|
|
53
|
+
#
|
|
54
|
+
def fields
|
|
55
|
+
yield Schema::Fields.new(self)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# This is a hash of all specified fields and their type converters. If a type converter is set (see #fields),
|
|
59
|
+
# values are parsed in the field attribute writers, and encoded when saving to the database.
|
|
60
|
+
def body_fields
|
|
61
|
+
@body_fields ||= {}
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Finds a single or an array of entries by UUID.
|
|
65
|
+
def find(uuid)
|
|
66
|
+
if uuid.is_a?(Array)
|
|
67
|
+
rows = dataset.filter(:uuid => uuid.map { |u| u.to_a.pack("H*") }).to_a
|
|
68
|
+
rows.map! { |row| retrieve(row) }
|
|
69
|
+
else
|
|
70
|
+
new(:uuid => uuid).reload
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Reference to the Sequel DB object
|
|
75
|
+
def db
|
|
76
|
+
@db = Heresy.db
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Reference to the Sequel dataset for this model's table.
|
|
80
|
+
def dataset
|
|
81
|
+
@dataset ||= db[schema.name]
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# The formatter is what converts the assembled model hash data into the string
|
|
85
|
+
# that gets saved in the database. The default formatter stores zlib compressed
|
|
86
|
+
# JSON hashes.
|
|
87
|
+
def formatter
|
|
88
|
+
@formatter ||= \
|
|
89
|
+
if superclass.respond_to?(:formatter)
|
|
90
|
+
superclass.formatter
|
|
91
|
+
else
|
|
92
|
+
:json
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
if @formatter.is_a?(Symbol)
|
|
96
|
+
require "heresy/formatting/#{@formatter}"
|
|
97
|
+
@formatter = Heresy::Formatting.const_get(@formatter.to_s.capitalize)
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
@formatter
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
protected
|
|
104
|
+
# Used by #find to fill up an empty model record with data using a row from the db.
|
|
105
|
+
def retrieve(row)
|
|
106
|
+
record = new(:uuid => row[:uuid].unpack("H*").first)
|
|
107
|
+
record.retrieve(row)
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
attr_reader :id
|
|
112
|
+
attr_reader :updated_at
|
|
113
|
+
|
|
114
|
+
def initialize(attributes = {})
|
|
115
|
+
set_attributes attributes
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def uuid
|
|
119
|
+
@uuid ||= UUID.generate(:compact)
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# Saves the record in the database.
|
|
123
|
+
def save
|
|
124
|
+
if new?
|
|
125
|
+
self.class.dataset << {:uuid => uuid.to_a.pack("H*"), :body => self.class.formatter.encode(assemble_body)}
|
|
126
|
+
reload
|
|
127
|
+
else
|
|
128
|
+
rowset.update({:updated_at => nil})
|
|
129
|
+
reload
|
|
130
|
+
end
|
|
131
|
+
true
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def new?
|
|
135
|
+
@id.nil?
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
# Reloads a model's data from the database using the existing UUID.
|
|
139
|
+
def reload
|
|
140
|
+
retrieve(rowset.select(:id, :body, :updated_at).first)
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def ==(other)
|
|
144
|
+
other.class == self.class && other.uuid == uuid
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def inspect
|
|
148
|
+
"<#{self.class.name} (#{uuid}) #{self.class.body_fields.keys.map { |k| "@#{k}=#{send(k).inspect}" } * ', '}>"
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def retrieve(row)
|
|
152
|
+
@id = row[:id]
|
|
153
|
+
@updated_at = row[:updated_at]
|
|
154
|
+
set_attributes self.class.formatter.decode(row[:body])
|
|
155
|
+
self
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
protected
|
|
159
|
+
# Assembles the model data into an encoded hash, ready to be formatted and stored.
|
|
160
|
+
def assemble_body
|
|
161
|
+
hash = {}
|
|
162
|
+
self.class.body_fields.each do |key, type|
|
|
163
|
+
if value = instance_variable_get("@#{key}")
|
|
164
|
+
hash[key] = type ? type.encode(value) : value
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
hash
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
# Performs a mass assignment of the model's data.
|
|
171
|
+
def set_attributes(attributes)
|
|
172
|
+
attributes.each do |key, value|
|
|
173
|
+
next if key.blank?
|
|
174
|
+
key = key.to_sym
|
|
175
|
+
if key == :uuid
|
|
176
|
+
@uuid = value
|
|
177
|
+
elsif self.class.body_fields.key?(key)
|
|
178
|
+
send "#{key}=", value
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
# A Sequel filter for the current row in the database.
|
|
184
|
+
def rowset
|
|
185
|
+
@rowset ||= self.class.dataset.filter(:uuid => uuid.to_a.pack("H*"))
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
module Heresy
|
|
2
|
+
# This class is responsible for defining and creating the table used to store model data.
|
|
3
|
+
class Schema
|
|
4
|
+
attr_accessor :name
|
|
5
|
+
attr_reader :id, :uuid, :updated_at, :body
|
|
6
|
+
|
|
7
|
+
def initialize(db, name)
|
|
8
|
+
@db = db
|
|
9
|
+
@name = name
|
|
10
|
+
@id = Column.new(:id, :int, :size => 11, :unsigned => true)
|
|
11
|
+
@uuid = Column.new(:uuid, :uuid, :unique => true)
|
|
12
|
+
@updated_at = Column.new(:updated_at, :timestamp, :default => 'CURRENT_TIMESTAMP'.lit, :index => true)
|
|
13
|
+
@body = Column.new(:body, :blob)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Checks whether the model table exists or not.
|
|
17
|
+
def exists?
|
|
18
|
+
@db.table_exists?(@name)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Creates the model table.
|
|
22
|
+
def create
|
|
23
|
+
to_sql.each { |sql| @db.execute_ddl(sql) }
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Drops the model table.
|
|
27
|
+
def drop
|
|
28
|
+
@db.drop_table @name
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Returns an array of the SQL statements used to create the table and any indexes.
|
|
32
|
+
def to_sql
|
|
33
|
+
@db.create_table_sql_list @name, *generate.create_info
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def inspect
|
|
37
|
+
"<Heresy::Schema:#{@name}>"
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
protected
|
|
41
|
+
def generate
|
|
42
|
+
gen = Sequel::Schema::Generator.new(@db)
|
|
43
|
+
@id.create(gen, :primary_key)
|
|
44
|
+
[@uuid, @updated_at, @body].each { |c| c.create(gen) }
|
|
45
|
+
gen
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
module Heresy
|
|
2
|
+
class Schema
|
|
3
|
+
# This class defines a single column in the database, using the same options
|
|
4
|
+
# as the Sequel library. There are a few convenience types though:
|
|
5
|
+
#
|
|
6
|
+
# - :uuid: Creates a binary(16) database field
|
|
7
|
+
#
|
|
8
|
+
class Column
|
|
9
|
+
attr_accessor :name, :type, :options
|
|
10
|
+
|
|
11
|
+
# The actual database type. This may be different if the given type is a
|
|
12
|
+
# Heresy convenience type, or if the database supports the type under a different name.
|
|
13
|
+
attr_reader :db_type
|
|
14
|
+
|
|
15
|
+
def initialize(name, type, options = {})
|
|
16
|
+
if type == :uuid
|
|
17
|
+
@db_type = :binary
|
|
18
|
+
options.update(:size => 16, :null => false)
|
|
19
|
+
else
|
|
20
|
+
@db_type = type
|
|
21
|
+
end
|
|
22
|
+
@name = name
|
|
23
|
+
@type = type
|
|
24
|
+
@options = options
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Creates the column using the Sequel schema generator. By default, the #column
|
|
28
|
+
# method is used. An alternative method like :primary_key can be specified.
|
|
29
|
+
def create(gen, method = :column)
|
|
30
|
+
gen.send(method, @name, @db_type, @options)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
module Heresy
|
|
2
|
+
class Schema
|
|
3
|
+
# This class is a helper for setting up fields in the model. This is likely called
|
|
4
|
+
# from the model class and never used directly:
|
|
5
|
+
#
|
|
6
|
+
# class Entry < Heresy::Model
|
|
7
|
+
# fields do |f|
|
|
8
|
+
# f.field :title, :string
|
|
9
|
+
# f.string :body # Uses #string convenience method
|
|
10
|
+
# end
|
|
11
|
+
# end
|
|
12
|
+
#
|
|
13
|
+
class Fields
|
|
14
|
+
module Integer
|
|
15
|
+
extend self
|
|
16
|
+
def parse(input) input.to_i end
|
|
17
|
+
def encode(input) input end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
module Float
|
|
21
|
+
extend self
|
|
22
|
+
def parse(input) input.to_f end
|
|
23
|
+
def encode(input) input end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
module DateTime
|
|
27
|
+
extend self
|
|
28
|
+
def parse(input)
|
|
29
|
+
case input
|
|
30
|
+
when Time then input
|
|
31
|
+
when String then Time.parse(input)
|
|
32
|
+
else input.to_time
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
def encode(input) input.xmlschema end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
@@types = {}
|
|
39
|
+
def self.types() @@types end
|
|
40
|
+
def self.type(key, type = nil)
|
|
41
|
+
class_eval "def #{key}(name) field(name, :#{key}) end"
|
|
42
|
+
@@types[key] = type
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
type :string
|
|
46
|
+
type :integer, Integer
|
|
47
|
+
type :float, Float
|
|
48
|
+
type :datetime, DateTime
|
|
49
|
+
|
|
50
|
+
def initialize(model)
|
|
51
|
+
@model = model
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Adds a field of the given type to the model by creating an attribute reader and writer.
|
|
55
|
+
def field(name, type_name)
|
|
56
|
+
if type = self.class.types[type_name]
|
|
57
|
+
@model.send(:attr_reader, name)
|
|
58
|
+
@model.class_eval "def #{name}=(v) @#{name} = self.class.body_fields[:#{name}].parse(v) end"
|
|
59
|
+
else
|
|
60
|
+
@model.send(:attr_accessor, name)
|
|
61
|
+
end
|
|
62
|
+
@model.body_fields[name] = type
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
data/test/fields_test.rb
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'test_helper'))
|
|
2
|
+
|
|
3
|
+
module HeresyTest
|
|
4
|
+
class FieldsTest < Heresy::TestCase
|
|
5
|
+
describe "field types from mass assignment" do
|
|
6
|
+
before :all do
|
|
7
|
+
@entry = Entry.new :title => 'whoa', :a => 'ignored', :comments_count => "3.0", :rating => '5', :published_at => Time.utc(2008, 1, 1)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
it "sets a string" do
|
|
11
|
+
@entry.title.should == 'whoa'
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
it "sets an integer" do
|
|
15
|
+
@entry.comments_count.should == 3
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
it "sets a float" do
|
|
19
|
+
@entry.rating.should == 5.0
|
|
20
|
+
@entry.rating.to_s.should == "5.0"
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
it "sets a time" do
|
|
24
|
+
@entry.published_at.should == Time.utc(2008, 1, 1)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
describe "field types from individual set" do
|
|
29
|
+
before :all do
|
|
30
|
+
@entry = Entry.new
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
it "sets a string" do
|
|
34
|
+
@entry.title = 'whoa'
|
|
35
|
+
@entry.title.should == 'whoa'
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
it "sets an integer" do
|
|
39
|
+
@entry.comments_count = '3.0'
|
|
40
|
+
@entry.comments_count.should == 3
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
it "sets a float" do
|
|
44
|
+
@entry.rating = 5
|
|
45
|
+
@entry.rating.should == 5.0
|
|
46
|
+
@entry.rating.to_s.should == "5.0"
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
it "sets a time from an xml schema" do
|
|
50
|
+
@entry.published_at = Time.utc(2008, 1, 1).xmlschema
|
|
51
|
+
@entry.published_at.should == Time.utc(2008, 1, 1)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
it "sets a time from a time" do
|
|
55
|
+
@entry.published_at = Time.utc(2008, 1, 1)
|
|
56
|
+
@entry.published_at.should == Time.utc(2008, 1, 1)
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
describe "field types from retrieved model" do
|
|
61
|
+
before :all do
|
|
62
|
+
@entry = Entry.new :title => 'whoa', :a => 'ignored', :comments_count => "3.0", :rating => '5', :published_at => Time.utc(2008, 1, 1)
|
|
63
|
+
@entry.save
|
|
64
|
+
@entry = Entry.find(@entry.uuid)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
it "sets a string" do
|
|
68
|
+
@entry.title = 'whoa'
|
|
69
|
+
@entry.title.should == 'whoa'
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
it "sets an integer" do
|
|
73
|
+
@entry.comments_count = '3.0'
|
|
74
|
+
@entry.comments_count.should == 3
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
it "sets a float" do
|
|
78
|
+
@entry.rating = 5
|
|
79
|
+
@entry.rating.should == 5.0
|
|
80
|
+
@entry.rating.to_s.should == "5.0"
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
it "sets a time" do
|
|
84
|
+
@entry.published_at.should == Time.utc(2008, 1, 1)
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'test_helper'))
|
|
2
|
+
require 'heresy/formatting/json'
|
|
3
|
+
|
|
4
|
+
module HeresyTest::Formatting
|
|
5
|
+
class JsonTest < Heresy::TestCase
|
|
6
|
+
before :all do
|
|
7
|
+
@formatter = Heresy::Formatting::Json
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
it "encodes and decodes hash with strings" do
|
|
11
|
+
input = {:a => 1, :b => 'two'}
|
|
12
|
+
@formatter.decode(@formatter.encode(input)).should == {'a' => 1, 'b' => 'two'}
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
data/test/index_test.rb
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'test_helper'))
|
|
2
|
+
|
|
3
|
+
module HeresyTest
|
|
4
|
+
class IndexTest < Heresy::TestCase
|
|
5
|
+
describe "Index creation" do
|
|
6
|
+
before :all do
|
|
7
|
+
@index = Heresy::Index.new(Heresy.db, :foo_bar_baz, :foo_id) { |idx| idx.field(:name, :varchar, :size => 255) }
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
after do
|
|
11
|
+
@index.drop rescue nil
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
it "generates CREATE TABLE sql" do
|
|
15
|
+
@index.to_sql.first.should =~ /CREATE TABLE/
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
it "detects existence of table" do
|
|
19
|
+
assert !@index.exists?
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
it "creates table" do
|
|
23
|
+
assert !@index.exists?
|
|
24
|
+
@index.create
|
|
25
|
+
assert @index.exists?
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
it "drops table" do
|
|
29
|
+
assert !@index.exists?
|
|
30
|
+
@index.create
|
|
31
|
+
assert @index.exists?
|
|
32
|
+
@index.drop
|
|
33
|
+
assert !@index.exists?
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
describe "Index" do
|
|
38
|
+
before :all do
|
|
39
|
+
@index = Heresy::Index.new(Heresy.db, :foo_bar_baz, :foo_id) { |idx| idx.field(:name, :varchar, :size => 255) }
|
|
40
|
+
@index.create
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
it "allows data to be inserted" do
|
|
44
|
+
old = @index.count
|
|
45
|
+
@index << {:foo_id => UUID.generate(:compact), :name => 'bob'}
|
|
46
|
+
@index.count.should == old + 1
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
it "clears data" do
|
|
50
|
+
@index << {:foo_id => UUID.generate(:compact), :name => 'bob'}
|
|
51
|
+
@index.clear
|
|
52
|
+
@index.count.should == 0
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
describe "(data retrieval)" do
|
|
56
|
+
before :all do
|
|
57
|
+
@index.clear
|
|
58
|
+
@uuid1 = UUID.generate(:compact)
|
|
59
|
+
@uuid2 = UUID.generate(:compact)
|
|
60
|
+
@index << {:foo_id => @uuid1, :name => 'bob'}
|
|
61
|
+
@index << {:foo_id => @uuid2, :name => 'bob'}
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
it "#find fetches data" do
|
|
65
|
+
@index.find(:name => 'bob').should == [@uuid1, @uuid2]
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
after :all do
|
|
70
|
+
@index.drop rescue nil
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
data/test/model_test.rb
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'test_helper'))
|
|
2
|
+
|
|
3
|
+
module HeresyTest
|
|
4
|
+
class ModelTest < Heresy::TestCase
|
|
5
|
+
before :all do
|
|
6
|
+
@entry = Entry.new(:title => 'whoa', :body => 'test')
|
|
7
|
+
@entry.save
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
describe "#save on new entry" do
|
|
11
|
+
before :all do
|
|
12
|
+
@new = Entry.new :title => 'whoa', :a => 'ignored', :comments_count => "3.0", :rating => '5'
|
|
13
|
+
@new.save
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
it "sets id" do
|
|
17
|
+
@new.id.should_not == nil
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it "sets uuid" do
|
|
21
|
+
@new.uuid.should_not == nil
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
it "sets updated_at" do
|
|
25
|
+
@new.updated_at.class.should == Time
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
it "sets title" do
|
|
29
|
+
@new.title.should == 'whoa'
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
it "sets comments_count" do
|
|
33
|
+
@new.comments_count.should == 3
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
it "sets rating" do
|
|
37
|
+
@new.rating.should == 5.0
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
describe "#save on existing entry" do
|
|
42
|
+
before :all do
|
|
43
|
+
sleep 1
|
|
44
|
+
@old_id = @entry.id
|
|
45
|
+
@old_uuid = @entry.uuid
|
|
46
|
+
@old_updated = @entry.updated_at
|
|
47
|
+
@entry.save
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
it "keeps id" do
|
|
51
|
+
@entry.id.should == @old_id
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
it "keeps uuid" do
|
|
55
|
+
@entry.uuid.should == @old_uuid
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
it "sets updated_at" do
|
|
59
|
+
@entry.updated_at.should_not == @old_updated
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
describe "#find" do
|
|
64
|
+
before :all do
|
|
65
|
+
@entry2 = Entry.new(:title => 'whoa', :body => 'test')
|
|
66
|
+
@entry2.save
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
it "fetches by uuid" do
|
|
70
|
+
Entry.find(@entry.uuid).should == @entry
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
it "fetches by array uuids" do
|
|
74
|
+
Entry.find([@entry.uuid, @entry2.uuid]).should == [@entry, @entry2]
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
data/test/schema_test.rb
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'test_helper'))
|
|
2
|
+
|
|
3
|
+
module HeresyTest
|
|
4
|
+
class SchemaTest < Heresy::TestCase
|
|
5
|
+
describe "Heresy Model Schema instance" do
|
|
6
|
+
it "sets model schema name to plural underscored class name" do
|
|
7
|
+
Entry.schema.name.should == :entries
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
describe "Schema" do
|
|
12
|
+
before :all do
|
|
13
|
+
@schema = Heresy::Schema.new(Heresy.db, :foo_bar_baz)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
after do
|
|
17
|
+
@schema.drop rescue nil
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it "generates CREATE TABLE sql" do
|
|
21
|
+
@schema.to_sql.first.should =~ /CREATE TABLE/
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
it "detects existence of table" do
|
|
25
|
+
assert !@schema.exists?
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
it "creates table" do
|
|
29
|
+
assert !@schema.exists?
|
|
30
|
+
@schema.create
|
|
31
|
+
assert @schema.exists?
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
it "drops table" do
|
|
35
|
+
assert !@schema.exists?
|
|
36
|
+
@schema.create
|
|
37
|
+
assert @schema.exists?
|
|
38
|
+
@schema.drop
|
|
39
|
+
assert !@schema.exists?
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
data/test/test_helper.rb
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
require 'rubygems'
|
|
2
|
+
require 'test/unit'
|
|
3
|
+
|
|
4
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
|
5
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
|
6
|
+
|
|
7
|
+
require 'rubygems'
|
|
8
|
+
gem 'jeremymcanally-matchy'
|
|
9
|
+
gem 'jeremymcanally-context'
|
|
10
|
+
gem 'rr'
|
|
11
|
+
|
|
12
|
+
require 'context'
|
|
13
|
+
require 'matchy'
|
|
14
|
+
require 'rr'
|
|
15
|
+
require 'logger'
|
|
16
|
+
require 'heresy-with-gems'
|
|
17
|
+
|
|
18
|
+
# TODO: remove this once it gets fixed in context
|
|
19
|
+
class String
|
|
20
|
+
def to_method_name
|
|
21
|
+
self.downcase.gsub(/[\s:',;!#()\.]+/,'_')
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
module Heresy
|
|
26
|
+
class TestCase < Test::Unit::TestCase
|
|
27
|
+
include RR::Adapters::TestUnit
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
module HeresyTest
|
|
32
|
+
class Entry < Heresy::Model
|
|
33
|
+
fields do |f|
|
|
34
|
+
f.string :title
|
|
35
|
+
f.string :body
|
|
36
|
+
f.integer :comments_count
|
|
37
|
+
f.float :rating
|
|
38
|
+
f.datetime :published_at
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
Heresy.db = Sequel.connect("mysql://root@localhost/heresy_test")
|
|
44
|
+
HeresyTest::Entry.schema.drop rescue nil
|
|
45
|
+
HeresyTest::Entry.schema.create
|
|
46
|
+
|
|
47
|
+
begin
|
|
48
|
+
require 'ruby-debug'
|
|
49
|
+
Debugger.start
|
|
50
|
+
rescue LoadError
|
|
51
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: kabuki-heresy
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- kabuki
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
|
|
12
|
+
date: 2009-03-29 00:00:00 -07:00
|
|
13
|
+
default_executable:
|
|
14
|
+
dependencies:
|
|
15
|
+
- !ruby/object:Gem::Dependency
|
|
16
|
+
name: activesupport
|
|
17
|
+
type: :runtime
|
|
18
|
+
version_requirement:
|
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
20
|
+
requirements:
|
|
21
|
+
- - ~>
|
|
22
|
+
- !ruby/object:Gem::Version
|
|
23
|
+
version: 2.2.0
|
|
24
|
+
version:
|
|
25
|
+
- !ruby/object:Gem::Dependency
|
|
26
|
+
name: sequel
|
|
27
|
+
type: :runtime
|
|
28
|
+
version_requirement:
|
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - ~>
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: 2.10.0
|
|
34
|
+
version:
|
|
35
|
+
- !ruby/object:Gem::Dependency
|
|
36
|
+
name: json
|
|
37
|
+
type: :runtime
|
|
38
|
+
version_requirement:
|
|
39
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
40
|
+
requirements:
|
|
41
|
+
- - ~>
|
|
42
|
+
- !ruby/object:Gem::Version
|
|
43
|
+
version: 1.1.3
|
|
44
|
+
version:
|
|
45
|
+
- !ruby/object:Gem::Dependency
|
|
46
|
+
name: uuid
|
|
47
|
+
type: :runtime
|
|
48
|
+
version_requirement:
|
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - ~>
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: 2.0.1
|
|
54
|
+
version:
|
|
55
|
+
description:
|
|
56
|
+
email: kabukiruby@gmail.com
|
|
57
|
+
executables: []
|
|
58
|
+
|
|
59
|
+
extensions: []
|
|
60
|
+
|
|
61
|
+
extra_rdoc_files:
|
|
62
|
+
- README.rdoc
|
|
63
|
+
- LICENSE
|
|
64
|
+
files:
|
|
65
|
+
- README.rdoc
|
|
66
|
+
- VERSION.yml
|
|
67
|
+
- lib/heresy
|
|
68
|
+
- lib/heresy/formatting
|
|
69
|
+
- lib/heresy/formatting/json.rb
|
|
70
|
+
- lib/heresy/index.rb
|
|
71
|
+
- lib/heresy/model.rb
|
|
72
|
+
- lib/heresy/schema
|
|
73
|
+
- lib/heresy/schema/column.rb
|
|
74
|
+
- lib/heresy/schema/fields.rb
|
|
75
|
+
- lib/heresy/schema.rb
|
|
76
|
+
- lib/heresy-with-gems.rb
|
|
77
|
+
- lib/heresy.rb
|
|
78
|
+
- test/fields_test.rb
|
|
79
|
+
- test/formatting
|
|
80
|
+
- test/formatting/json_test.rb
|
|
81
|
+
- test/index_test.rb
|
|
82
|
+
- test/model_test.rb
|
|
83
|
+
- test/schema_test.rb
|
|
84
|
+
- test/test_helper.rb
|
|
85
|
+
- LICENSE
|
|
86
|
+
has_rdoc: true
|
|
87
|
+
homepage: http://github.com/kabuki/heresy
|
|
88
|
+
post_install_message:
|
|
89
|
+
rdoc_options:
|
|
90
|
+
- --inline-source
|
|
91
|
+
- --charset=UTF-8
|
|
92
|
+
require_paths:
|
|
93
|
+
- lib
|
|
94
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
95
|
+
requirements:
|
|
96
|
+
- - ">="
|
|
97
|
+
- !ruby/object:Gem::Version
|
|
98
|
+
version: "0"
|
|
99
|
+
version:
|
|
100
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
101
|
+
requirements:
|
|
102
|
+
- - ">="
|
|
103
|
+
- !ruby/object:Gem::Version
|
|
104
|
+
version: "0"
|
|
105
|
+
version:
|
|
106
|
+
requirements: []
|
|
107
|
+
|
|
108
|
+
rubyforge_project:
|
|
109
|
+
rubygems_version: 1.2.0
|
|
110
|
+
signing_key:
|
|
111
|
+
specification_version: 2
|
|
112
|
+
summary: TODO
|
|
113
|
+
test_files: []
|
|
114
|
+
|