activefile 0.0.0beta
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 +15 -0
- data/MIT-LICENSE +20 -0
- data/README.rdoc +35 -0
- data/Rakefile +18 -0
- data/lib/active_file/adapter.rb +273 -0
- data/lib/active_file/base.rb +21 -0
- data/lib/activefile.rb +29 -0
- metadata +49 -0
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
NTQ1YjFmMGQ2NjI5ZWU4Y2ZjYTNhYjY2NjQwYTE1ZDcxZmI4ODM4ZQ==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
NGZhMzQwMDY4NzVmNjMxZTY2NjFmYzhjMjliNWU5OTQwMjQ1ZjhjOQ==
|
7
|
+
!binary "U0hBNTEy":
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
YTFhZjEyNDZiYWE4ZTAwNTE0MGQzZWQ5NjQ0NzNmMTAwMWQ0MjYwMWM2MTRm
|
10
|
+
ZmU3ZThiODUzNzY0Yzc1N2U1YmM1YjQ4YjA2YjI2ODg5ZTE0OTkyMWQ2NWYz
|
11
|
+
MjMwZmI3ZDBlNTAyOTFhNjE4NjczZThkM2VmZDc3MjA4ZjkyNGQ=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
OTU4ZmZjN2RmMGJmZTUxYjM1OTk4YmVjMDczNDQ5NzFkYjQ3MTEwNGY5OGQ2
|
14
|
+
MmYzNWMwNzFlNjcwZTljZDE0OTliMmMzZjM2OTI1MTJlNDczZGJmN2Q5YWIy
|
15
|
+
OTNjZWIxZDU2ODk5ZWRlNzY0NjI0ZGVjM2MyNjM3NTdmMjJhZGY=
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2012 YOURNAME
|
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,35 @@
|
|
1
|
+
== Gem under construction. Base functionality already done, but I still need reorganize it. I work with Unit tests now.
|
2
|
+
|
3
|
+
Please, be patient! All work will be done untill May.
|
4
|
+
|
5
|
+
== Welcome to ActiveFile
|
6
|
+
|
7
|
+
ActiveFile is a lightweight file system ORM.
|
8
|
+
|
9
|
+
Build a persistent domain model by mapping file system objects to Ruby classes. It inherits ActiveRecord-similar interface.
|
10
|
+
|
11
|
+
|
12
|
+
|
13
|
+
class Shop < ActiveFile::Base
|
14
|
+
parent_to :product
|
15
|
+
end
|
16
|
+
|
17
|
+
class Product < ActiveFile::Base
|
18
|
+
child_of :shop
|
19
|
+
end
|
20
|
+
|
21
|
+
shop = Shop.new(:name => "Apple Store")
|
22
|
+
shop.save!
|
23
|
+
|
24
|
+
Shop.all.size #> 1
|
25
|
+
|
26
|
+
iPad = Product.new(:name => "iPad", :parent => shop, :data => "The iPad is a line of tablet computers designed and marketed by Apple Inc., which runs Apple's iOS operating system.")
|
27
|
+
iPad.save!
|
28
|
+
|
29
|
+
product = Product.where(:name => "iPad")[0]
|
30
|
+
product.data #> "The iPad "...
|
31
|
+
product.shop #> <Shop instance>
|
32
|
+
product.shop.name #> "Apple Store"
|
33
|
+
|
34
|
+
|
35
|
+
# In result, two persistent files were created, accessible via ORM mechanism.
|
data/Rakefile
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
|
2
|
+
desc "Default Task"
|
3
|
+
task default: [ :test ]
|
4
|
+
|
5
|
+
# Run the unit tests
|
6
|
+
#Rake::TestTask.new { |t|
|
7
|
+
## t.libs << "test"
|
8
|
+
# t.pattern = 'test/**/*_test.rb'
|
9
|
+
# t.warning = true
|
10
|
+
# t.verbose = true
|
11
|
+
#}
|
12
|
+
|
13
|
+
task :test do
|
14
|
+
ruby = File.join(*RbConfig::CONFIG.values_at('bindir', 'RUBY_INSTALL_NAME'))
|
15
|
+
Dir.glob("test/**/*_test.rb").all? do |file|
|
16
|
+
sh(ruby, '-Ilib:test', file)
|
17
|
+
end or raise "Failures"
|
18
|
+
end
|
@@ -0,0 +1,273 @@
|
|
1
|
+
module ActiveFile
|
2
|
+
# Data Source Storage Adapter
|
3
|
+
module Adapter
|
4
|
+
require 'fileutils'
|
5
|
+
RAISE_TRUE = true
|
6
|
+
RAISE_FALSE = false
|
7
|
+
|
8
|
+
def initialize args
|
9
|
+
super args
|
10
|
+
end
|
11
|
+
|
12
|
+
def base_folder arg
|
13
|
+
puts "BaseFolder is #{arg}"
|
14
|
+
end
|
15
|
+
|
16
|
+
|
17
|
+
# touch file to read!
|
18
|
+
def load!
|
19
|
+
self.data
|
20
|
+
self
|
21
|
+
end
|
22
|
+
|
23
|
+
def data= data_arg
|
24
|
+
@self_data = data_arg
|
25
|
+
end
|
26
|
+
|
27
|
+
def data
|
28
|
+
if @self_data == nil
|
29
|
+
@self_data = File.read(self.get_source_path)
|
30
|
+
end
|
31
|
+
@self_data || ""
|
32
|
+
end
|
33
|
+
|
34
|
+
# Main rule to link attaches (css) with their targets (layouts, contents). Special filename format used for this purpose;
|
35
|
+
def attach_my_name!
|
36
|
+
# Is we an attached instance?
|
37
|
+
unless self.target.nil?
|
38
|
+
#@ext = CSS_EXT if self.type == SourceType::CSS
|
39
|
+
self.name = target.type.to_s + TARGET_DIVIDER + target.name #+ @ext.to_s
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# Get source folder. Create, if not exists.
|
44
|
+
def get_source_folder
|
45
|
+
Adapter.get_source_folder(type)
|
46
|
+
end
|
47
|
+
def get_name
|
48
|
+
attach_my_name!
|
49
|
+
return name
|
50
|
+
end
|
51
|
+
# Get source filename
|
52
|
+
def get_filename
|
53
|
+
get_name + get_extension
|
54
|
+
end
|
55
|
+
# Alias for get_source_path
|
56
|
+
def get_filepath
|
57
|
+
get_source_path
|
58
|
+
end
|
59
|
+
# Get source path. For targeted objects, target name + '--' + target type appends
|
60
|
+
def get_source_path
|
61
|
+
raise ArgumentError, 'Name can not be blank!' if name.blank? && target.nil?
|
62
|
+
raise ArgumentError, 'Target name can not be blank!' if target && target.name.blank?
|
63
|
+
get_source_folder + get_filename
|
64
|
+
end
|
65
|
+
def get_extension
|
66
|
+
return ".scss" if type == SourceType::CSS
|
67
|
+
return extension.blank? ? "" : "."+extension
|
68
|
+
|
69
|
+
type_ext = SOURCE_TYPE_EXTENSIONS[type.to_i] || ""
|
70
|
+
return "" if type_ext == "*"
|
71
|
+
if type_ext == "*"
|
72
|
+
unless new_record?
|
73
|
+
# Custom extension, from filename
|
74
|
+
Dir.glob(dir+"*").each do |f|
|
75
|
+
name_with_extension = f.split('/').last
|
76
|
+
name, ext = name_with_extension.split('.')
|
77
|
+
if name == get_name
|
78
|
+
type_ext = ext
|
79
|
+
break
|
80
|
+
end
|
81
|
+
end
|
82
|
+
else
|
83
|
+
_name, _ext = name.split(".")
|
84
|
+
unless _name.empty? && _ext.empty?
|
85
|
+
type_ext = _ext
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
type_ext = "." + type_ext unless type_ext.empty?
|
90
|
+
type_ext
|
91
|
+
end
|
92
|
+
# Get unique id of the source
|
93
|
+
def get_id
|
94
|
+
ID_PREFIX + type.to_s + ID_DIVIDER + get_name
|
95
|
+
end
|
96
|
+
# Rename the source file, return boolean operation result
|
97
|
+
def rename(new_file_name)
|
98
|
+
raise "Source is new record, call the save! method before rename." if new_record?
|
99
|
+
# rename attached file, if present ()
|
100
|
+
attach = get_attach
|
101
|
+
# later..
|
102
|
+
old_file_path = get_source_path
|
103
|
+
new_file_path = get_source_folder + new_file_name + get_extension
|
104
|
+
b_result = !!File.rename(old_file_path, new_file_path)
|
105
|
+
raise "Unable to rename source" unless b_result
|
106
|
+
self.name = new_file_name
|
107
|
+
if attach
|
108
|
+
# to rename attach, create new copy of attached object with new target name, and delete old
|
109
|
+
attach_source = attach.clone
|
110
|
+
attach_source.target = self
|
111
|
+
b_result = attach_source.save! && attach.delete!
|
112
|
+
raise 'Attached file rename failed' unless b_result
|
113
|
+
end
|
114
|
+
b_result
|
115
|
+
end
|
116
|
+
def new_record?
|
117
|
+
p get_source_path
|
118
|
+
!File.exists?(get_source_path)
|
119
|
+
end
|
120
|
+
private
|
121
|
+
def save_method(raise_exception_on_error)
|
122
|
+
File.open(get_source_path, "w") do
|
123
|
+
|file| file.write(data.force_encoding('utf-8'))
|
124
|
+
end
|
125
|
+
rescue => e
|
126
|
+
return raise_exception_on_error == RAISE_TRUE ? raise(e) : false
|
127
|
+
end
|
128
|
+
def delete_method(raise_exception_on_error)
|
129
|
+
delete_file_name = get_source_path
|
130
|
+
File.delete(delete_file_name)
|
131
|
+
return true
|
132
|
+
rescue => e
|
133
|
+
return raise_exception_on_error == RAISE_TRUE ? raise(e) : false
|
134
|
+
end
|
135
|
+
public
|
136
|
+
# Save the source, return boolean operation result
|
137
|
+
def save
|
138
|
+
save_method(RAISE_FALSE)
|
139
|
+
end
|
140
|
+
def save!
|
141
|
+
save_method(RAISE_TRUE)
|
142
|
+
end
|
143
|
+
# Delete the source: if successful returns true, else return false
|
144
|
+
def delete
|
145
|
+
delete_method(RAISE_FALSE)
|
146
|
+
end
|
147
|
+
# Delete the source: if successful returns true, else raise an error
|
148
|
+
def delete!
|
149
|
+
raise StandardError, "Unable to delete file if object hasn't been saved yet" if new_record?
|
150
|
+
delete_method(RAISE_TRUE)
|
151
|
+
end
|
152
|
+
def get_target_type
|
153
|
+
name.split(TARGET_DIVIDER)[0]
|
154
|
+
end
|
155
|
+
def get_target_name
|
156
|
+
name.split(TARGET_DIVIDER)[1]
|
157
|
+
end
|
158
|
+
# Get target object
|
159
|
+
def get_target
|
160
|
+
return nil unless name.include?(TARGET_DIVIDER)
|
161
|
+
#target_type, target_name = name[0..-2].split(TARGET_DIVIDER)
|
162
|
+
target_type = get_target_type
|
163
|
+
target_name = get_target_name
|
164
|
+
|
165
|
+
target_type_extension = SOURCE_TYPE_EXTENSIONS[target_type.to_i]
|
166
|
+
unless target_type_extension.empty?
|
167
|
+
target_type_extension = "." + target_type_extension
|
168
|
+
end
|
169
|
+
|
170
|
+
target = Adapter.get_source_folder(target_type) + target_name + target_type_extension
|
171
|
+
return nil unless File.exists?(target)
|
172
|
+
return Source.new({ :type => target_type.to_i, :name => target_name, :data => nil })
|
173
|
+
end
|
174
|
+
# Get attached object
|
175
|
+
def get_attach attach_type=SourceType::CSS
|
176
|
+
Adapter.where(:type => attach_type, :name => get_attached_name).first
|
177
|
+
end
|
178
|
+
def get_attached_name
|
179
|
+
type.to_s + TARGET_DIVIDER + name
|
180
|
+
end
|
181
|
+
#def get_attached_filename
|
182
|
+
# attached_file_extension = SOURCE_TYPE_EXTENSIONS[attach_type.to_i]
|
183
|
+
# attached_file_extension = "." + attached_file_extension unless attached_file_extension.empty?
|
184
|
+
# type.to_s + TARGET_DIVIDER + name + attached_file_extension
|
185
|
+
#end
|
186
|
+
def get_attach_or_create attach_type=SourceType::CSS
|
187
|
+
attach = get_attach(attach_type)
|
188
|
+
if attach.nil?
|
189
|
+
attach = Source.new(:type => attach_type, :extension => SOURCE_TYPE_EXTENSIONS[attach_type])
|
190
|
+
attach.target = self
|
191
|
+
attach.save!
|
192
|
+
end
|
193
|
+
attach
|
194
|
+
end
|
195
|
+
#
|
196
|
+
# STATIC METHODS MODULE
|
197
|
+
module ClassMethods
|
198
|
+
# Creates a new source instance and saves it to disk. Returns the newly created source. If a failure has occurred or source already exists -
|
199
|
+
# an exception will be raised.
|
200
|
+
def create(attributes={})
|
201
|
+
raise ArgumentError, "expected an attributes Hash, got #{attributes.inspect}" unless attributes.is_a?(Hash)
|
202
|
+
source_instance = Source.new(attributes)
|
203
|
+
raise 'Source file with such name already exists!' if File.exists?(source_instance.get_source_path)
|
204
|
+
source_instance.save!
|
205
|
+
return source_instance
|
206
|
+
end
|
207
|
+
# Get source folder for any source type. Create, if not exists.
|
208
|
+
def get_source_folder(type)
|
209
|
+
source_folder = Rails.env == 'test' ? TEST_SOURCE_FOLDERS[type.to_i || SourceType::UNDEFINED] : SOURCE_FOLDERS[type.to_i || SourceType::UNDEFINED]
|
210
|
+
FileUtils.mkpath(source_folder) unless File.exists?(source_folder)
|
211
|
+
return source_folder
|
212
|
+
end
|
213
|
+
# Get names array of all sources with specified type
|
214
|
+
def all_by_type(source_type)
|
215
|
+
files = Array.new
|
216
|
+
dir = Adapter.get_source_folder(source_type)
|
217
|
+
source_extension = SOURCE_TYPE_EXTENSIONS[source_type.to_i]
|
218
|
+
|
219
|
+
Dir.glob(dir+"*").each do |f|
|
220
|
+
name_with_extension = f.split('/').last
|
221
|
+
extension = name_with_extension.split('.').size > 1 ? name_with_extension.split('.').last : ""
|
222
|
+
name_without_extension = nil
|
223
|
+
|
224
|
+
name_without_extension = name_with_extension
|
225
|
+
|
226
|
+
if source_extension.blank?
|
227
|
+
name_without_extension = name_with_extension
|
228
|
+
else
|
229
|
+
name_without_extension = source_extension == "*" ? name_with_extension.split('.').first : name_with_extension[0..-source_extension.length-2]
|
230
|
+
end
|
231
|
+
|
232
|
+
s = Source.new({ :type => source_type, :name => name_without_extension, :extension => extension, :data => nil })
|
233
|
+
target_object = s.get_target
|
234
|
+
s.target = target_object unless target_object.nil?
|
235
|
+
files.push(s)
|
236
|
+
end
|
237
|
+
return files
|
238
|
+
end
|
239
|
+
def all
|
240
|
+
(Rails.env == 'test' ? TEST_SOURCE_FOLDERS : SOURCE_FOLDERS).map {|key_type, val| Adapter.all_by_type(key_type) }.reject { |ar| ar.empty? }.flatten
|
241
|
+
end
|
242
|
+
# Find the source
|
243
|
+
def where(attributes)
|
244
|
+
raise ArgumentError, "expected an attributes Hash, got #{attributes.inspect}" unless attributes.is_a?(Hash)
|
245
|
+
Adapter.all.select do |source|
|
246
|
+
match = true
|
247
|
+
attributes.each{|key, val|
|
248
|
+
match = false if source.send(key) != val
|
249
|
+
}
|
250
|
+
match
|
251
|
+
end
|
252
|
+
end
|
253
|
+
def find_by_id(id)
|
254
|
+
id = id[ID_PREFIX.size .. -1]
|
255
|
+
type, name = id.include?(TARGET_DIVIDER) ? (id).split(ID_DIVIDER) : id.split(ID_DIVIDER)
|
256
|
+
Adapter.find_by_name_and_type(name, type.to_i).first
|
257
|
+
end
|
258
|
+
# Complex finders:
|
259
|
+
def method_missing(m, *args, &block)
|
260
|
+
if m.to_s.index("find_by_") == 0
|
261
|
+
attributes = m["find_by_".size..-1].split("_and_")
|
262
|
+
raise "Attributes count expected: #{attributes.size}, got: #{args.size}" unless attributes.size == args.size
|
263
|
+
match_hash = {}
|
264
|
+
attributes.each_with_index {|attr, index| match_hash[attr.to_sym] = args[index]}
|
265
|
+
return Adapter.where(match_hash)
|
266
|
+
else
|
267
|
+
puts "There's no method called #{m} here -- please try again with args #{args}"
|
268
|
+
end
|
269
|
+
end
|
270
|
+
end
|
271
|
+
extend ClassMethods
|
272
|
+
end
|
273
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module ActiveFile
|
2
|
+
require 'ostruct'
|
3
|
+
class Base < OpenStruct
|
4
|
+
|
5
|
+
|
6
|
+
include Adapter
|
7
|
+
extend Adapter::ClassMethods
|
8
|
+
def ahola
|
9
|
+
puts 'ahols here!'
|
10
|
+
end
|
11
|
+
#'a'.camelize.safe_constantize
|
12
|
+
#def child_of(parent_name)
|
13
|
+
# puts "ok, I am a child of #{parent_name}"
|
14
|
+
#end
|
15
|
+
#
|
16
|
+
#def parent_to(child_name)
|
17
|
+
# puts "OH, I am a parent to #{child_name}"
|
18
|
+
#end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
data/lib/activefile.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2013 ariekdev
|
3
|
+
#
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
5
|
+
# a copy of this software and associated documentation files (the
|
6
|
+
# "Software"), to deal in the Software without restriction, including
|
7
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
8
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
9
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
10
|
+
# the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be
|
13
|
+
# included in all copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
17
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
18
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
19
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
20
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
21
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
22
|
+
#++
|
23
|
+
|
24
|
+
require "active_support/dependencies/autoload"
|
25
|
+
module ActiveFile
|
26
|
+
extend ActiveSupport::Autoload
|
27
|
+
autoload :Base
|
28
|
+
autoload :Adapter
|
29
|
+
end
|
metadata
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: activefile
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.0beta
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Vitaly Pestov
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2013-03-16 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: Build a hierarchical model of filesystem objects.
|
14
|
+
email: vitalyp@softwareplanet.uk.com
|
15
|
+
executables: []
|
16
|
+
extensions: []
|
17
|
+
extra_rdoc_files: []
|
18
|
+
files:
|
19
|
+
- lib/activefile.rb
|
20
|
+
- lib/active_file/adapter.rb
|
21
|
+
- lib/active_file/base.rb
|
22
|
+
- MIT-LICENSE
|
23
|
+
- Rakefile
|
24
|
+
- README.rdoc
|
25
|
+
homepage: http://www.interlink-ua.com
|
26
|
+
licenses:
|
27
|
+
- MIT
|
28
|
+
metadata: {}
|
29
|
+
post_install_message:
|
30
|
+
rdoc_options: []
|
31
|
+
require_paths:
|
32
|
+
- lib
|
33
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: 1.9.3
|
38
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
39
|
+
requirements:
|
40
|
+
- - ! '>'
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: 1.3.1
|
43
|
+
requirements: []
|
44
|
+
rubyforge_project:
|
45
|
+
rubygems_version: 2.0.2
|
46
|
+
signing_key:
|
47
|
+
specification_version: 4
|
48
|
+
summary: Object-relational mapper framework. Please, be patient. Under construction.
|
49
|
+
test_files: []
|