og 0.25.0 → 0.26.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/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
|
|