soup 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,44 @@
1
+ require 'rubygems'
2
+ require 'active_record'
3
+
4
+ class Tuple < ActiveRecord::Base
5
+ def self.prepare_database
6
+ ActiveRecord::Migration.create_table :tuples, :force => true do |t|
7
+ t.column :snip_id, :integer
8
+ t.column :name, :string
9
+ t.column :value, :text
10
+ t.column :created_at, :datetime
11
+ t.column :updated_at, :datetime
12
+ end
13
+ end
14
+
15
+ def self.for_snip(id)
16
+ find_all_by_snip_id(id)
17
+ end
18
+
19
+ def self.find_matching(name, value_conditions=nil)
20
+ condition_sql = "name = '#{name}'"
21
+ condition_sql += " AND value #{value_conditions}" if value_conditions
22
+ find(:all, :conditions => condition_sql)
23
+ end
24
+
25
+ def self.all_for_snip_named(name)
26
+ id = find_by_name_and_value("name", name).snip_id
27
+ for_snip(id)
28
+ end
29
+
30
+ # TODO: *totally* not threadsafe.
31
+ def self.next_snip_id
32
+ maximum(:snip_id) + 1 rescue 1
33
+ end
34
+
35
+ def save
36
+ super if new_record? || dirty?
37
+ end
38
+
39
+ private
40
+
41
+ def dirty?
42
+ true # hmm.
43
+ end
44
+ end
@@ -0,0 +1,47 @@
1
+ require 'rubygems'
2
+ require 'data_mapper'
3
+
4
+ DataMapper::Database.setup({
5
+ :adapter => 'sqlite3',
6
+ :database => 'soup_development.db'
7
+ })
8
+
9
+ # This tuple implementation is broken - there's a weird interaction
10
+ # where values are not set within the web application.
11
+ #
12
+ class Tuple < DataMapper::Base
13
+
14
+ property :snip_id, :integer
15
+
16
+ property :name, :string
17
+ property :value, :text
18
+
19
+ property :created_at, :datetime
20
+ property :updated_at, :datetime
21
+
22
+ def self.prepare_database
23
+ DataMapper::Persistence.auto_migrate!
24
+ end
25
+
26
+ def self.for_snip(id)
27
+ all(:snip_id => id)
28
+ end
29
+
30
+ def self.all_for_snip_named(name)
31
+ id = first(:name => "name", :value => name).snip_id
32
+ for_snip(id)
33
+ end
34
+
35
+ # TODO: *totally* not threadsafe.
36
+ def self.next_snip_id
37
+ database.query("SELECT MAX(snip_id) + 1 FROM tuples")[0] || 1
38
+ end
39
+
40
+ def save
41
+ if dirty? or new_record?
42
+ super
43
+ end
44
+ end
45
+
46
+ alias_method :destroy, :destroy!
47
+ end
@@ -0,0 +1,29 @@
1
+ require 'rubygems'
2
+ require 'sequel'
3
+
4
+ DB = Sequel.sqlite 'soup_development.db'
5
+
6
+ class Tuple < Sequel::Model(:tuples)
7
+ set_schema do
8
+ primary_key :id
9
+ integer :snip_id
10
+ string :name
11
+ string :value
12
+ datetime :created_at
13
+ datetime :updated_at
14
+ end
15
+
16
+ def self.for_snip(id)
17
+ Tuple.filter(:snip_id => id).to_a
18
+ end
19
+
20
+ # TODO: *totally* not threadsafe.
21
+ def self.next_snip_id
22
+ max(:snip_id) + 1
23
+ end
24
+ end
25
+
26
+
27
+ # Create the table with this:
28
+ #
29
+ # Tuple.create_table
@@ -0,0 +1,142 @@
1
+ require 'rubygems'
2
+ require 'blankslate'
3
+
4
+ # methods called on Tuple:
5
+ # Tuple.for_snip(id)
6
+ # Tuple.find_matching(tuple_name, tuple_value_conditions)
7
+ # Tuple.all_for_snip_named(name)
8
+ # Tuple.next_snip_id
9
+ # Tuple#save
10
+ # Tuple#name
11
+ # Tuple#value
12
+ # Tuple#destroy
13
+
14
+ class Snip < BlankSlate
15
+
16
+ # Returns the snip with the given name (i.e. the snip with the tuple of "name" -> name)
17
+ #
18
+ def self.[](name)
19
+ tuples = Tuple.all_for_snip_named(name)
20
+ snip = Snip.new(:__id => tuples.first.snip_id)
21
+ snip.replace_tuples(tuples)
22
+ snip
23
+ rescue
24
+ return nil
25
+ end
26
+
27
+ # Returns all snips which match the given criteria, i.e. which have a tuple that
28
+ # matches the given conditions. For example:
29
+ #
30
+ # Snip.with(:created_at, "> '2007-01-01'")
31
+ #
32
+ # should return all Snips who have a 'created_at' value greater than '2007-01-01'.
33
+ #
34
+ def self.with(name, tuple_value_conditions=nil)
35
+ matching_tuples = Tuple.find_matching(name, tuple_value_conditions)
36
+ matching_tuples.map { |t| t.snip_id }.uniq.map { |snip_id| find(snip_id) }
37
+ end
38
+
39
+ # Returns the snip with the given ID (i.e. the collection of all tuples
40
+ # with the matching snip_id, gathered into a magical snip.)
41
+ #
42
+ def self.find(id)
43
+ raise "not found" unless (tuples = Tuple.for_snip(id)).any?
44
+ snip = Snip.new(:__id => id)
45
+ snip.replace_tuples(tuples)
46
+ snip
47
+ end
48
+
49
+ attr_reader :tuples
50
+
51
+ def initialize(attributes = {})
52
+ set_id(attributes.delete(:__id))
53
+ @tuples = {}
54
+ attributes.each { |name, value| set_value(name, value) }
55
+ end
56
+
57
+ def save
58
+ raise "Saving would be pointless - there's no data!" if @tuples.empty?
59
+ set_id_if_necessary
60
+ each_tuple { |t| t.save }
61
+ self
62
+ end
63
+
64
+ def destroy
65
+ each_tuple { |t| t.destroy }
66
+ end
67
+
68
+ def reload
69
+ return self unless self.id
70
+ replace_tuples(Tuple.for_snip(id))
71
+ self
72
+ end
73
+
74
+ def attributes
75
+ @tuples.inject({}) { |h, (name, t)| h[name] = t.value; h }
76
+ end
77
+
78
+ def replace_tuples(new_tuples)
79
+ @tuples.clear
80
+ new_tuples.each { |tuple| @tuples[tuple.name] = tuple }
81
+ end
82
+
83
+ def to_s
84
+ "<Snip id:#{self.id || "unset"} #{tuples_as_string}>"
85
+ end
86
+
87
+ def inspect
88
+ "<Snip id:#{self.id || "unset"} name:#{self.name}>"
89
+ end
90
+
91
+ def method_missing(method, *args)
92
+ value = args.length > 1 ? args : args.first
93
+ if method.to_s =~ /(.*)=\Z/ # || value - could be a nice DSL touch.
94
+ set_value($1, value)
95
+ else
96
+ get_value(method.to_s)
97
+ end
98
+ end
99
+
100
+ def id #:nodoc: why is ID special?
101
+ @id
102
+ end
103
+
104
+
105
+ private
106
+
107
+ def each_tuple
108
+ @tuples.values.each { |tuple| yield tuple }
109
+ end
110
+
111
+ def set_id(id)
112
+ @id = id
113
+ self
114
+ end
115
+
116
+ def set_id_if_necessary
117
+ if self.id.nil?
118
+ set_id(Tuple.next_snip_id)
119
+ @tuples.values.each { |tuple| tuple.snip_id = self.id }
120
+ end
121
+ end
122
+
123
+ def tuples_as_string
124
+ @tuples.inject("") { |hash, (name, tuple)| hash += " #{name}:'#{tuple.value}'" }.strip
125
+ end
126
+
127
+ def get_value(name)
128
+ @tuples[name.to_s] ? @tuples[name.to_s].value : nil
129
+ end
130
+
131
+ def set_value(name, value)
132
+ tuple = @tuples[name.to_s]
133
+ if tuple
134
+ tuple.value = value
135
+ else
136
+ attributes = {:snip_id => self.id, :name => name.to_s, :value => value}
137
+ tuple = @tuples[name.to_s] = Tuple.new(attributes)
138
+ end
139
+ tuple.value
140
+ end
141
+
142
+ end
@@ -0,0 +1,7 @@
1
+ # require 'data_mapper_tuple'
2
+ # or
3
+ require 'active_record_tuple'
4
+ # or
5
+ # require 'sequel_tuple'
6
+
7
+ require 'snip'
metadata ADDED
@@ -0,0 +1,57 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: soup
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - James Adam
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2008-03-04 00:00:00 +00:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description:
17
+ email: james@lazyatom.com
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files: []
23
+
24
+ files:
25
+ - lib/active_record_tuple.rb
26
+ - lib/data_mapper_tuple.rb
27
+ - lib/sequel_tuple.rb
28
+ - lib/snip.rb
29
+ - lib/soup.rb
30
+ has_rdoc: false
31
+ homepage:
32
+ post_install_message:
33
+ rdoc_options: []
34
+
35
+ require_paths:
36
+ - lib
37
+ required_ruby_version: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ version: "0"
42
+ version:
43
+ required_rubygems_version: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: "0"
48
+ version:
49
+ requirements: []
50
+
51
+ rubyforge_project: soup
52
+ rubygems_version: 0.9.5
53
+ signing_key:
54
+ specification_version: 2
55
+ summary: I suppose it's a document database. Or a tuple store. But really, it's just data sloshing around, waiting to be used.
56
+ test_files: []
57
+