og 0.25.0 → 0.26.0
Sign up to get free protection for your applications and to get access to all the features.
- data/ProjectInfo +2 -2
- data/README +1 -1
- data/doc/RELEASES +78 -0
- data/lib/glue/revisable.rb +123 -1
- data/lib/glue/searchable.rb +28 -0
- data/lib/glue/taggable.rb +18 -3
- data/lib/glue/timestamped.rb +5 -5
- data/lib/og.rb +28 -4
- data/lib/og/collection.rb +15 -0
- data/lib/og/entity.rb +64 -44
- data/lib/og/evolution.rb +1 -1
- data/lib/og/manager.rb +16 -0
- data/lib/og/relation.rb +5 -1
- data/lib/og/relation/refers_to.rb +6 -0
- data/lib/og/store.rb +13 -0
- data/lib/og/store/kirby.rb +6 -5
- data/lib/og/store/mysql.rb +34 -5
- data/lib/og/store/psql.rb +409 -82
- data/lib/og/store/sql.rb +27 -11
- data/lib/og/store/sqlite.rb +3 -3
- data/lib/og/validation.rb +53 -35
- data/test/glue/tc_revisable.rb +62 -0
- data/test/og/CONFIG.rb +2 -2
- data/test/og/store/tc_kirby.rb +31 -0
- data/test/og/tc_finder.rb +20 -0
- data/test/og/tc_validation.rb +53 -0
- metadata +9 -6
- data/lib/glue/taggable_old.rb +0 -228
- data/lib/og/vendor/kbserver.rb +0 -20
- data/lib/og/vendor/kirbybase.rb +0 -2941
data/ProjectInfo
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
TITLE : &title Og
|
4
4
|
NAME : &pkg og
|
5
|
-
VERSION : '0.
|
5
|
+
VERSION : '0.26.0'
|
6
6
|
STATUS : beta
|
7
7
|
|
8
8
|
AUTHOR : George Moschovitis
|
@@ -17,7 +17,7 @@ DESCRIPTION: >
|
|
17
17
|
KirbyBase, Filesystem and more.
|
18
18
|
|
19
19
|
DEPENDENCIES:
|
20
|
-
- [ glue, '= 0.
|
20
|
+
- [ glue, '= 0.26.0' ]
|
21
21
|
|
22
22
|
DISTRIBUTE: [ gem, tgz, zip ]
|
23
23
|
|
data/README
CHANGED
data/doc/RELEASES
CHANGED
@@ -1,3 +1,81 @@
|
|
1
|
+
== Version 0.26.0
|
2
|
+
|
3
|
+
This is the release with the most community contributions. Check
|
4
|
+
out the great new stuff. Download now!
|
5
|
+
|
6
|
+
Most notable changes:
|
7
|
+
|
8
|
+
* New CacheSweeper mixin. Using this mixin allows you to keep
|
9
|
+
the cache cleaning logic in one place. This logic is called
|
10
|
+
automagically by many default Nitro/Og methods (for example
|
11
|
+
Og insert/update, scaffolding, etc). You can fully customize
|
12
|
+
the behaviour.
|
13
|
+
|
14
|
+
class Article
|
15
|
+
include CacheSweeper
|
16
|
+
|
17
|
+
def expire_affected(action = :all)
|
18
|
+
expire_affected_output('articles/view')
|
19
|
+
...
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
a = Article[1]
|
24
|
+
a.title = 'New'
|
25
|
+
a.save # => calls expire_affected.
|
26
|
+
|
27
|
+
* Searchable mixin. Include this mixin to your classes to make
|
28
|
+
them searchable by the auto administration system.
|
29
|
+
|
30
|
+
* Better validations implementation. Cleaner code, less evals,
|
31
|
+
more flexible and easier to extend.
|
32
|
+
|
33
|
+
* New scaffolding / auto administration system. The implementation
|
34
|
+
is much cleaner and easier to customize. It leverages the latest
|
35
|
+
advancements (dispatcher, sweeper, etc) and adds search support,
|
36
|
+
pager, breadcrumps and more. You can define your own controls
|
37
|
+
to handle properties and relations. Stay tuned for more stuff
|
38
|
+
in the near future.
|
39
|
+
|
40
|
+
* New Og revisable mixin. Just include this mixin in your classes
|
41
|
+
and get db backed revision support for free. Here comes an
|
42
|
+
example:
|
43
|
+
|
44
|
+
class Article
|
45
|
+
is Revisable
|
46
|
+
property :body, String, :revisable => true
|
47
|
+
property :title, String
|
48
|
+
end
|
49
|
+
|
50
|
+
Automatically generates the Revision class (and the
|
51
|
+
backend schema):
|
52
|
+
|
53
|
+
class Article::Revision
|
54
|
+
|
55
|
+
article.revisions
|
56
|
+
|
57
|
+
article.revise do |a|
|
58
|
+
a.title = 'hello'
|
59
|
+
a.body = 'world'
|
60
|
+
end
|
61
|
+
|
62
|
+
article.rollback(4)
|
63
|
+
|
64
|
+
* Bug fixed KirbyBase Og adapter. This works great with the
|
65
|
+
new 2.5 gem.
|
66
|
+
|
67
|
+
* Added more rational defaults, and many predefined options to
|
68
|
+
minimize the amount of setup needed to get your app running. Of
|
69
|
+
course you can still customize just about everything in Nitro.
|
70
|
+
|
71
|
+
* Improvements to PostgreSQL automatic generation of foreign key
|
72
|
+
constraints.
|
73
|
+
|
74
|
+
* Added evolution support to the MySql store.
|
75
|
+
|
76
|
+
* Many, many, many bug fixes and smaller improvements.
|
77
|
+
|
78
|
+
|
1
79
|
== Version 0.25.0
|
2
80
|
|
3
81
|
This is the first in a series of releases focused on stability
|
data/lib/glue/revisable.rb
CHANGED
@@ -1,2 +1,124 @@
|
|
1
|
+
module Glue
|
1
2
|
|
2
|
-
|
3
|
+
# Revision support for Og-managed classes.
|
4
|
+
#
|
5
|
+
# class Article
|
6
|
+
# is Revisable
|
7
|
+
# property :body, String, :revisable => true
|
8
|
+
# property :title, String
|
9
|
+
# end
|
10
|
+
#
|
11
|
+
# Generates the Revision class:
|
12
|
+
#
|
13
|
+
# class Article::Revision
|
14
|
+
#
|
15
|
+
# article.revisions
|
16
|
+
#
|
17
|
+
# article.revise do |a|
|
18
|
+
# a.title = 'hello'
|
19
|
+
# a.body = 'world'
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
# article.rollback(4)
|
23
|
+
|
24
|
+
module Revisable
|
25
|
+
# The revision of the revisable object.
|
26
|
+
|
27
|
+
property :revision, Fixnum
|
28
|
+
|
29
|
+
# This mixin is injected into the dynamically generated
|
30
|
+
# Revision class. You can customize this in your application
|
31
|
+
# to store extra fields per revision.
|
32
|
+
|
33
|
+
module Mixin
|
34
|
+
# The create time for the revision.
|
35
|
+
|
36
|
+
property :create_time, Time
|
37
|
+
|
38
|
+
# Override to handle your options.
|
39
|
+
|
40
|
+
def initialize(obj, options = {})
|
41
|
+
revision_from(obj)
|
42
|
+
@create_time = Time.now
|
43
|
+
end
|
44
|
+
|
45
|
+
def revision_from(obj)
|
46
|
+
for prop in obj.class.properties.values
|
47
|
+
# gmosx, FIXME: test against primary key, not oid.
|
48
|
+
unless prop.symbol == :oid
|
49
|
+
instance_variable_set "@#{prop}", obj.send(prop.to_s)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def revision_to(obj)
|
55
|
+
for prop in obj.class.properties.values
|
56
|
+
# gmosx, FIXME: test against primary key, not oid.
|
57
|
+
unless prop.symbol == :oid
|
58
|
+
obj.instance_variable_set "@#{prop}", self.send(prop.to_s)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
alias_method :apply_to, :revision_to
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.included(base)
|
66
|
+
super
|
67
|
+
|
68
|
+
base.module_eval %{
|
69
|
+
class Revision < #{base}
|
70
|
+
include Revisable::Mixin
|
71
|
+
belongs_to #{base}
|
72
|
+
end
|
73
|
+
}
|
74
|
+
|
75
|
+
base.has_many :revisions, base::Revision
|
76
|
+
end
|
77
|
+
|
78
|
+
# Can accept options like owner or a comment.
|
79
|
+
# You can specialize this in your app.
|
80
|
+
|
81
|
+
def revise(options = {})
|
82
|
+
if self.revision.nil?
|
83
|
+
self.revision = 1
|
84
|
+
else
|
85
|
+
self.revision += 1
|
86
|
+
end
|
87
|
+
self.revisions << self.class::Revision.new(self, options)
|
88
|
+
yield(self) if block_given?
|
89
|
+
self.save
|
90
|
+
end
|
91
|
+
alias_method :revise!, :revise
|
92
|
+
|
93
|
+
# Rollback to an older revision.
|
94
|
+
|
95
|
+
def rollback(rev, options = {})
|
96
|
+
self.revise(options) { |obj| get_revision(rev).apply_to(obj) }
|
97
|
+
end
|
98
|
+
|
99
|
+
# Return a revision.
|
100
|
+
|
101
|
+
def get_revision(rev)
|
102
|
+
return self if rev.to_i == self.revision
|
103
|
+
self.revisions.find_one(:condition => "revision=#{rev}")
|
104
|
+
end
|
105
|
+
|
106
|
+
# Return the last revision.
|
107
|
+
|
108
|
+
def last_revision
|
109
|
+
self.revisions(:order => 'revision DESC', :limit => 1).first
|
110
|
+
end
|
111
|
+
|
112
|
+
# The number of revisions.
|
113
|
+
|
114
|
+
def revision_count
|
115
|
+
self.revisions.count
|
116
|
+
end
|
117
|
+
alias_method :revisions_count, :revision_count
|
118
|
+
|
119
|
+
end
|
120
|
+
|
121
|
+
end
|
122
|
+
|
123
|
+
# * George Moschovitis <gm@navel.gr>
|
124
|
+
# * Dirk Barnikel <dirk.barnikel@gmx.de>
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Glue
|
2
|
+
|
3
|
+
# Search support for Og managed classes.
|
4
|
+
|
5
|
+
module Searchable
|
6
|
+
|
7
|
+
def self.included(base)
|
8
|
+
base.extend(ClassMethods)
|
9
|
+
end
|
10
|
+
|
11
|
+
module ClassMethods
|
12
|
+
|
13
|
+
# Override this method in your class to customize the
|
14
|
+
# search. This is a nice default method.
|
15
|
+
|
16
|
+
def search(query)
|
17
|
+
search_props = properties.values.select { |p| p.searchable }
|
18
|
+
condition = search_props.collect { |p| "#{p} LIKE '%#{query}%'" }.join(' OR ')
|
19
|
+
all(:condition => condition)
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
# * George Moschovitis <gm@navel.gr>
|
data/lib/glue/taggable.rb
CHANGED
@@ -41,6 +41,10 @@ class Tag
|
|
41
41
|
def self.total_frequency(tags = Tag.all)
|
42
42
|
tags.inject(1) { |total, t| total += t.count }
|
43
43
|
end
|
44
|
+
|
45
|
+
def to_s
|
46
|
+
@name
|
47
|
+
end
|
44
48
|
end
|
45
49
|
|
46
50
|
module Glue
|
@@ -65,7 +69,12 @@ module Glue
|
|
65
69
|
# Tag.find_by_name('ruby').articles
|
66
70
|
|
67
71
|
module Taggable
|
72
|
+
# The tag string separator.
|
73
|
+
|
74
|
+
setting :separator, :default => ' ', :doc => 'The tag string separator'
|
75
|
+
|
68
76
|
include Og::EntityMixin
|
77
|
+
|
69
78
|
many_to_many Tag
|
70
79
|
|
71
80
|
# Add a tag for this object.
|
@@ -78,7 +87,7 @@ module Taggable
|
|
78
87
|
self.tags.clear if options[:clear]
|
79
88
|
|
80
89
|
for name in Taggable.tags_to_names(the_tags)
|
81
|
-
Tag.find_or_create_by_name(name).tag(self)
|
90
|
+
Tag.find_or_create_by_name(name).tag(self) unless self.tagged_with?(name)
|
82
91
|
end
|
83
92
|
end
|
84
93
|
alias_method :tag!, :tag
|
@@ -89,6 +98,12 @@ module Taggable
|
|
89
98
|
tags.collect { |t| t.name }
|
90
99
|
end
|
91
100
|
|
101
|
+
# Return the tag string
|
102
|
+
|
103
|
+
def tag_string(separator = Taggable.separator)
|
104
|
+
tags.collect { |t| t.name }.join(separator)
|
105
|
+
end
|
106
|
+
|
92
107
|
# Checks to see if this object has been tagged
|
93
108
|
# with +tag_name+.
|
94
109
|
|
@@ -125,7 +140,7 @@ module Taggable
|
|
125
140
|
# UNION (OR)
|
126
141
|
|
127
142
|
def find_with_any_tag(*names)
|
128
|
-
info = ogmanager.store.join_table_info(self,
|
143
|
+
info = ogmanager.store.join_table_info(self, Tag)
|
129
144
|
count = names.size
|
130
145
|
names = names.map { |n| "'#{n}'" }.join(',')
|
131
146
|
sql = %{
|
@@ -158,7 +173,7 @@ module Taggable
|
|
158
173
|
|
159
174
|
# Helper.
|
160
175
|
|
161
|
-
def self.tags_to_names(the_tags, separator =
|
176
|
+
def self.tags_to_names(the_tags, separator = Taggable.separator)
|
162
177
|
if the_tags.is_a? Array
|
163
178
|
names = the_tags
|
164
179
|
elsif the_tags.is_a? String
|
data/lib/glue/timestamped.rb
CHANGED
@@ -9,9 +9,9 @@ module Glue
|
|
9
9
|
module Timestamped
|
10
10
|
include Aspects
|
11
11
|
|
12
|
-
property :create_time, Time, :
|
13
|
-
property :update_time, Time, :
|
14
|
-
property :access_time, Time, :
|
12
|
+
property :create_time, Time, :control => :none
|
13
|
+
property :update_time, Time, :control => :none
|
14
|
+
property :access_time, Time, :control => :none
|
15
15
|
|
16
16
|
before "@create_time = @update_time = Time.now", :on => :og_insert
|
17
17
|
before "@update_time = Time.now", :on => :og_update
|
@@ -28,8 +28,8 @@ end
|
|
28
28
|
|
29
29
|
module TimestampedOnCreate
|
30
30
|
include Aspects
|
31
|
-
property :create_time, Time, :
|
32
|
-
before "@create_time =
|
31
|
+
property :create_time, Time, :control => :none
|
32
|
+
before "@create_time = Time.now", :on => :og_insert
|
33
33
|
end
|
34
34
|
|
35
35
|
end
|
data/lib/og.rb
CHANGED
@@ -43,7 +43,7 @@ module Og
|
|
43
43
|
|
44
44
|
# The version.
|
45
45
|
|
46
|
-
Version = '0.
|
46
|
+
Version = '0.26.0'
|
47
47
|
|
48
48
|
# Library path.
|
49
49
|
|
@@ -110,8 +110,32 @@ module Og
|
|
110
110
|
# as the default store.
|
111
111
|
|
112
112
|
def setup(options = {:store => :sqlite})
|
113
|
-
|
114
|
-
|
113
|
+
begin
|
114
|
+
# This is a flag a store or manager can use to determine
|
115
|
+
# if it was being called by Og.setup to provide
|
116
|
+
# additional, faster or enhanced functionality.
|
117
|
+
|
118
|
+
puts options[:called_by_og_setup]
|
119
|
+
options[:called_by_og_setup] = true if options[:called_by_og_setup].nil?
|
120
|
+
|
121
|
+
m = @@manager = Manager.new(options)
|
122
|
+
m.manage_classes
|
123
|
+
|
124
|
+
# Allows functionality that requires a store is finalized
|
125
|
+
# to be implemented. A vastly superior method of constructing
|
126
|
+
# foreign key constraints is an example of functionality
|
127
|
+
# this provides. Currently only used by the PostgreSQL store.
|
128
|
+
|
129
|
+
m.post_setup if options[:called_by_og_setup]
|
130
|
+
rescue Exception => ex
|
131
|
+
Logger.error "Og.setup had problems: #{ex.class} => #{ex.message}"
|
132
|
+
if $DBG
|
133
|
+
Logger.error ex.inspect
|
134
|
+
Logger.error ex.backtrace.join("\n")
|
135
|
+
exit
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
115
139
|
return m
|
116
140
|
end
|
117
141
|
alias_method :connect, :setup
|
@@ -138,4 +162,4 @@ require 'og/types'
|
|
138
162
|
require 'og/validation'
|
139
163
|
require 'og/markers'
|
140
164
|
|
141
|
-
# * George Moschovitis <gm@navel.gr>
|
165
|
+
# * George Moschovitis <gm@navel.gr>
|
data/lib/og/collection.rb
CHANGED
@@ -113,8 +113,11 @@ class Collection
|
|
113
113
|
end
|
114
114
|
|
115
115
|
# Add a new member to the collection.
|
116
|
+
# this method will overwrite any objects already
|
117
|
+
# existing in the collection
|
116
118
|
|
117
119
|
def push(obj, options = nil)
|
120
|
+
remove(obj) if members.include?(obj)
|
118
121
|
@members.push(obj)
|
119
122
|
unless @building or owner.unsaved?
|
120
123
|
@owner.send(@insert_proc, obj, options)
|
@@ -125,6 +128,9 @@ class Collection
|
|
125
128
|
|
126
129
|
# Remove a member from the collection, the actual object
|
127
130
|
# is not deleted.
|
131
|
+
#--
|
132
|
+
# TODO: add remove by oid!
|
133
|
+
#++
|
128
134
|
|
129
135
|
def remove(*objects)
|
130
136
|
objects = objects.flatten
|
@@ -141,6 +147,9 @@ class Collection
|
|
141
147
|
end
|
142
148
|
|
143
149
|
# Delete a member from the collection AND the store.
|
150
|
+
#--
|
151
|
+
# TODO: add delete by oid!
|
152
|
+
#++
|
144
153
|
|
145
154
|
def delete(*objects)
|
146
155
|
objects = objects.flatten
|
@@ -213,6 +222,12 @@ class Collection
|
|
213
222
|
end
|
214
223
|
end
|
215
224
|
|
225
|
+
# Find one object.
|
226
|
+
|
227
|
+
def find_one(options = {})
|
228
|
+
find(options).first
|
229
|
+
end
|
230
|
+
|
216
231
|
# Redirect all other methods to the members array.
|
217
232
|
|
218
233
|
def method_missing(symbol, *args, &block)
|
data/lib/og/entity.rb
CHANGED
@@ -12,9 +12,13 @@ module EntityMixin
|
|
12
12
|
|
13
13
|
def save(options = nil)
|
14
14
|
self.class.ogmanager.store.save(self, options)
|
15
|
-
# return self
|
16
15
|
end
|
17
16
|
alias_method :save!, :save
|
17
|
+
alias_method :validate_and_save, :save
|
18
|
+
|
19
|
+
def force_save!(options = nil)
|
20
|
+
self.class.ogmanager.store.force_save(self, options)
|
21
|
+
end
|
18
22
|
|
19
23
|
def insert
|
20
24
|
self.class.ogmanager.store.insert(self)
|
@@ -70,6 +74,16 @@ module EntityMixin
|
|
70
74
|
end
|
71
75
|
alias_method :assign, :assign_properties
|
72
76
|
|
77
|
+
# Returns a symbol => value hash of the object's
|
78
|
+
# properties.
|
79
|
+
|
80
|
+
def properties_to_hash
|
81
|
+
hash = {}
|
82
|
+
for sym, prop in self.class.properties
|
83
|
+
hash[sym] = instance_variable_get("@#{sym}")
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
73
87
|
include RelationDSL
|
74
88
|
|
75
89
|
class_inherit do
|
@@ -106,10 +120,16 @@ module EntityMixin
|
|
106
120
|
alias_method :[], :load
|
107
121
|
alias_method :exist?, :load
|
108
122
|
|
123
|
+
# Update the representation of this class in the
|
124
|
+
# store.
|
125
|
+
|
109
126
|
def update(set, options = nil)
|
110
127
|
ogmanager.store.update_by_sql(self, set, options)
|
111
128
|
end
|
112
129
|
|
130
|
+
# Find a specific instance of this class according
|
131
|
+
# to the given conditions.
|
132
|
+
|
113
133
|
def find(options = {})
|
114
134
|
if find_options = self.ann.self[:find_options]
|
115
135
|
options = find_options.dup.update(options)
|
@@ -122,6 +142,8 @@ module EntityMixin
|
|
122
142
|
end
|
123
143
|
alias_method :all, :find
|
124
144
|
|
145
|
+
# Find a single instance of this class.
|
146
|
+
|
125
147
|
def find_one(options = {})
|
126
148
|
options[:class] = self
|
127
149
|
ogmanager.store.find_one(options)
|
@@ -137,6 +159,8 @@ module EntityMixin
|
|
137
159
|
ogmanager.store.select_one(sql, self)
|
138
160
|
end
|
139
161
|
|
162
|
+
# Perform a count query.
|
163
|
+
|
140
164
|
def count(options = {})
|
141
165
|
options[:class] = self
|
142
166
|
ogmanager.store.count(options)
|
@@ -177,8 +201,13 @@ module EntityMixin
|
|
177
201
|
ogmanager.store
|
178
202
|
end
|
179
203
|
|
204
|
+
# Returns the primary key for this class.
|
205
|
+
|
180
206
|
def primary_key
|
181
|
-
|
207
|
+
# gmosx: LEAVE as is, seems to be a workaround for a
|
208
|
+
# nasty bug in the current facets version.
|
209
|
+
pk = ann.self.primary_key
|
210
|
+
if pk.nil?
|
182
211
|
pk = Entity.resolve_primary_key(self)
|
183
212
|
ann :self, :primary_key => pk
|
184
213
|
end
|
@@ -246,7 +275,8 @@ module EntityMixin
|
|
246
275
|
# Set the primary key.
|
247
276
|
|
248
277
|
def set_primary_key(pk, pkclass = Fixnum)
|
249
|
-
ann self, :primary_key => Property.new(:symbol => pk, :klass => pkclass)
|
278
|
+
#ann self, :primary_key => Property.new(:symbol => pk, :klass => pkclass)
|
279
|
+
ann self, :primary_key => properties[pk].dup
|
250
280
|
end
|
251
281
|
|
252
282
|
# Is this entity a polymorphic parent?
|
@@ -323,52 +353,18 @@ module EntityMixin
|
|
323
353
|
|
324
354
|
def method_missing(sym, *args)
|
325
355
|
if match = /find_(all_by|by)_([_a-zA-Z]\w*)/.match(sym.to_s)
|
326
|
-
finder
|
327
|
-
attrs = match.captures.last.split('_and_')
|
328
|
-
|
329
|
-
if args.last.is_a?(Hash)
|
330
|
-
options = args.pop
|
331
|
-
else
|
332
|
-
options = {}
|
333
|
-
end
|
334
|
-
|
335
|
-
condition = []
|
336
|
-
attrs.each_with_index do |a, idx|
|
337
|
-
condition << %|#{a} #{options.delete("#{a}_op".to_sym) || '='} #{ogmanager.store.quote(args[idx])}|
|
338
|
-
end
|
339
|
-
|
340
|
-
options.update(
|
341
|
-
:class => self,
|
342
|
-
:condition => condition.join(' AND ')
|
343
|
-
)
|
344
|
-
|
345
|
-
return ogmanager.store.send(finder, options)
|
356
|
+
return finder(match, args)
|
346
357
|
elsif match = /find_or_create_by_([_a-zA-Z]\w*)/.match(sym.to_s)
|
347
|
-
|
348
|
-
attrs = match.captures.last.split('_and_')
|
349
|
-
|
350
|
-
if args.last.is_a?(Hash)
|
351
|
-
options = args.pop
|
352
|
-
else
|
353
|
-
options = {}
|
354
|
-
end
|
358
|
+
obj = finder(match, args)
|
355
359
|
|
356
|
-
|
357
|
-
|
358
|
-
condition << %|#{a} #{options.delete("#{a}_op".to_sym) || '='} #{ogmanager.store.quote(args[idx])}|
|
359
|
-
end
|
360
|
-
|
361
|
-
options.update(
|
362
|
-
:class => self,
|
363
|
-
:condition => condition.join(' AND ')
|
364
|
-
)
|
365
|
-
|
366
|
-
unless obj = ogmanager.store.send(finder, options)
|
360
|
+
unless obj
|
361
|
+
attrs = match.captures.last.split('_and_')
|
367
362
|
obj = self.create do |obj|
|
368
|
-
attrs.
|
369
|
-
obj.instance_variable_set "@#{
|
363
|
+
attrs.zip(args).map do |name, value|
|
364
|
+
obj.instance_variable_set "@#{name}", value
|
370
365
|
end
|
371
366
|
end
|
367
|
+
yield(obj) if block_given?
|
372
368
|
end
|
373
369
|
|
374
370
|
return obj
|
@@ -376,6 +372,30 @@ module EntityMixin
|
|
376
372
|
super
|
377
373
|
end
|
378
374
|
end
|
375
|
+
|
376
|
+
private
|
377
|
+
|
378
|
+
# Helper method for dynamic finders. Finds the object dynamically parsed
|
379
|
+
# method name is after.
|
380
|
+
|
381
|
+
def finder(match, args)
|
382
|
+
finder = (match.captures.first == 'all_by' ? :find : :find_one)
|
383
|
+
attrs = match.captures.last.split('_and_')
|
384
|
+
|
385
|
+
options = {}
|
386
|
+
options = args.pop if args.last.is_a?(Hash)
|
387
|
+
|
388
|
+
condition = attrs.zip(args).map do |name, value|
|
389
|
+
%|#{name} #{options.delete("#{name}_op".to_sym) || '='} #{ogmanager.store.quote(value)}|
|
390
|
+
end.join(' AND ')
|
391
|
+
|
392
|
+
options.update(
|
393
|
+
:class => self,
|
394
|
+
:condition => condition
|
395
|
+
)
|
396
|
+
|
397
|
+
return ogmanager.store.send(finder, options)
|
398
|
+
end
|
379
399
|
|
380
400
|
end
|
381
401
|
|