ohm-contrib 0.1.2 → 1.0.rc0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. data/lib/ohm/{contrib/callbacks.rb → callbacks.rb} +20 -65
  2. data/lib/ohm/contrib.rb +9 -17
  3. data/lib/ohm/datatypes.rb +38 -0
  4. data/lib/ohm/{contrib/locking.rb → locking.rb} +1 -2
  5. data/lib/ohm/plugin.rb +54 -0
  6. data/lib/ohm/{contrib/scope.rb → scope.rb} +7 -6
  7. data/lib/ohm/{contrib/slug.rb → slug.rb} +11 -6
  8. data/lib/ohm/{contrib/soft_delete.rb → softdelete.rb} +23 -19
  9. data/lib/ohm/timestamping.rb +41 -0
  10. data/ohm-contrib.gemspec +30 -0
  11. data/rakefile +6 -0
  12. data/test/{instance_callbacks_test.rb → instance_callbacks.rb} +2 -6
  13. data/test/{macro_callbacks_test.rb → macro_callbacks.rb} +1 -9
  14. data/test/plugin.rb +212 -0
  15. data/test/{scope_test.rb → scope.rb} +2 -3
  16. data/test/slug.rb +24 -0
  17. data/test/{soft_delete_test.rb → soft_delete.rb} +13 -3
  18. data/test/{timestamping_test.rb → timestamping.rb} +5 -6
  19. metadata +33 -70
  20. data/Rakefile +0 -8
  21. data/lib/ohm/contrib/active_model_extension.rb +0 -87
  22. data/lib/ohm/contrib/boundaries.rb +0 -41
  23. data/lib/ohm/contrib/date_validations.rb +0 -23
  24. data/lib/ohm/contrib/extra_validations.rb +0 -48
  25. data/lib/ohm/contrib/fulltext_searching.rb +0 -80
  26. data/lib/ohm/contrib/length_validations.rb +0 -32
  27. data/lib/ohm/contrib/number_validations.rb +0 -14
  28. data/lib/ohm/contrib/timestamping.rb +0 -38
  29. data/lib/ohm/contrib/typecast.rb +0 -350
  30. data/lib/ohm/contrib/web_validations.rb +0 -52
  31. data/test/activemodel_test.rb +0 -27
  32. data/test/boundaries_test.rb +0 -45
  33. data/test/callbacks_lint.rb +0 -141
  34. data/test/date_validations_test.rb +0 -29
  35. data/test/fixtures/ascii8bit.txt +0 -1
  36. data/test/fulltext_searching_test.rb +0 -63
  37. data/test/length_validations_test.rb +0 -31
  38. data/test/membership_validation_test.rb +0 -31
  39. data/test/number_validations_test.rb +0 -37
  40. data/test/slug_test.rb +0 -30
  41. data/test/typecast_array_test.rb +0 -146
  42. data/test/typecast_boolean_test.rb +0 -43
  43. data/test/typecast_date_test.rb +0 -82
  44. data/test/typecast_decimal_test.rb +0 -82
  45. data/test/typecast_existing_attribute_test.rb +0 -22
  46. data/test/typecast_float_test.rb +0 -57
  47. data/test/typecast_hash_test.rb +0 -120
  48. data/test/typecast_integer_test.rb +0 -71
  49. data/test/typecast_string_test.rb +0 -43
  50. data/test/typecast_time_test.rb +0 -72
  51. data/test/typecast_timezone_test.rb +0 -37
  52. data/test/web_validations_test.rb +0 -79
