ohm-arfreaks 0.0.1 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.markdown +44 -5
- data/Rakefile +1 -1
- data/lib/ohm-arfreaks.rb +107 -8
- data/spec/attributes_spec.rb +54 -0
- data/spec/columns_spec.rb +75 -0
- data/spec/db/redis.pid +0 -0
- data/spec/its_helper.rb +15 -0
- data/spec/methods_spec.rb +86 -0
- data/spec/model.rb +6 -0
- data/spec/provide_helper.rb +45 -0
- data/spec/redis.conf +13 -0
- data/spec/spec_helper.rb +22 -0
- data/spec/validations_spec.rb +9 -0
- metadata +22 -5
data/README.markdown
CHANGED
@@ -20,16 +20,55 @@ This library provides some of them to Ohm::Model.
|
|
20
20
|
|
21
21
|
require 'ohm-arfreaks'
|
22
22
|
|
23
|
-
class
|
23
|
+
class Video < Ohm::Model
|
24
24
|
attribute :url
|
25
|
+
set :tags
|
25
26
|
end
|
26
27
|
|
27
|
-
|
28
|
-
|
28
|
+
Video.first
|
29
|
+
=> #<Video id: 470, url: "a", tags: []>
|
29
30
|
|
30
|
-
|
31
|
+
Video.create!(:url=>...)
|
32
|
+
Video.new.save!
|
31
33
|
|
32
|
-
|
34
|
+
### Available Methods
|
35
|
+
|
36
|
+
def self.primary_key # AR
|
37
|
+
def self.columns # AR
|
38
|
+
def self.column_names # AR
|
39
|
+
def self.content_columns # AR
|
40
|
+
def self.columns_hash # AR
|
41
|
+
def self.create!(attributes) # AR
|
42
|
+
def save! # AR
|
43
|
+
def self.first(*args) # AR
|
44
|
+
def self.last(*args) # AR
|
45
|
+
def self.count(*args) # AR
|
46
|
+
def self.delete_all(cond = nil) # AR
|
47
|
+
def new_record? # AR
|
48
|
+
def attributes # AR
|
49
|
+
|
50
|
+
(reported by: % grep 'def ' lib/ohm-arfreaks.rb |grep '# AR' )
|
51
|
+
|
52
|
+
### NOTE
|
53
|
+
|
54
|
+
Ohm::Model#attributes returns an names of attributed fields in vanilla ohm gem.
|
55
|
+
Of course, it's not familier to our ar-freaks, so it's overwritten by this gem.
|
56
|
+
|
57
|
+
class Video < Ohm::Model
|
58
|
+
attribute :url
|
59
|
+
set :tags
|
60
|
+
end
|
61
|
+
|
62
|
+
Video.first.attributes
|
63
|
+
=> [:url] # in vanilla ohm
|
64
|
+
=> {:url=>"a", :tags=>[]} # in ohm-arfreaks
|
65
|
+
|
66
|
+
|
67
|
+
Test
|
68
|
+
----
|
69
|
+
|
70
|
+
% redis-server spec/redis.conf # running on port:6380 in default
|
71
|
+
% spec -c spec
|
33
72
|
|
34
73
|
|
35
74
|
Homepage
|
data/Rakefile
CHANGED
@@ -6,7 +6,7 @@ AUTHOR = "maiha"
|
|
6
6
|
EMAIL = "maiha@wota.jp"
|
7
7
|
HOMEPAGE = "http://github.com/maiha/ohm-arfreaks"
|
8
8
|
SUMMARY = "Ohm::Model extensions for AR freaks"
|
9
|
-
GEM_VERSION = "0.0
|
9
|
+
GEM_VERSION = "0.1.0"
|
10
10
|
|
11
11
|
spec = Gem::Specification.new do |s|
|
12
12
|
s.rubyforge_project = 'asakusarb'
|
data/lib/ohm-arfreaks.rb
CHANGED
@@ -1,34 +1,133 @@
|
|
1
1
|
module Ohm
|
2
2
|
class Model
|
3
|
+
######################################################################
|
4
|
+
### Columns
|
5
|
+
|
6
|
+
Column = Struct.new(:name, :type)
|
7
|
+
|
8
|
+
def self.primary_key # AR
|
9
|
+
:id
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.columns # AR
|
13
|
+
[Column.new(primary_key, :attribute)] +
|
14
|
+
content_columns +
|
15
|
+
[:collection, :counter].map do |type|
|
16
|
+
__send__("#{type}s").map{|name| Column.new(name, type)}
|
17
|
+
end.flatten
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.column_names # AR
|
21
|
+
columns.map(&:name)
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.content_columns # AR
|
25
|
+
attributes.map{|name| Column.new(name, :attribute)}
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.columns_hash # AR
|
29
|
+
columns.inject({}){|h,c| h[c.name] = c; h}
|
30
|
+
end
|
31
|
+
|
3
32
|
######################################################################
|
4
33
|
### Validations
|
5
34
|
|
6
35
|
RecordNotSaved = Class.new(RuntimeError)
|
7
36
|
|
8
|
-
def self.create!(attributes)
|
37
|
+
def self.create!(attributes) # AR
|
9
38
|
object = new(attributes)
|
10
39
|
object.save!
|
11
40
|
object
|
12
41
|
end
|
13
42
|
|
14
|
-
def save!
|
43
|
+
def save! # AR
|
15
44
|
save || raise(RecordNotSaved)
|
16
45
|
end
|
17
46
|
|
18
47
|
######################################################################
|
19
48
|
### Accessor methods
|
20
49
|
|
21
|
-
def self.first(*args)
|
22
|
-
(args.empty? ? all : find(*args))
|
50
|
+
def self.first(*args) # AR
|
51
|
+
(args.empty? ? all : find(*args))[0]
|
23
52
|
end
|
24
53
|
|
25
|
-
def self.last(*args)
|
26
|
-
(args.empty? ? all : find(*args))
|
54
|
+
def self.last(*args) # AR
|
55
|
+
(args.empty? ? all : find(*args))[-1]
|
27
56
|
end
|
28
57
|
|
29
|
-
|
30
|
-
def self.count(*args)
|
58
|
+
def self.count(*args) # AR
|
31
59
|
(args.empty? ? all : find(*args)).size
|
32
60
|
end
|
61
|
+
|
62
|
+
######################################################################
|
63
|
+
### ActiveRecord class methods
|
64
|
+
|
65
|
+
def self.delete_all(cond = nil) # AR
|
66
|
+
if cond
|
67
|
+
raise NotImplementedError, "Sorry, conditional delete_all is not implemented yet"
|
68
|
+
else
|
69
|
+
all.each(&:delete)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
######################################################################
|
74
|
+
### ActiveRecord instance methods
|
75
|
+
|
76
|
+
def new_record? # AR
|
77
|
+
new?
|
78
|
+
end
|
79
|
+
|
80
|
+
######################################################################
|
81
|
+
### Overwrite attributes (and related methods)
|
82
|
+
|
83
|
+
def attributes # AR
|
84
|
+
hash = {}
|
85
|
+
self.class.attributes.each do |attr|
|
86
|
+
hash[attr] = __send__(attr)
|
87
|
+
end
|
88
|
+
self.class.collections.each do |attr|
|
89
|
+
hash[attr] = (begin __send__(attr).to_a; rescue MissingID; []; end)
|
90
|
+
end
|
91
|
+
self.class.counters.each do |attr|
|
92
|
+
hash[attr] = __send__(attr).to_i
|
93
|
+
end
|
94
|
+
return hash
|
95
|
+
end
|
96
|
+
|
97
|
+
def delete
|
98
|
+
delete_from_indices
|
99
|
+
delete_attributes(self.class.attributes)
|
100
|
+
delete_attributes(self.class.counters)
|
101
|
+
delete_attributes(self.class.collections)
|
102
|
+
delete_model_membership
|
103
|
+
self
|
104
|
+
end
|
105
|
+
|
106
|
+
def inspect
|
107
|
+
attrs = attributes.map{|(k,v)| "#{k}: #{v.inspect}"}.join(', ')
|
108
|
+
"#<#{self.class} id: #{new_record? ? "nil" : id}, #{attrs}>"
|
109
|
+
end
|
110
|
+
|
111
|
+
# Write attributes using SET
|
112
|
+
# This method will be removed once MSET becomes standard.
|
113
|
+
def write_with_set
|
114
|
+
self.class.attributes.each do |att|
|
115
|
+
value = send(att)
|
116
|
+
value.to_s.empty? ?
|
117
|
+
db.set(key(att), value) :
|
118
|
+
db.del(key(att))
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
# Write attributes using MSET
|
123
|
+
# This is the preferred method, and will be the only option
|
124
|
+
# available once MSET becomes standard.
|
125
|
+
def write_with_mset
|
126
|
+
unless self.class.attributes.empty?
|
127
|
+
rems, adds = self.class.attributes.map { |a| [key(a), send(a)] }.partition { |t| t.last.to_s.empty? }
|
128
|
+
db.del(*rems.flatten.compact) unless rems.empty?
|
129
|
+
db.mset(adds.flatten) unless adds.empty?
|
130
|
+
end
|
131
|
+
end
|
33
132
|
end
|
34
133
|
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), '/spec_helper')
|
2
|
+
|
3
|
+
describe Ohm::Model do
|
4
|
+
provide :attributes do
|
5
|
+
subject {
|
6
|
+
Video.create!(:url => "http://localhost/")
|
7
|
+
}
|
8
|
+
|
9
|
+
######################################################################
|
10
|
+
### Attribute
|
11
|
+
|
12
|
+
its(:attributes) { should be_kind_of(Hash) }
|
13
|
+
|
14
|
+
it "should contain given attribute" do
|
15
|
+
subject.attributes[:url].should == "http://localhost/"
|
16
|
+
end
|
17
|
+
|
18
|
+
######################################################################
|
19
|
+
### Counter
|
20
|
+
|
21
|
+
it "should contain given counter" do
|
22
|
+
subject.attributes[:viewed].should == 0
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should contain a valid counter value after incr and reload" do
|
26
|
+
subject.incr(:viewed)
|
27
|
+
subject.attributes[:viewed].should == 1
|
28
|
+
Video[subject.id].attributes[:viewed].should == 1
|
29
|
+
end
|
30
|
+
|
31
|
+
######################################################################
|
32
|
+
### Set
|
33
|
+
|
34
|
+
it "should contain given set as array" do
|
35
|
+
subject.tags.concat "foo"
|
36
|
+
subject.attributes[:tags].should be_kind_of(Array)
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should contain given set" do
|
40
|
+
subject.tags.concat "foo"
|
41
|
+
subject.attributes[:tags].should == ["foo"]
|
42
|
+
end
|
43
|
+
|
44
|
+
it "should contain given set as array" do
|
45
|
+
subject.tags.concat "foo"
|
46
|
+
subject.tags.concat "bar"
|
47
|
+
subject.attributes[:tags].should == ["foo", "bar"]
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should contain empty array when no set given" do
|
51
|
+
subject.attributes[:tags].should == []
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), '/spec_helper')
|
2
|
+
|
3
|
+
describe Ohm::Model do
|
4
|
+
######################################################################
|
5
|
+
### columns
|
6
|
+
|
7
|
+
it do
|
8
|
+
Video.should provide(:columns)
|
9
|
+
end
|
10
|
+
|
11
|
+
describe ".columns" do
|
12
|
+
it "should return an array of Column" do
|
13
|
+
Video.columns.map(&:class).should == [Ohm::Model::Column, Ohm::Model::Column, Ohm::Model::Column, Ohm::Model::Column]
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should contains column names" do
|
17
|
+
Video.columns.map(&:name).should == [:id, :url, :tags, :viewed]
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should contains column types" do
|
21
|
+
Video.columns.map(&:type).should == [:attribute, :attribute, :collection, :counter]
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
######################################################################
|
26
|
+
### column_names
|
27
|
+
|
28
|
+
it do
|
29
|
+
Video.should provide(:column_names)
|
30
|
+
end
|
31
|
+
|
32
|
+
describe ".column_names" do
|
33
|
+
it "should return an array of Symbol" do
|
34
|
+
Video.column_names.map(&:class).uniq.should == [Symbol]
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should return column names" do
|
38
|
+
Video.column_names.should == [:id, :url, :tags, :viewed]
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
######################################################################
|
43
|
+
### content_columns
|
44
|
+
|
45
|
+
it do
|
46
|
+
Video.should provide(:content_columns)
|
47
|
+
end
|
48
|
+
|
49
|
+
describe ".content_columns" do
|
50
|
+
it "should return an array of Column" do
|
51
|
+
Video.content_columns.map(&:class).uniq.should == [Ohm::Model::Column]
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should contains only attribute columns" do
|
55
|
+
Video.content_columns.map(&:type).uniq.should == [:attribute]
|
56
|
+
end
|
57
|
+
|
58
|
+
it "should not contains primary keys" do
|
59
|
+
Video.content_columns.map(&:name).should_not include(:id)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
######################################################################
|
64
|
+
### columns_hash
|
65
|
+
|
66
|
+
it do
|
67
|
+
Video.should provide(:columns_hash)
|
68
|
+
end
|
69
|
+
|
70
|
+
describe ".columns_hash" do
|
71
|
+
it "should return a hash" do
|
72
|
+
Video.columns_hash.should be_kind_of(Hash)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
data/spec/db/redis.pid
ADDED
File without changes
|
data/spec/its_helper.rb
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), '/spec_helper')
|
2
|
+
|
3
|
+
describe Ohm::Model do
|
4
|
+
######################################################################
|
5
|
+
### delete_all
|
6
|
+
|
7
|
+
it do
|
8
|
+
Ohm::Model.should provide(:delete_all)
|
9
|
+
end
|
10
|
+
|
11
|
+
describe ".delete_all" do
|
12
|
+
before do
|
13
|
+
Video.create!(:url=>"a")
|
14
|
+
@count = Video.count
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should delete all records" do
|
18
|
+
lambda { Video.delete_all }.should change(Video, :count).from(@count).to(0)
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should raise NotImplementedError when args are given" do
|
22
|
+
lambda { Video.delete_all(:some_cond) }.should raise_error(NotImplementedError)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
context "(url 'a' and 'b')" do
|
27
|
+
before do
|
28
|
+
Video.delete_all
|
29
|
+
Video.create!(:url=>"a")
|
30
|
+
Video.create!(:url=>"b")
|
31
|
+
end
|
32
|
+
|
33
|
+
######################################################################
|
34
|
+
### first
|
35
|
+
it do
|
36
|
+
Ohm::Model.should provide(:first)
|
37
|
+
end
|
38
|
+
|
39
|
+
describe ".first" do
|
40
|
+
it "should return an record" do
|
41
|
+
# TODO: how to know the orders
|
42
|
+
Video.first.url.should match(/\Aa|b\Z/)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
######################################################################
|
47
|
+
### all
|
48
|
+
|
49
|
+
it do
|
50
|
+
Ohm::Model.should provide(:all)
|
51
|
+
end
|
52
|
+
|
53
|
+
describe ".all" do
|
54
|
+
it "should return all records" do
|
55
|
+
Video.all.map(&:url).sort.should == ["a", "b"]
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
######################################################################
|
60
|
+
### last
|
61
|
+
|
62
|
+
it do
|
63
|
+
Ohm::Model.should provide(:last)
|
64
|
+
end
|
65
|
+
|
66
|
+
describe ".last" do
|
67
|
+
it "should return an record" do
|
68
|
+
# TODO: how to know the orders
|
69
|
+
Video.last.url.should match(/\Aa|b\Z/)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
######################################################################
|
75
|
+
### new_recoard?
|
76
|
+
|
77
|
+
provide :new_record? do
|
78
|
+
it "should return true for non saved record" do
|
79
|
+
Video.new.new_record?.should == true
|
80
|
+
end
|
81
|
+
|
82
|
+
it "should return false for existing record" do
|
83
|
+
Video.create!(:url=>"a").new_record?.should == false
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
data/spec/model.rb
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
######################################################################
|
2
|
+
### provide matcher
|
3
|
+
Spec::Matchers.define :provide do |expected|
|
4
|
+
match do |obj|
|
5
|
+
(obj.public_methods + obj.protected_methods + obj.private_methods).include?(expected.to_s)
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
module Spec
|
10
|
+
module Example
|
11
|
+
module Subject
|
12
|
+
module ExampleGroupMethods
|
13
|
+
# == Examples
|
14
|
+
#
|
15
|
+
# describe User do
|
16
|
+
# subject { User.new }
|
17
|
+
# provide :name
|
18
|
+
#
|
19
|
+
# [intead of]
|
20
|
+
#
|
21
|
+
# it "should provide #name" do
|
22
|
+
# methods = subject.public_methods + subject.protected_methods + subject.private_methods
|
23
|
+
# methods.should include("name")
|
24
|
+
# end
|
25
|
+
# end
|
26
|
+
#
|
27
|
+
def provide(name, &block)
|
28
|
+
it "should provide ##{name}" do
|
29
|
+
subject.should provide(name)
|
30
|
+
end
|
31
|
+
|
32
|
+
if block
|
33
|
+
describe("##{name}") do
|
34
|
+
define_method(name) do |*args|
|
35
|
+
subject.send(name, *args)
|
36
|
+
end
|
37
|
+
class_eval(&block)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
data/spec/redis.conf
ADDED
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
|
2
|
+
require 'spec'
|
3
|
+
require 'rr'
|
4
|
+
|
5
|
+
Spec::Runner.configure do |config|
|
6
|
+
config.mock_with RR::Adapters::Rspec
|
7
|
+
end
|
8
|
+
|
9
|
+
require 'ohm'
|
10
|
+
Ohm.connect(:port=>6380) # this port number should be defined in redis.conf
|
11
|
+
Dir.glob(File.join(File.dirname(__FILE__), '/../lib/*.rb')).each{|lib| require lib}
|
12
|
+
require File.join(File.dirname(__FILE__), '/its_helper')
|
13
|
+
require File.join(File.dirname(__FILE__), '/provide_helper')
|
14
|
+
require File.join(File.dirname(__FILE__), '/model')
|
15
|
+
|
16
|
+
def path(key)
|
17
|
+
Pathname(File.join(File.dirname(__FILE__) + "/fixtures/#{key}"))
|
18
|
+
end
|
19
|
+
|
20
|
+
def data(key)
|
21
|
+
(@__fixture_data_cache__ ||= {})[key] ||= path(key).read{}
|
22
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,12 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ohm-arfreaks
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 1
|
8
|
+
- 0
|
9
|
+
version: 0.1.0
|
5
10
|
platform: ruby
|
6
11
|
authors:
|
7
12
|
- maiha
|
@@ -9,7 +14,7 @@ autorequire:
|
|
9
14
|
bindir: bin
|
10
15
|
cert_chain: []
|
11
16
|
|
12
|
-
date: 2010-
|
17
|
+
date: 2010-03-17 00:00:00 +09:00
|
13
18
|
default_executable:
|
14
19
|
dependencies: []
|
15
20
|
|
@@ -27,6 +32,16 @@ files:
|
|
27
32
|
- README.markdown
|
28
33
|
- Rakefile
|
29
34
|
- lib/ohm-arfreaks.rb
|
35
|
+
- spec/db/redis.pid
|
36
|
+
- spec/attributes_spec.rb
|
37
|
+
- spec/redis.conf
|
38
|
+
- spec/provide_helper.rb
|
39
|
+
- spec/columns_spec.rb
|
40
|
+
- spec/model.rb
|
41
|
+
- spec/methods_spec.rb
|
42
|
+
- spec/its_helper.rb
|
43
|
+
- spec/spec_helper.rb
|
44
|
+
- spec/validations_spec.rb
|
30
45
|
has_rdoc: true
|
31
46
|
homepage: http://github.com/maiha/ohm-arfreaks
|
32
47
|
licenses: []
|
@@ -40,18 +55,20 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
40
55
|
requirements:
|
41
56
|
- - ">="
|
42
57
|
- !ruby/object:Gem::Version
|
58
|
+
segments:
|
59
|
+
- 0
|
43
60
|
version: "0"
|
44
|
-
version:
|
45
61
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
46
62
|
requirements:
|
47
63
|
- - ">="
|
48
64
|
- !ruby/object:Gem::Version
|
65
|
+
segments:
|
66
|
+
- 0
|
49
67
|
version: "0"
|
50
|
-
version:
|
51
68
|
requirements: []
|
52
69
|
|
53
70
|
rubyforge_project: asakusarb
|
54
|
-
rubygems_version: 1.3.
|
71
|
+
rubygems_version: 1.3.6
|
55
72
|
signing_key:
|
56
73
|
specification_version: 3
|
57
74
|
summary: Ohm::Model extensions for AR freaks
|