undestroyable 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,7 @@
1
+ Copyright (C) 2012 Kot-Begemot
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
+
5
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+
7
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,37 @@
1
+ # Strategies
2
+
3
+ * none
4
+ Deletes record, as with usual destroy method.
5
+ * column
6
+ Updates deleted_at column of the record. This is default strategy,
7
+ if undestroyable is activated for model.
8
+ * table
9
+ Move table into separate column
10
+ * database
11
+ Move table into separate database
12
+
13
+ # Options:
14
+
15
+ * table_name
16
+ Same as model table.
17
+ * table_suffix
18
+ Default prefix is: deleted
19
+ * full_table_name
20
+ table_prefix + table_name + table_suffix.
21
+
22
+ # Simple usage example:
23
+
24
+ class Bill < ActiveRecord::Base
25
+ undestroyable
26
+ end
27
+
28
+ # Complicated usage example:
29
+
30
+ class ConceptCat < ActiveRecord::Base
31
+ undstroyable do
32
+ startegy :database
33
+ connection { adapter: "sqlite3", dbfile: ":memory:"}
34
+ table_name :scrap
35
+ table_suffix :metalic
36
+ end
37
+ end
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rake/testtask'
3
+
4
+ Rake::TestTask.new do |t|
5
+ t.libs << "test"
6
+ t.test_files = FileList['test/cases/**/*_test.rb']
7
+ t.verbose = true
8
+ end
9
+
10
+ task :default => :test
@@ -0,0 +1,21 @@
1
+ module Undestroyable
2
+ autoload :Configuration, 'undestroyable/configuration'
3
+ autoload :STRATEGIES, 'undestroyable/strategies'
4
+ autoload :VERSION, 'undestroyable/version'
5
+
6
+ require 'undestroyable/orm'
7
+
8
+ module Orm
9
+ autoload :ActiveRecord, 'undestroyable/orm/active_record'
10
+ end
11
+
12
+ def self.config
13
+ @config ||= Configuration.new(copy_system: false).tap do |c|
14
+ def c.orm(orm_type)
15
+ constant = Undestroyable::Orm
16
+ loadable = Orm.get_by_key(orm_type)
17
+ constant.const_defined?(loadable) ? constant.const_get(loadable) : constant.const_missing(loadable)
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,142 @@
1
+ module Undestroyable
2
+
3
+ class WrongStrategyError < Exception; end
4
+ class ConnectionIsCompulsaryError < Exception; end
5
+
6
+ # Specifies which strategy to use for this configuration.
7
+ #
8
+ # Possible options include:
9
+ # [:startegy]
10
+ # [:none]
11
+ # Deletes record, as with usual destroy method. This option may be used as a cancelation
12
+ # of undestoyable state of record, for STI model as an example.
13
+ # [:column]
14
+ # Updates <tt>:deleted_at</tt> column of the record. This is default strategy,
15
+ # in cafe if undestroyable is specified for model.
16
+ # [:table]
17
+ # Move record into separate table.
18
+ # [:database]
19
+ # Move table into separate database.
20
+ # [:dump]
21
+ # Marshal dump all deleted records into common table table.
22
+ # [:table_name]
23
+ # Keep alternative to table name. By default it will equivalent to one used by Model backend.
24
+ # It is ignored by <tt>:none</tt> and <tt>::column</tt> strategies.
25
+ # [:table_prefix]
26
+ # Keep table prefix. If it is set not nil, it will be ignored for table creation. Default is nil.
27
+ # It is ignored by <tt>:none</tt> and <tt>::column</tt> strategies.
28
+ # [:table_suffix]
29
+ # Keep table suffix. If it is set not nil, it will be ignored for table creation. Default is nil.
30
+ # It is ignored by <tt>:none</tt> and <tt>::column</tt> strategies.
31
+ # [:full_table_name]
32
+ # Keep full table name. If this field is not specified it will be generated from
33
+ # table_name + table_prefix + table_suffix.
34
+ # It is ignored by <tt>:none</tt> and <tt>::column</tt> strategies.
35
+ # [:connection]
36
+ # Keep credetial information for remote database connection.
37
+ # *NB!* Compulsary for :database strategy, ignored for rest strategies.
38
+ #
39
+ # == EXAMPLES
40
+ #
41
+ # class ConceptCat < ActiveRecord::Base
42
+ # undstroyable do
43
+ # startegy :table
44
+ # table_name :metalic
45
+ # table_suffix :scrap
46
+ # end
47
+ # end
48
+ #
49
+ # class ConceptCat < ActiveRecord::Base
50
+ # undstroyable do
51
+ # startegy :database
52
+ # connection {}
53
+ # table_name :scrap
54
+ # end
55
+ # end
56
+ #
57
+ # class ConceptCat < ActiveRecord::Base
58
+ # undstroyable do
59
+ # startegy :dump
60
+ # table_name :scrap
61
+ # end
62
+ # end
63
+ #
64
+ class Configuration
65
+
66
+ attr_reader :configuration
67
+
68
+ def initialize(opts = {},&block)
69
+ copy_system = opts[:copy_system].nil? ? true : opts.delete(:copy_system)
70
+ @configuration ||= (copy_system ? Undestroyable.config.configuration.dup : {})
71
+ @configuration[:strategy] ||= :column
72
+ prepare &block if block_given?
73
+ end
74
+
75
+ def prepare &block
76
+ instance_eval &block
77
+ raise(::Undestroyable::ConnectionIsCompulsaryError.new "Connection information is compulsary") if database? && connection_settings.blank?
78
+ end
79
+ alias_method :setup, :prepare
80
+
81
+ #[:strategy, :table_name, :table_suffix, :full_table_name, :connection]
82
+
83
+ ::Undestroyable::STRATEGIES.each do |strategy|
84
+ define_method "#{strategy}?" do
85
+ configuration[:strategy] == strategy
86
+ end
87
+ end
88
+
89
+ # Allows to access <tt>configuration</tt> values via provided keys.
90
+ # Example:
91
+ #
92
+ # c = Undestroyable.config
93
+ # c[:strategy] # => :column
94
+ def [](option_name)
95
+ # :full_table_name will be generated on first request
96
+ generate_full_name if option_name == :full_table_name && !configuration[:full_table_name]
97
+ configuration[option_name]
98
+ end
99
+
100
+ def strategy(shrtkey)
101
+ raise WrongStrategyError.new(%Q{Such strategy "#{shrtkey}" does not exists.}) unless ::Undestroyable::STRATEGIES.include? shrtkey.to_sym
102
+ table_name('dump') if shrtkey.to_sym == :dump && !@configuration[:table_name]
103
+ @configuration[:strategy] = shrtkey.to_sym
104
+ end
105
+
106
+ def table_name(alternative_table_name)
107
+ @configuration[:table_name] = alternative_table_name
108
+ end
109
+
110
+ def table_prefix(alternative_table_prefix)
111
+ @configuration[:table_prefix] = alternative_table_prefix
112
+ end
113
+
114
+ def table_suffix(alternative_table_suffix)
115
+ @configuration[:table_suffix] = alternative_table_suffix
116
+ end
117
+
118
+ def full_table_name(alternative_full_table_name)
119
+ @configuration[:full_table_name] = alternative_full_table_name
120
+ end
121
+
122
+ def connection(alternative_connection)
123
+ @configuration[:connection] = alternative_connection
124
+ end
125
+
126
+ def connection_settings
127
+ @configuration[:connection]
128
+ end
129
+
130
+ private
131
+
132
+ def generate_full_name
133
+ if @configuration[:table_prefix] || @configuration[:table_name] || @configuration[:table_suffix]
134
+ table_name = "#{@configuration[:table_prefix].to_s}_" if @configuration[:table_prefix]
135
+ (table_name ||= '') << @configuration[:table_name].to_s
136
+ table_name << "_#{@configuration[:table_suffix].to_s}" if @configuration[:table_suffix]
137
+ @configuration[:full_table_name] = table_name.to_sym
138
+ end
139
+ end
140
+
141
+ end
142
+ end
@@ -0,0 +1,16 @@
1
+ module Undestroyable
2
+ module Orm
3
+ SUPPORTED = %(ActiveRecord).freeze
4
+
5
+ class UnexisingOrmError < RuntimeError; end
6
+
7
+ def Orm.get_by_key(key)
8
+ case key.to_sym
9
+ when :active_record
10
+ 'ActiveRecord'
11
+ else
12
+ raise UnexisingOrmError.new "No orm matching: :#{key.to_s}"
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,46 @@
1
+ require 'active_record'
2
+
3
+ module Undestroyable
4
+ module Orm
5
+ module ActiveRecord
6
+ autoload :Column, 'undestroyable/orm/active_record/column'
7
+ autoload :Table, 'undestroyable/orm/active_record/table'
8
+ autoload :Database, 'undestroyable/orm/active_record/database'
9
+ autoload :Dump, 'undestroyable/orm/active_record/dump'
10
+
11
+ def self.included(base)
12
+ base.instance_eval { alias_method :destroy!, :destroy }
13
+ base.extend ClassMethods
14
+ end
15
+
16
+ module ClassMethods
17
+ def undestroyable &block
18
+ undestr_config(&block) unless @undestr_config
19
+ undestr_config.table_name(table_name) unless undestr_config[:table_name]
20
+ undestr_include_startegy
21
+ undestr_config
22
+ end
23
+
24
+ protected
25
+
26
+ def undestr_include_startegy
27
+ undestr_strategy = undestr_config[:strategy]
28
+ if Undestroyable::STRATEGIES.include? undestr_strategy
29
+ if undestr_config.none?
30
+ #nothing should happen for this strategy.
31
+ else
32
+ include "Undestroyable::Orm::ActiveRecord::#{undestr_strategy.to_s.classify}".constantize
33
+ end
34
+ end
35
+ end
36
+
37
+ # Keeps undestroyable configuration for class.
38
+ def undestr_config &block
39
+ @undestr_config ||= Undestroyable::Configuration.new(&block)
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+
46
+ ActiveRecord::Base.send :include, Undestroyable::Orm::ActiveRecord
@@ -0,0 +1,29 @@
1
+ module Undestroyable
2
+ module Orm
3
+ module ActiveRecord
4
+ # <tt>:column</tt> strategy is the easiest among the rest. It just updates <tt>:deleted_at</tt> column
5
+ # of the record, and set default scope conditions, to prevent its returning by default. Despite the record
6
+ # is actually just updated, no update callback will be triggered.
7
+ #
8
+ # == Example
9
+ #
10
+ # class ConceptCat < ActiveRecord::Base
11
+ # undstroyable do
12
+ # startegy :column
13
+ # end
14
+ # end
15
+ #
16
+ module Column
17
+ def self.included(base)
18
+ base.send :default_scope, base.where('deleted_at IS NULL')
19
+ end
20
+
21
+ def destroy
22
+ run_callbacks :destroy do
23
+ update_column :deleted_at, Time.now
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,64 @@
1
+ module Undestroyable
2
+ module Orm
3
+ module ActiveRecord
4
+ # <tt>:database</tt> strategy will save any deleted record within remote database. <tt>:connection</tt> option
5
+ # *is required*. By default, it will use same naming as a actual table. In order to change the table name,
6
+ # following options can be specified within configuration:
7
+ #
8
+ # [:table_name]
9
+ # Keep alternative to table name. By default it will equivalent to one used by Model backend.
10
+ # [:table_prefix]
11
+ # Keep table prefix. If it is set not nil, it will be ignored for table creation. Default is nil.
12
+ # [:table_suffix]
13
+ # Keep table suffix. If it is set not nil, it will be ignored for table creation. Default is nil.
14
+ # [:full_table_name]
15
+ # Keep full table name. If this field is not specified it will be generated from
16
+ # table_name + table_prefix + table_suffix.
17
+ #
18
+ # == Example
19
+ #
20
+ # class ConceptCat < ActiveRecord::Base
21
+ # undstroyable do
22
+ # startegy :database
23
+ # connection { adapter: "sqlite3", dbfile: ":memory:"}
24
+ # table_name :scrap
25
+ # table_suffix :metalic
26
+ # end
27
+ # end
28
+ #
29
+ module Database
30
+
31
+ def self.included(base)
32
+ base.send :include, Table
33
+ base.send :include, InstanceMethods
34
+ base.extend ClassMethods
35
+ end
36
+
37
+ module ClassMethods
38
+ # Because undest_arel_table is just being ignored over there....
39
+ def undest_mirror
40
+ @undest_mirror ||= begin
41
+ copy = self.dup
42
+ copy.table_name = undestr_config[:full_table_name]
43
+ def copy.name; undestr_config[:full_table_name].to_s.classify; end
44
+ copy.establish_connection undestr_config[:connection]
45
+ copy
46
+ end
47
+ end
48
+ protected :undest_mirror
49
+ end
50
+
51
+ module InstanceMethods
52
+ def destroy
53
+ run_callbacks :destroy do
54
+ deleted_attributes = undest_compile_insert_attributes
55
+ deleted_attributes << [self.class.undest_arel_table['deleted_at'], Time.now.utc] if self.class.send(:undest_mirror).columns_hash['deleted_at']
56
+ self.class.undest_relation.insert deleted_attributes
57
+ destroy!
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,84 @@
1
+ module Undestroyable
2
+ module Orm
3
+ module ActiveRecord
4
+ # <tt>:dump</tt> strategy is the most simple one. It requires only one table for whole project to save a data
5
+ # in it. What it does internally, is just marshal dump an object and save it into specific table. That table
6
+ # should have predefined format. That allows to keep an objects of different class within same table. In order
7
+ # to change the table name, following options can be specified within configuration:
8
+ #
9
+ # [:table_name]
10
+ # Keep alternative to table name. By default it will equivalent to one used by Model backend.
11
+ # [:table_prefix]
12
+ # Keep table prefix. If it is set not nil, it will be ignored for table creation. Default is nil.
13
+ # [:table_suffix]
14
+ # Keep table suffix. If it is set not nil, it will be ignored for table creation. Default is nil.
15
+ # [:full_table_name]
16
+ # Keep full table name. If this field is not specified it will be generated from
17
+ # table_name + table_prefix + table_suffix.
18
+ #
19
+ # == Table structure
20
+ #
21
+ # [:dump]
22
+ # This is a text column that keepes deleted object.
23
+ # [:deleted_at]
24
+ # This column keeps a time of the recodr deletion. Time is saved as UTC.
25
+ #
26
+ # == Example
27
+ #
28
+ # Undestroyable.config do
29
+ # strategy :dump
30
+ # full_table_name :dump
31
+ # end
32
+ #
33
+ # class Order < ActiveRecord::Base
34
+ # undestroyable
35
+ # end
36
+ #
37
+ # *or*
38
+ #
39
+ # class Invoice < ActiveRecord::Base
40
+ # undestroyable do
41
+ # strategy :dump
42
+ # table_suffix :dump
43
+ # end
44
+ # end
45
+ #
46
+ # +dump+ table will contain an order as an object.
47
+ #
48
+ # *NB!* Check out Marshal class in Ruby, for how to make this text back to object.
49
+ module Dump
50
+
51
+ def self.included(base)
52
+ base.send :include, InstanceMethods
53
+ base.extend ClassMethods
54
+ end
55
+
56
+ module ClassMethods
57
+ # Because undest_arel_table is just being ignored over there....
58
+ def undest_mirror
59
+ @undest_mirror ||= begin
60
+ dump_table = ::Undestroyable::Orm::ActiveRecord::DumpTable
61
+ dump_table.table_name = undestr_config[:full_table_name]
62
+ dump_table.establish_connection(undestr_config[:connection]) unless undestr_config[:connection].blank?
63
+ dump_table
64
+ end
65
+ end
66
+ protected :undest_mirror
67
+ end
68
+
69
+ module InstanceMethods
70
+ def destroy
71
+ run_callbacks :destroy do
72
+ dump_attributes = {dump: Marshal::dump(self)}
73
+ dump_attributes.merge!(deleted_at: Time.now.utc) if self.class.send(:undest_mirror).columns_hash['deleted_at']
74
+ self.class.send(:undest_mirror).create(dump_attributes)
75
+ destroy!
76
+ end
77
+ end
78
+ end
79
+ end
80
+
81
+ class DumpTable < ::ActiveRecord::Base; end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,76 @@
1
+ module Undestroyable
2
+ module Orm
3
+ module ActiveRecord
4
+ # <tt>:table</tt> strategy will save any deleted record within separate table. By default, it will use
5
+ # same naming as a actual table. In order to change the table name, following options can be specified
6
+ # within configuration:
7
+ #
8
+ # [:table_name]
9
+ # Keep alternative to table name. By default it will equivalent to one used by Model backend.
10
+ # [:table_prefix]
11
+ # Keep table prefix. If it is set not nil, it will be ignored for table creation. Default is nil.
12
+ # [:table_suffix]
13
+ # Keep table suffix. If it is set not nil, it will be ignored for table creation. Default is nil.
14
+ # [:full_table_name]
15
+ # Keep full table name. If this field is not specified it will be generated from
16
+ # table_name + table_prefix + table_suffix.
17
+ #
18
+ # == Example
19
+ #
20
+ # class ConceptCat < ActiveRecord::Base
21
+ # undstroyable do
22
+ # startegy :database
23
+ # connection { adapter: "sqlite3", dbfile: ":memory:"}
24
+ # table_name :scrap
25
+ # table_suffix :metalic
26
+ # end
27
+ # end
28
+ #
29
+ module Table
30
+ def self.included(base)
31
+ base.send :include, InstanceMethods
32
+ base.extend ClassMethods
33
+ end
34
+
35
+ module ClassMethods
36
+ def undest_arel_table
37
+ @undest_table ||= ::Arel::Table.new(undestr_config[:full_table_name], self)
38
+ end
39
+
40
+ def undest_relation
41
+ @undest_relation ||= ::ActiveRecord::Relation.new(undest_mirror, undest_arel_table)
42
+ end
43
+
44
+ # Because undest_arel_table is just being ignored over there....
45
+ def undest_mirror
46
+ @undest_mirror ||= begin
47
+ copy = self.dup
48
+ copy.table_name = undestr_config[:full_table_name]
49
+ copy
50
+ end
51
+ end
52
+ protected :undest_mirror
53
+ end
54
+
55
+ module InstanceMethods
56
+ def destroy
57
+ run_callbacks :destroy do
58
+ deleted_attributes = undest_compile_insert_attributes
59
+ deleted_attributes << [self.class.undest_arel_table['deleted_at'], Time.now.utc]
60
+ self.class.undest_relation.insert deleted_attributes
61
+ destroy!
62
+ end
63
+ end
64
+
65
+ def undest_compile_insert_attributes
66
+ attributes.map do |key, value|
67
+ [self.class.undest_arel_table[key], value]
68
+ end
69
+ end
70
+
71
+ protected :undest_compile_insert_attributes
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,3 @@
1
+ module Undestroyable
2
+ STRATEGIES = [:none,:column,:table,:database,:dump].freeze
3
+ end
@@ -0,0 +1,3 @@
1
+ module Undestroyable
2
+ VERSION = '0.0.1'
3
+ end
metadata ADDED
@@ -0,0 +1,114 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: undestroyable
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - E-Max
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-05-02 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: activesupport
16
+ requirement: &78020570 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: 3.0.0
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: *78020570
25
+ - !ruby/object:Gem::Dependency
26
+ name: activerecord
27
+ requirement: &78020340 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: 3.0.0
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: *78020340
36
+ - !ruby/object:Gem::Dependency
37
+ name: sqlite3
38
+ requirement: &78020140 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ type: :development
45
+ prerelease: false
46
+ version_requirements: *78020140
47
+ - !ruby/object:Gem::Dependency
48
+ name: database_cleaner
49
+ requirement: &78019870 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: 0.7.2
55
+ type: :development
56
+ prerelease: false
57
+ version_requirements: *78019870
58
+ - !ruby/object:Gem::Dependency
59
+ name: ruby-debug19
60
+ requirement: &78019660 !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ! '>='
64
+ - !ruby/object:Gem::Version
65
+ version: '0'
66
+ type: :development
67
+ prerelease: false
68
+ version_requirements: *78019660
69
+ description: An aim or this gem is to provide an agile and comfortable way for Ruby
70
+ developers to get rid of unnecessary data within their project databases.
71
+ email:
72
+ - emax@studentify.nl
73
+ executables: []
74
+ extensions: []
75
+ extra_rdoc_files: []
76
+ files:
77
+ - lib/undestroyable/version.rb
78
+ - lib/undestroyable/orm/active_record.rb
79
+ - lib/undestroyable/orm/active_record/dump.rb
80
+ - lib/undestroyable/orm/active_record/database.rb
81
+ - lib/undestroyable/orm/active_record/column.rb
82
+ - lib/undestroyable/orm/active_record/table.rb
83
+ - lib/undestroyable/orm.rb
84
+ - lib/undestroyable/strategies.rb
85
+ - lib/undestroyable/configuration.rb
86
+ - lib/undestroyable.rb
87
+ - LICENSE
88
+ - README.md
89
+ - Rakefile
90
+ homepage: http://github.com/kot-begemot/undestroyable
91
+ licenses: []
92
+ post_install_message:
93
+ rdoc_options: []
94
+ require_paths:
95
+ - lib
96
+ required_ruby_version: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ! '>='
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ required_rubygems_version: !ruby/object:Gem::Requirement
103
+ none: false
104
+ requirements:
105
+ - - ! '>='
106
+ - !ruby/object:Gem::Version
107
+ version: 1.3.6
108
+ requirements: []
109
+ rubyforge_project: undestroyable
110
+ rubygems_version: 1.8.10
111
+ signing_key:
112
+ specification_version: 3
113
+ summary: Undestroyable gem
114
+ test_files: []