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 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 Page < Ohm::Model
23
+ class Video < Ohm::Model
24
24
  attribute :url
25
+ set :tags
25
26
  end
26
27
 
27
- Page.first
28
- # => #<Page:1 url="http://...">
28
+ Video.first
29
+ => #<Video id: 470, url: "a", tags: []>
29
30
 
30
- Page.create!(:url=>...)
31
+ Video.create!(:url=>...)
32
+ Video.new.save!
31
33
 
32
- Page.new.save!
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.1"
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)).first
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)).last
54
+ def self.last(*args) # AR
55
+ (args.empty? ? all : find(*args))[-1]
27
56
  end
28
57
 
29
- # TODO: should use native 'count' method for performance reason
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
@@ -0,0 +1,15 @@
1
+ module Spec
2
+ module Example
3
+ module Subject
4
+ module ExampleGroupMethods
5
+ def its(*args, &block)
6
+ describe(args.first) do
7
+ define_method(:subject) { super().send(*args) }
8
+ it(&block)
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+
@@ -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,6 @@
1
+
2
+ class Video < Ohm::Model
3
+ attribute :url
4
+ counter :viewed
5
+ set :tags
6
+ end
@@ -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
@@ -0,0 +1,13 @@
1
+ dir ./spec/db
2
+ pidfile ./spec/db/redis.pid
3
+ daemonize yes
4
+ port 6380
5
+ timeout 300
6
+
7
+ save 900 10000
8
+
9
+ loglevel warning
10
+
11
+ logfile stdout
12
+
13
+ databases 16
@@ -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
@@ -0,0 +1,9 @@
1
+ require File.join(File.dirname(__FILE__), '/spec_helper')
2
+
3
+ describe Video do
4
+ it do
5
+ Video.should provide(:create!)
6
+ end
7
+
8
+ provide :save!
9
+ 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
- version: 0.0.1
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-02-05 00:00:00 +09:00
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.5
71
+ rubygems_version: 1.3.6
55
72
  signing_key:
56
73
  specification_version: 3
57
74
  summary: Ohm::Model extensions for AR freaks