perry 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -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
@@ -0,0 +1,13 @@
1
+ module Perry
2
+ module Version
3
+
4
+ MAJOR = 0
5
+ MINOR = 4
6
+ TINY = 0
7
+
8
+ def self.to_s # :nodoc:
9
+ [MAJOR, MINOR, TINY].join('.')
10
+ end
11
+
12
+ end
13
+ 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
+