perry 0.4.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.rdoc +38 -0
- data/Rakefile +70 -0
- data/lib/perry.rb +48 -0
- data/lib/perry/adapters.rb +5 -0
- data/lib/perry/adapters/abstract_adapter.rb +92 -0
- data/lib/perry/adapters/bertrpc_adapter.rb +32 -0
- data/lib/perry/adapters/restful_http_adapter.rb +99 -0
- data/lib/perry/association.rb +330 -0
- data/lib/perry/association_preload.rb +69 -0
- data/lib/perry/associations/common.rb +51 -0
- data/lib/perry/associations/contains.rb +64 -0
- data/lib/perry/associations/external.rb +67 -0
- data/lib/perry/base.rb +204 -0
- data/lib/perry/cacheable.rb +80 -0
- data/lib/perry/cacheable/entry.rb +18 -0
- data/lib/perry/cacheable/store.rb +45 -0
- data/lib/perry/core_ext/kernel/singleton_class.rb +14 -0
- data/lib/perry/errors.rb +49 -0
- data/lib/perry/logger.rb +54 -0
- data/lib/perry/persistence.rb +55 -0
- data/lib/perry/relation.rb +153 -0
- data/lib/perry/relation/finder_methods.rb +56 -0
- data/lib/perry/relation/query_methods.rb +69 -0
- data/lib/perry/scopes.rb +45 -0
- data/lib/perry/scopes/conditions.rb +101 -0
- data/lib/perry/serialization.rb +48 -0
- data/lib/perry/version.rb +13 -0
- metadata +187 -0
@@ -0,0 +1,56 @@
|
|
1
|
+
module Perry::FinderMethods
|
2
|
+
|
3
|
+
def find(ids_or_mode, options={})
|
4
|
+
case ids_or_mode
|
5
|
+
when Fixnum, String
|
6
|
+
self.where(:id => ids_or_mode.to_i).first(options) || raise(Perry::RecordNotFound, "Could not find #{@klass} with :id = #{ids_or_mode}")
|
7
|
+
when Array
|
8
|
+
self.where(:id => ids_or_mode).all(options).tap do |result|
|
9
|
+
raise Perry::RecordNotFound, "Couldn't find all #{@klass} with ids (#{ids_or_mode.join(',')}) (expected #{ids_or_mode.size} records but got #{result.size})." unless result.size == ids_or_mode.size
|
10
|
+
end
|
11
|
+
when :all
|
12
|
+
self.all(options)
|
13
|
+
when :first
|
14
|
+
self.first(options)
|
15
|
+
else
|
16
|
+
raise ArgumentError, "Unknown arguments for method find"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def all(options={})
|
21
|
+
self.apply_finder_options(options).to_a
|
22
|
+
end
|
23
|
+
|
24
|
+
def first(options={})
|
25
|
+
self.apply_finder_options(options).limit(1).to_a.first
|
26
|
+
end
|
27
|
+
|
28
|
+
def search(options={})
|
29
|
+
relation = self
|
30
|
+
options.each do |search, *args|
|
31
|
+
relation = relation.send(search, *args) if @klass.send(:condition_details, search)
|
32
|
+
end
|
33
|
+
relation
|
34
|
+
end
|
35
|
+
|
36
|
+
def apply_finder_options(options)
|
37
|
+
relation = clone
|
38
|
+
return relation unless options
|
39
|
+
|
40
|
+
[:joins, :limit, :offset, :order, :select, :group, :having, :from, :fresh, :includes].each do |finder|
|
41
|
+
relation = relation.send(finder, options[finder]) if options[finder]
|
42
|
+
end
|
43
|
+
|
44
|
+
relation = relation.where(options[:conditions]) if options.has_key?(:conditions)
|
45
|
+
relation = relation.where(options[:where]) if options.has_key?(:where)
|
46
|
+
|
47
|
+
relation = relation.includes(options[:include]) if options.has_key?(:include)
|
48
|
+
|
49
|
+
relation = relation.search(options[:search]) if options.has_key?(:search)
|
50
|
+
|
51
|
+
relation = relation.sql(options[:sql]) if options.has_key?(:sql)
|
52
|
+
|
53
|
+
relation
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
module Perry::QueryMethods
|
2
|
+
# TRP: Define each of the variables the query options will be stored in.
|
3
|
+
attr_accessor :select_values, :group_values, :order_values, :joins_values, :includes_values, :where_values, :having_values,
|
4
|
+
:limit_value, :offset_value, :from_value,
|
5
|
+
:raw_sql_value, :fresh_value
|
6
|
+
|
7
|
+
def select(*args)
|
8
|
+
if block_given?
|
9
|
+
to_a.select {|*block_args| yield(*block_args) }
|
10
|
+
else
|
11
|
+
clone.tap { |r| r.select_values += args if args_valid? args }
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def group(*args)
|
16
|
+
clone.tap { |r| r.group_values += args if args_valid? args }
|
17
|
+
end
|
18
|
+
|
19
|
+
def order(*args)
|
20
|
+
clone.tap { |r| r.order_values += args if args_valid? args }
|
21
|
+
end
|
22
|
+
|
23
|
+
def joins(*args)
|
24
|
+
clone.tap { |r| r.joins_values += args if args_valid?(args) }
|
25
|
+
end
|
26
|
+
|
27
|
+
def includes(*args)
|
28
|
+
args.reject! { |a| a.nil? }
|
29
|
+
clone.tap { |r| r.includes_values += (r.includes_values + args).flatten.uniq if args_valid? args }
|
30
|
+
end
|
31
|
+
|
32
|
+
def where(*args)
|
33
|
+
clone.tap { |r| r.where_values += args.compact.select { |i| args_valid? i } if args_valid? args }
|
34
|
+
end
|
35
|
+
|
36
|
+
def having(*args)
|
37
|
+
clone.tap { |r| r.having_values += args if args_valid? args }
|
38
|
+
end
|
39
|
+
|
40
|
+
def limit(value = true)
|
41
|
+
clone.tap { |r| r.limit_value = value }
|
42
|
+
end
|
43
|
+
|
44
|
+
def offset(value = true)
|
45
|
+
clone.tap { |r| r.offset_value = value }
|
46
|
+
end
|
47
|
+
|
48
|
+
def from(table)
|
49
|
+
clone.tap { |r| r.from_value = table }
|
50
|
+
end
|
51
|
+
|
52
|
+
def fresh(val=true)
|
53
|
+
clone.tap do |r|
|
54
|
+
r.fresh_value = val
|
55
|
+
r.reset_queries if r.fresh_value
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def sql(raw_sql)
|
60
|
+
clone.tap { |r| r.raw_sql_value = raw_sql }
|
61
|
+
end
|
62
|
+
|
63
|
+
protected
|
64
|
+
|
65
|
+
def args_valid?(args)
|
66
|
+
args.respond_to?(:empty?) ? !args.empty? : !!args
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
data/lib/perry/scopes.rb
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'perry/scopes/conditions'
|
2
|
+
|
3
|
+
module Perry::Scopes
|
4
|
+
|
5
|
+
module ClassMethods
|
6
|
+
|
7
|
+
def scopes
|
8
|
+
read_inheritable_attribute(:scopes) || write_inheritable_attribute(:scopes, {})
|
9
|
+
end
|
10
|
+
|
11
|
+
def scoped
|
12
|
+
current_scope ? relation.merge(current_scope) : relation.clone
|
13
|
+
end
|
14
|
+
|
15
|
+
def scope(name, scope_options={})
|
16
|
+
name = name.to_sym
|
17
|
+
|
18
|
+
# TRP: Define the scope and merge onto the relation
|
19
|
+
scopes[name] = lambda do |*args|
|
20
|
+
options = scope_options.is_a?(Proc) ? scope_options.call(*args) : scope_options
|
21
|
+
if options.is_a?(Hash)
|
22
|
+
scoped.apply_finder_options(options)
|
23
|
+
else
|
24
|
+
scoped.merge(options)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# TRP: Bind the above block to a method for easy access
|
29
|
+
singleton_class.send(:define_method, name, &scopes[name])
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
module InstanceMethods
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.included(receiver)
|
39
|
+
receiver.extend ClassMethods
|
40
|
+
receiver.send :include, InstanceMethods
|
41
|
+
|
42
|
+
receiver.extend Conditions
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
|
2
|
+
# TRP: Implementation of this feature was heavily influenced by binarylogic's Searchlogic-2.4.19
|
3
|
+
# => http://github.com/binarylogic/searchlogic
|
4
|
+
#
|
5
|
+
# It is designed to mimick much of the API of searchlogic so that it can be used alongside AR objects utilizing Searchlogic
|
6
|
+
# without developer confusion. There are certain features that are skipped because of the nature of Perry.
|
7
|
+
|
8
|
+
module Perry::Scopes
|
9
|
+
|
10
|
+
module Conditions
|
11
|
+
|
12
|
+
COMPARISON_CONDITIONS = {
|
13
|
+
:equals => [:is, :eq],
|
14
|
+
:does_not_equal => [:not_equal_to, :is_not, :not, :ne],
|
15
|
+
:less_than => [:lt, :before],
|
16
|
+
:less_than_or_equal_to => [:lte],
|
17
|
+
:greater_than => [:gt, :after],
|
18
|
+
:greater_than_or_equal_to => [:gte],
|
19
|
+
}
|
20
|
+
|
21
|
+
WILDCARD_CONDITIONS = {
|
22
|
+
:like => [:contains, :includes],
|
23
|
+
:not_like => [:does_not_include],
|
24
|
+
:begins_with => [:bw],
|
25
|
+
:not_begin_with => [:does_not_begin_with],
|
26
|
+
:ends_with => [:ew],
|
27
|
+
:not_end_with => [:does_not_end_with]
|
28
|
+
}
|
29
|
+
|
30
|
+
CONDITIONS = {}
|
31
|
+
|
32
|
+
# Add any / all variations to every comparison and wildcard condition
|
33
|
+
COMPARISON_CONDITIONS.merge(WILDCARD_CONDITIONS).each do |condition, aliases|
|
34
|
+
CONDITIONS[condition] = aliases
|
35
|
+
CONDITIONS["#{condition}_any".to_sym] = aliases.collect { |a| "#{a}_any".to_sym }
|
36
|
+
CONDITIONS["#{condition}_all".to_sym] = aliases.collect { |a| "#{a}_all".to_sym }
|
37
|
+
end
|
38
|
+
|
39
|
+
CONDITIONS[:equals_any] = CONDITIONS[:equals_any] + [:in]
|
40
|
+
CONDITIONS[:does_not_equal_all] = CONDITIONS[:does_not_equal_all] + [:not_in]
|
41
|
+
|
42
|
+
PRIMARY_CONDITIONS = CONDITIONS.keys
|
43
|
+
ALIAS_CONDITIONS = CONDITIONS.values.flatten
|
44
|
+
|
45
|
+
def respond_to?(name, include_private=false)
|
46
|
+
super ||
|
47
|
+
condition_details(name)
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def method_missing(name, *args, &block)
|
53
|
+
if details = condition_details(name)
|
54
|
+
create_condition(details[:attribute], details[:condition], args)
|
55
|
+
send(name, *args)
|
56
|
+
else
|
57
|
+
super
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def condition_details(method_name)
|
62
|
+
return nil unless defined_attributes
|
63
|
+
|
64
|
+
attribute_name_matcher = defined_attributes.join("|")
|
65
|
+
conditions_matcher = (PRIMARY_CONDITIONS + ALIAS_CONDITIONS).join("|")
|
66
|
+
|
67
|
+
if method_name.to_s =~ /^(#{attribute_name_matcher})_(#{conditions_matcher})$/
|
68
|
+
{:attribute => $1, :condition => $2}
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def create_condition(attribute, condition, args)
|
73
|
+
if PRIMARY_CONDITIONS.include?(condition.to_sym)
|
74
|
+
create_primary_condition(attribute, condition)
|
75
|
+
elsif ALIAS_CONDITIONS.include?(condition.to_sym)
|
76
|
+
create_alias_condition(attribute, condition, args)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def create_primary_condition(attribute, condition)
|
81
|
+
scope_name = "#{attribute}_#{condition}"
|
82
|
+
scope scope_name, lambda { |a| where(scope_name => a) }
|
83
|
+
end
|
84
|
+
|
85
|
+
def create_alias_condition(attribute, condition, args)
|
86
|
+
primary_condition = primary_condition(condition)
|
87
|
+
alias_name = "#{attribute}_#{condition}"
|
88
|
+
primary_name = "#{attribute}_#{primary_condition}"
|
89
|
+
send(primary_name, *args) # go back to method_missing and make sure we create the method
|
90
|
+
singleton_class.class_eval { alias_method alias_name, primary_name }
|
91
|
+
end
|
92
|
+
|
93
|
+
# Returns the primary condition for the given alias. Ex:
|
94
|
+
#
|
95
|
+
# primary_condition(:gt) => :greater_than
|
96
|
+
def primary_condition(alias_condition)
|
97
|
+
CONDITIONS.find { |k, v| k == alias_condition.to_sym || v.include?(alias_condition.to_sym) }.first
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
module Perry::Serialization
|
4
|
+
|
5
|
+
module ClassMethods
|
6
|
+
|
7
|
+
def serialize(fields)
|
8
|
+
[*fields].each do |field|
|
9
|
+
serialized_attributes << field
|
10
|
+
|
11
|
+
define_method("deserialize_#{field}") do
|
12
|
+
YAML.load(self[field]) rescue self[field]
|
13
|
+
end
|
14
|
+
|
15
|
+
alias_method "#{field}_raw", field
|
16
|
+
alias_method field, "deserialize_#{field}"
|
17
|
+
|
18
|
+
set_serialize_writers(field) if self.write_adapter
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def set_serialize_writers(field)
|
24
|
+
define_method("serialize_#{field}") do |value|
|
25
|
+
self[field] = value.to_yaml
|
26
|
+
end
|
27
|
+
|
28
|
+
alias_method "#{field}_raw=", "#{field}="
|
29
|
+
alias_method "#{field}=", "serialize_#{field}"
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
module InstanceMethods
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.included(receiver)
|
39
|
+
receiver.class_eval do
|
40
|
+
class_inheritable_accessor :serialized_attributes
|
41
|
+
self.serialized_attributes = []
|
42
|
+
end
|
43
|
+
|
44
|
+
receiver.extend ClassMethods
|
45
|
+
receiver.send :include, InstanceMethods
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
metadata
ADDED
@@ -0,0 +1,187 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: perry
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 15
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 4
|
9
|
+
- 0
|
10
|
+
version: 0.4.0
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Travis Petticrew
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2011-04-14 00:00:00 -05:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: shoulda
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 39
|
30
|
+
segments:
|
31
|
+
- 2
|
32
|
+
- 10
|
33
|
+
- 0
|
34
|
+
version: 2.10.0
|
35
|
+
type: :development
|
36
|
+
version_requirements: *id001
|
37
|
+
- !ruby/object:Gem::Dependency
|
38
|
+
name: leftright
|
39
|
+
prerelease: false
|
40
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ">="
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
hash: 19
|
46
|
+
segments:
|
47
|
+
- 0
|
48
|
+
- 0
|
49
|
+
- 6
|
50
|
+
version: 0.0.6
|
51
|
+
type: :development
|
52
|
+
version_requirements: *id002
|
53
|
+
- !ruby/object:Gem::Dependency
|
54
|
+
name: fakeweb
|
55
|
+
prerelease: false
|
56
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
hash: 27
|
62
|
+
segments:
|
63
|
+
- 1
|
64
|
+
- 3
|
65
|
+
- 0
|
66
|
+
version: 1.3.0
|
67
|
+
type: :development
|
68
|
+
version_requirements: *id003
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: factory_girl
|
71
|
+
prerelease: false
|
72
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ">="
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
hash: 3
|
78
|
+
segments:
|
79
|
+
- 0
|
80
|
+
version: "0"
|
81
|
+
type: :development
|
82
|
+
version_requirements: *id004
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: activesupport
|
85
|
+
prerelease: false
|
86
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
87
|
+
none: false
|
88
|
+
requirements:
|
89
|
+
- - ">="
|
90
|
+
- !ruby/object:Gem::Version
|
91
|
+
hash: 3
|
92
|
+
segments:
|
93
|
+
- 2
|
94
|
+
- 3
|
95
|
+
- 0
|
96
|
+
version: 2.3.0
|
97
|
+
type: :runtime
|
98
|
+
version_requirements: *id005
|
99
|
+
- !ruby/object:Gem::Dependency
|
100
|
+
name: bertrpc
|
101
|
+
prerelease: false
|
102
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
103
|
+
none: false
|
104
|
+
requirements:
|
105
|
+
- - ">="
|
106
|
+
- !ruby/object:Gem::Version
|
107
|
+
hash: 27
|
108
|
+
segments:
|
109
|
+
- 1
|
110
|
+
- 3
|
111
|
+
- 0
|
112
|
+
version: 1.3.0
|
113
|
+
type: :runtime
|
114
|
+
version_requirements: *id006
|
115
|
+
description:
|
116
|
+
email: bobo@petticrew.net
|
117
|
+
executables: []
|
118
|
+
|
119
|
+
extensions: []
|
120
|
+
|
121
|
+
extra_rdoc_files:
|
122
|
+
- README.rdoc
|
123
|
+
files:
|
124
|
+
- README.rdoc
|
125
|
+
- Rakefile
|
126
|
+
- lib/perry/adapters/abstract_adapter.rb
|
127
|
+
- lib/perry/adapters/bertrpc_adapter.rb
|
128
|
+
- lib/perry/adapters/restful_http_adapter.rb
|
129
|
+
- lib/perry/adapters.rb
|
130
|
+
- lib/perry/association.rb
|
131
|
+
- lib/perry/association_preload.rb
|
132
|
+
- lib/perry/associations/common.rb
|
133
|
+
- lib/perry/associations/contains.rb
|
134
|
+
- lib/perry/associations/external.rb
|
135
|
+
- lib/perry/base.rb
|
136
|
+
- lib/perry/cacheable/entry.rb
|
137
|
+
- lib/perry/cacheable/store.rb
|
138
|
+
- lib/perry/cacheable.rb
|
139
|
+
- lib/perry/core_ext/kernel/singleton_class.rb
|
140
|
+
- lib/perry/errors.rb
|
141
|
+
- lib/perry/logger.rb
|
142
|
+
- lib/perry/persistence.rb
|
143
|
+
- lib/perry/relation/finder_methods.rb
|
144
|
+
- lib/perry/relation/query_methods.rb
|
145
|
+
- lib/perry/relation.rb
|
146
|
+
- lib/perry/scopes/conditions.rb
|
147
|
+
- lib/perry/scopes.rb
|
148
|
+
- lib/perry/serialization.rb
|
149
|
+
- lib/perry/version.rb
|
150
|
+
- lib/perry.rb
|
151
|
+
has_rdoc: true
|
152
|
+
homepage: http://github.com/tpett/perry
|
153
|
+
licenses: []
|
154
|
+
|
155
|
+
post_install_message:
|
156
|
+
rdoc_options:
|
157
|
+
- --main
|
158
|
+
- README.rdoc
|
159
|
+
require_paths:
|
160
|
+
- lib
|
161
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
162
|
+
none: false
|
163
|
+
requirements:
|
164
|
+
- - ">="
|
165
|
+
- !ruby/object:Gem::Version
|
166
|
+
hash: 3
|
167
|
+
segments:
|
168
|
+
- 0
|
169
|
+
version: "0"
|
170
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
171
|
+
none: false
|
172
|
+
requirements:
|
173
|
+
- - ">="
|
174
|
+
- !ruby/object:Gem::Version
|
175
|
+
hash: 3
|
176
|
+
segments:
|
177
|
+
- 0
|
178
|
+
version: "0"
|
179
|
+
requirements: []
|
180
|
+
|
181
|
+
rubyforge_project:
|
182
|
+
rubygems_version: 1.4.2
|
183
|
+
signing_key:
|
184
|
+
specification_version: 3
|
185
|
+
summary: Ruby library for querying and mapping data through generic interfaces
|
186
|
+
test_files: []
|
187
|
+
|