@@ -1,80 +0,0 @@
1
- begin
2
- require "text"
3
- rescue LoadError
4
- raise LoadError,
5
- "Type `[sudo] gem install text` to use Ohm::FulltextSearching."
6
- end
7
-
8
- module Ohm
9
- module FulltextSearching
10
- def self.included(model)
11
- model.extend ClassMethods
12
- end
13
-
14
- module ClassMethods
15
- def search(hash)
16
- find(hash_to_metaphones(hash))
17
- end
18
-
19
- def hash_to_metaphones(hash)
20
- ret = Hash.new { |h, k| h[k] = [] }
21
-
22
- hash.each do |att, string|
23
- metaphones(string).each { |m| ret[:"fulltext_#{att}"] << m }
24
- end
25
-
26
- return ret
27
- end
28
-
29
- def double_metaphone(str)
30
- return [] if STOPWORDS.include?(str.to_s.downcase)
31
-
32
- Text::Metaphone.double_metaphone(str).compact
33
- end
34
-
35
- def metaphones(str)
36
- str.to_s.strip.split(/\s+/).map { |s| double_metaphone(s) }.flatten
37
- end
38
-
39
- def fulltext(att)
40
- field = :"fulltext_#{att}"
41
-
42
- define_method(field) { self.class.metaphones(send(att)) }
43
- index(field)
44
- end
45
- end
46
-
47
- STOPWORDS = %w{a about above according across actually adj after
48
- afterwards again against all almost alone along already also although
49
- always among amongst an and another any anyhow anyone anything anywhere
50
- are aren't around as at b be became because become becomes becoming
51
- been before beforehand begin behind being below beside besides between
52
- beyond both but by c can can't cannot caption co co. could couldn't d
53
- did didn't do does doesn't don't down during e each eg eight eighty
54
- either else elsewhere end ending enough etc even ever every everyone
55
- everything everywhere except f few first for found from further g h
56
- had has hasn't have haven't he he'd he'll he's hence her here here's
57
- hereafter hereby herein hereupon hers herself him himself his how
58
- however hundred i i'd i'll i'm i've ie if in inc. indeed instead into is
59
- isn't it it's its itself j k l last later latter latterly least less let
60
- let's like likely ltd m made make makes many maybe me meantime meanwhile
61
- might miss more moreover most mostly mr mrs much must my myself n namely
62
- neither never nevertheless next nine ninety no nobody none nonetheless
63
- noone nor not nothing now nowhere o of off often on once one one's only
64
- onto or other others otherwise our ours ourselves out over overall own
65
- p per perhaps q r rather recent recently s same seem seemed seeming
66
- seems seven several she she'd she'll she's should shouldn't since so
67
- some somehow someone something sometime sometimes somewhere still such
68
- t taking than that that'll that's that've the their them themselves then
69
- thence there there'd there'll there're there's there've thereafter thereby
70
- therefore therein thereupon these they they'd they'll they're they've
71
- thirty this those though three through throughout thru thus to together
72
- too toward towards u under unless unlike unlikely until up upon us used
73
- using v very via w was wasn't we we'd we'll we're we've well were weren't
74
- what what'll what's what've whatever when whence whenever where where's
75
- whereafter whereas whereby wherein whereupon wherever whether which while
76
- whither who who'd who'll who's whoever whole whom whomever whose why will
77
- with within without won't would wouldn't x y yes yet you you'd you'll
78
- you're you've your yours yourself yourselves z}
79
- end
80
- end
@@ -1,32 +0,0 @@
1
- module Ohm
2
- module LengthValidations
3
-
4
- def assert_min_length(att, length, error = [att, :too_short])
5
- if assert_present(att, error)
6
- m = send(att).to_s
7
- assert is_long_enough?(m, length), error
8
- end
9
- end
10
-
11
- def assert_max_length(att, length, error = [att, :too_long])
12
- if assert_present(att, error)
13
- m = send(att).to_s
14
- assert is_too_long?(m, length), error
15
- end
16
- end
17
-
18
- private
19
- def is_too_long?(string, length)
20
- string.size <= length
21
- rescue ArgumentError
22
- return false
23
- end
24
-
25
- def is_long_enough?(string, length)
26
- string.size >= length
27
- rescue ArgumentError
28
- return false
29
- end
30
- end
31
- end
32
-
@@ -1,14 +0,0 @@
1
- module Ohm
2
- # This module will include all numeric validation needs.
3
- # As of VERSION 0.0.27, Ohm::NumberValidations#assert_decimal
4
- # is the only method provided.
5
- module NumberValidations
6
- DECIMAL_REGEX = /^(\d+)?(\.\d+)?$/
7
-
8
- protected
9
- def assert_decimal(att, error = [att, :not_decimal])
10
- assert_format att, DECIMAL_REGEX, error
11
- end
12
- end
13
- end
14
-
@@ -1,38 +0,0 @@
1
- module Ohm
2
- # Provides created_at / updated_at timestamps.
3
- #
4
- # @example
5
- #
6
- # class Post < Ohm::Model
7
- # include Ohm::Timestamping
8
- # end
9
- #
10
- # post = Post.create
11
- # post.created_at.to_s == Time.now.utc.to_s
12
- # # => true
13
- #
14
- # post = Post[post.id]
15
- # post.save
16
- # post.updated_at.to_s == Time.now.utc.to_s
17
- # # => true
18
- module Timestamping
19
- def self.included(base)
20
- base.attribute :created_at
21
- base.attribute :updated_at
22
- end
23
-
24
- def create
25
- self.created_at ||= Time.now.utc.to_s
26
-
27
- super
28
- end
29
-
30
- protected
31
- def write
32
- self.updated_at = Time.now.utc.to_s
33
-
34
- super
35
- end
36
- end
37
- end
38
-
@@ -1,350 +0,0 @@
1
- require 'bigdecimal'
2
- require 'time'
3
- require 'date'
4
- require 'forwardable'
5
- require "json"
6
-
7
- module Ohm
8
- # Provides all the primitive types. The following are included:
9
- #
10
- # * String
11
- # * Decimal
12
- # * Integer
13
- # * Float
14
- # * Date
15
- # * Time
16
- # * Hash
17
- # * Array
18
- # * Boolean
19
- module Types
20
- def self.defined?(type)
21
- @_constants ||= constants.map { |c| c.to_sym }
22
- @_constants.include?(type.to_sym)
23
- end
24
-
25
- def self.[](type)
26
- const_get(type.to_s.split('::').last)
27
- end
28
-
29
- class Base < BasicObject
30
- class Exception < ::Exception; end
31
-
32
- extend ::Forwardable
33
-
34
- @@delegation_blacklist = [
35
- :==, :to_s, :initialize, :inspect, :object_id, :__send__, :__id__,
36
- :respond_to?
37
- ]
38
-
39
- def self.[](value)
40
- return empty if value.to_s.empty?
41
-
42
- new(value)
43
- end
44
-
45
- def self.empty
46
- defined?(self::RAW) ? self::RAW.new : nil
47
- end
48
-
49
- def self.delegate_to(klass, except = @@delegation_blacklist)
50
- methods = klass.public_instance_methods.map { |e| e.to_sym } - except
51
- def_delegators :object, *methods
52
- end
53
-
54
- def inspect
55
- @raw.inspect
56
- end
57
- end
58
-
59
- class Primitive < Base
60
- def initialize(value)
61
- @raw = value
62
- end
63
-
64
- def to_s
65
- @raw.to_s
66
- end
67
-
68
- def ==(other)
69
- to_s == other.to_s
70
- end
71
-
72
- def respond_to?(method)
73
- object.respond_to?(method)
74
- rescue ::ArgumentError
75
- @raw.respond_to?(method)
76
- end
77
-
78
- def object
79
- @raw
80
- end
81
- end
82
-
83
- class String < Primitive
84
- delegate_to ::String
85
-
86
- def type
87
- ::String
88
- end
89
- end
90
-
91
- class Decimal < Primitive
92
- delegate_to ::BigDecimal
93
-
94
- def object
95
- ::Kernel::BigDecimal(@raw)
96
- end
97
-
98
- def type
99
- ::BigDecimal
100
- end
101
- end
102
-
103
- class Integer < Primitive
104
- delegate_to ::Fixnum
105
-
106
- def object
107
- ::Kernel::Integer(@raw)
108
- end
109
-
110
- def type
111
- ::Fixnum
112
- end
113
- end
114
-
115
- class Float < Primitive
116
- delegate_to ::Float
117
-
118
- def object
119
- ::Kernel::Float(@raw)
120
- end
121
-
122
- def type
123
- ::Float
124
- end
125
- end
126
-
127
- class Time < Primitive
128
- delegate_to ::Time
129
-
130
- def object
131
- ::Time.parse(@raw).utc
132
- end
133
-
134
- def type
135
- ::Time
136
- end
137
- end
138
-
139
- class Date < Primitive
140
- delegate_to ::Date
141
-
142
- def object
143
- ::Date.parse(@raw)
144
- end
145
-
146
- def type
147
- ::Date
148
- end
149
- end
150
-
151
- class Boolean
152
- def self.[](value)
153
- case value
154
- when 'false', false, '0', 0 then false
155
- when 'true', true, '1', 1 then true
156
- end
157
- end
158
- end
159
-
160
- class Serialized < Base
161
- attr :object
162
-
163
- def initialize(raw)
164
- @object = case raw
165
- when self.class::RAW
166
- raw
167
- when ::String
168
- begin
169
- ::JSON.parse(raw)
170
- rescue ::JSON::ParserError
171
- raw
172
- end
173
- when self.class
174
- raw.object
175
- else
176
- ::Kernel.raise ::TypeError,
177
- "%s does not accept %s" % [self.class, raw.inspect]
178
- end
179
- end
180
-
181
- def ==(other)
182
- object == other
183
- end
184
-
185
- def to_s
186
- object.to_json
187
- end
188
- alias :inspect :to_s
189
-
190
- def respond_to?(method)
191
- object.respond_to?(method)
192
- end
193
-
194
- def type
195
- self.class::RAW
196
- end
197
- end
198
-
199
- class Hash < Serialized
200
- RAW = ::Hash
201
-
202
- delegate_to ::Hash
203
-
204
- # @private since basic object doesn't include a #class we need
205
- # to define this manually
206
- def class
207
- ::Ohm::Types::Hash
208
- end
209
- end
210
-
211
- class Array < Serialized
212
- RAW = ::Array
213
-
214
- delegate_to ::Array
215
-
216
- # @private since basic object doesn't include a #class we need
217
- # to define this manually
218
- def class
219
- ::Ohm::Types::Array
220
- end
221
- end
222
- end
223
-
224
- # Provides unobtrusive, non-explosive typecasting. Instead of exploding on
225
- # set of an invalid value, this module takes the approach of just taking in
226
- # parameters and letting you do validation yourself. The only thing this
227
- # module does for you is the boilerplate casting you might need to do.
228
- #
229
- # @example
230
- #
231
- # # without typecasting
232
- # class Item < Ohm::Model
233
- # attribute :price
234
- # attribute :posted
235
- # end
236
- #
237
- # item = Item.create(:price => 299, :posted => Time.now.utc)
238
- # item = Item[item.id]
239
- #
240
- # # now when you try and grab `item.price`, its a string.
241
- # "299" == item.price
242
- # # => true
243
- #
244
- # # you can opt to manually cast everytime, or do it in the model, i.e.
245
- #
246
- # class Item
247
- # def price
248
- # BigDecimal(read_local(:price))
249
- # end
250
- # end
251
- #
252
- # The Typecasted way
253
- # ------------------
254
- #
255
- # class Item < Ohm::Model
256
- # include Ohm::Typecast
257
- #
258
- # attribute :price, Decimal
259
- # attribute :posted, Time
260
- # end
261
- #
262
- # item = Item.create(:price => "299", :posted => Time.now.utc)
263
- # item = Item[item.id]
264
- # item.price.class == BigDecimal
265
- # # => true
266
- #
267
- # item.price.to_s == "299"
268
- # # => true
269
- #
270
- # item.price * 2 == 598
271
- # # => true
272
- #
273
- # item.posted.strftime('%m/%d/%Y')
274
- # # => works!!!
275
- module Typecast
276
- def self.included(base)
277
- base.extend ClassMethods
278
- end
279
-
280
- module ClassMethods
281
- # Defines a typecasted attribute.
282
- #
283
- # @example
284
- #
285
- # class User < Ohm::Model
286
- # include Ohm::Typecast
287
- #
288
- # attribute :birthday, Date
289
- # attribute :last_login, Time
290
- # attribute :age, Integer
291
- # attribute :spending, Decimal
292
- # attribute :score, Float
293
- # end
294
- #
295
- # user = User.new(:birthday => "2000-01-01")
296
- # user.birthday.month == 1
297
- # # => true
298
- #
299
- # user.birthday.year == 2000
300
- # # => true
301
- #
302
- # user.birthday.day == 1
303
- # # => true
304
- #
305
- # user = User.new(:age => 20)
306
- # user.age - 1 == 19
307
- # => true
308
- #
309
- # @param [Symbol] name the name of the attribute to define.
310
- # @param [Class] type (defaults to Ohm::Types::String) a class defined in
311
- # Ohm::Types. You may define custom types in Ohm::Types if
312
- # you need to.
313
- # @return [Array] the array of attributes already defined.
314
- # @return [nil] if the attribute is already defined.
315
- def attribute(name, type = Ohm::Types::String, klass = Ohm::Types[type])
316
- # Primitive types maintain a reference to the original object
317
- # stored in @_attributes[att]. Hence mutation works for the
318
- # Primitive case. For cases like Hash, Array where the value
319
- # is `JSON.parse`d, we need to set the actual Ohm::Types::Hash
320
- # (or similar) to @_attributes[att] for mutation to work.
321
- if klass.superclass == Ohm::Types::Primitive
322
- define_method(name) { klass[read_local(name)] }
323
- else
324
- define_method(name) { write_local(name, klass[read_local(name)]) }
325
- end
326
-
327
- define_method(:"#{name}=") do |value|
328
- write_local(name, klass[value].to_s)
329
- end
330
-
331
- attributes << name unless attributes.include?(name)
332
- types[name] = type
333
- end
334
- alias :typecast :attribute
335
-
336
- def types
337
- @types ||= {}
338
- end
339
-
340
- private
341
- def const_missing(name)
342
- if Ohm::Types.defined?(name)
343
- Ohm::Types[name]
344
- else
345
- super
346
- end
347
- end
348
- end
349
- end
350
- end
@@ -1,52 +0,0 @@
1
- module Ohm
2
- # All credit goes to gnrfan of github
3
- # Basically an extraction from http://github.com/gnrfan/ohm_extra_validations
4
- #
5
- # * 2010-05-29 Updated Email Regex, Extracted out regexs to constants
6
- #
7
- # This module provides the following:
8
- # * assert_slug
9
- # * assert_email
10
- # * assert_url
11
- # * assert_ipv4
12
- module WebValidations
13
- # @see http://fightingforalostcause.net/misc/2006/compare-email-regex.php
14
- EMAIL_REGEX = /^([\w\!\#$\%\&\'\*\+\-\/\=\?\^\`{\|\}\~]+\.)*[\w\!\#$\%\&\'\*\+\-\/\=\?\^\`{\|\}\~]+@((((([a-z0-9]{1}[a-z0-9\-]{0,62}[a-z0-9]{1})|[a-z])\.)+[a-z]{2,6})|(\d{1,3}\.){3}\d{1,3}(\:\d{1,5})?)$/i
15
-
16
- SLUG_REGEX = /^[-\w]+$/
17
-
18
- URL_REGEX = /^(http|https):\/\/([a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,5}|(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}|localhost)(:[0-9]{1,5})?(\/.*)?$/ix
19
-
20
- IPV4_REGEX = /^(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}$/
21
-
22
- protected
23
- def assert_slug(att, error = [att, :not_slug])
24
- if assert_present(att, error) and assert_unique(att)
25
- assert_format(att, SLUG_REGEX, error)
26
- end
27
- end
28
-
29
- def assert_email(att, error = [att, :not_email])
30
- if assert_present(att, error)
31
- assert_format(att, EMAIL_REGEX, error)
32
- end
33
- end
34
-
35
- def assert_url(att, error = [att, :not_url])
36
- if assert_present(att, error)
37
- assert_format(att, URL_REGEX, error)
38
- end
39
- end
40
-
41
- def assert_ipv4(att, error = [att, :not_ipv4])
42
- if assert_present(att, error)
43
- assert_format(att, IPV4_REGEX, error)
44
- end
45
- end
46
-
47
- def assert_ipaddr(att, error = [att, :not_ipaddr])
48
- assert_ipv4(att, error)
49
- end
50
- end
51
- end
52
-
@@ -1,27 +0,0 @@
1
- # encoding: UTF-8
2
-
3
- require File.expand_path("./helper", File.dirname(__FILE__))
4
-
5
- require "test/unit"
6
- require "active_model"
7
-
8
- class ActiveModelTest < Test::Unit::TestCase
9
- include ActiveModel::Lint::Tests
10
-
11
- class Post < Ohm::Model
12
- include Ohm::ActiveModelExtension
13
-
14
- attribute :body
15
- list :related, Post
16
-
17
- def validate
18
- assert_present :body
19
- end
20
- end
21
-
22
- def setup
23
- @model = Post.new
24
- end
25
- end
26
-
27
-
@@ -1,45 +0,0 @@
1
- # encoding: UTF-8
2
-
3
- require File.expand_path("./helper", File.dirname(__FILE__))
4
-
5
- class Person < Ohm::Model
6
- include Ohm::Boundaries
7
-
8
- attribute :name
9
- index :name
10
- end
11
-
12
- test "first / last are nil when no records" do
13
- assert nil == Person.first
14
- assert nil == Person.last
15
- end
16
-
17
- test "first / last returns the only record when just 1 record" do
18
- matz = Person.create(:name => "matz")
19
-
20
- assert matz == Person.first
21
- assert matz == Person.last
22
- end
23
-
24
- test "has chronological order by default" do
25
- matz = Person.create(:name => "matz")
26
- linus = Person.create(:name => "linus")
27
-
28
- assert matz == Person.first
29
- assert linus == Person.last
30
- end
31
-
32
- test "respects filters passed in" do
33
- matz = Person.create(:name => "matz")
34
- linus = Person.create(:name => "linus")
35
-
36
- assert matz == Person.first(:name => "matz")
37
- assert matz == Person.last(:name => "matz")
38
-
39
- assert linus == Person.first(:name => "linus")
40
- assert linus == Person.last(:name => "linus")
41
-
42
- assert nil == Person.first(:name => "quentin")
43
- assert nil == Person.last(:name => "quentin")
44
- end
45
-