ohm-arfreaks 0.0.1 → 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/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
|