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
@@ -4,8 +4,6 @@ module Ohm
4
4
  # You can implement callbacks by overriding any of the following
5
5
  # methods:
6
6
  #
7
- # - before_validate
8
- # - after_validate
9
7
  # - before_create
10
8
  # - after_create
11
9
  # - before_save
@@ -22,8 +20,6 @@ module Ohm
22
20
  # class Post < Ohm::Model
23
21
  # include Ohm::Callbacks
24
22
  #
25
- # before :validate, :clean_decimals
26
- #
27
23
  # before :create, :timestamp!
28
24
  # before :save, :recalc_votes
29
25
  #
@@ -53,11 +49,11 @@ module Ohm
53
49
  # end
54
50
 
55
51
  module Callbacks
56
- def self.included(base)
57
- base.extend Macros
52
+ def self.included(model)
53
+ model.extend ClassMethods
58
54
  end
59
55
 
60
- module Macros
56
+ module ClassMethods
61
57
  # Use to add a before callback on `method`. Only symbols
62
58
  # are allowed, no string eval, no block option also.
63
59
  #
@@ -79,12 +75,12 @@ module Ohm
79
75
  # end
80
76
  # end
81
77
  #
82
- # @param [Symbol] method the method type, `:validate`, `:create`, or `:save`
78
+ # @param [Symbol] method the method type, e.g. `:create`, or `:save`
83
79
  # @param [Symbol] callback the name of the method to execute
84
80
  # @return [Array] the callback in an array if added e.g. [:timestamp]
85
81
  # @return [nil] if the callback already exists
86
82
  def before(method, callback)
87
- unless callbacks[:before][method].include? callback
83
+ unless callbacks[:before][method].include?(callback)
88
84
  callbacks[:before][method] << callback
89
85
  end
90
86
  end
@@ -115,7 +111,7 @@ module Ohm
115
111
  # @return [Array] the callback in an array if added e.g. [:timestamp]
116
112
  # @return [nil] if the callback already exists
117
113
  def after(method, callback)
118
- unless callbacks[:after][method].include? callback
114
+ unless callbacks[:after][method].include?(callback)
119
115
  callbacks[:after][method] << callback
120
116
  end
121
117
  end
@@ -126,76 +122,35 @@ module Ohm
126
122
  end
127
123
  end
128
124
 
129
- # Overrides the validate method of Ohm::Model. This is a bit tricky,
130
- # since typically you override this. Make sure you do something like:
131
- #
132
- # def validate
133
- # super
134
- #
135
- # # do your assertions
136
- # end
137
- #
138
- # This ensures that you call this method when you defined your own validate
139
- # method.
140
- #
141
- # In all honesty, I don't see the value of putting this here, and I'm still
142
- # weighing if this is _really_ needed.
143
- def validate
144
- execute_callback(:before, :validate)
145
- super
146
- execute_callback(:after, :validate)
147
- end
148
-
149
125
  # The overriden create of Ohm::Model. It checks if the
150
126
  # model is valid, and executes all before :create callbacks.
151
127
  #
152
128
  # If the create succeeds, all after :create callbacks are
153
129
  # executed.
154
- def create
155
- return unless valid?
130
+ def write
131
+ creating = @creating
156
132
 
157
- execute_callback(:before, :create)
133
+ execute_callback(:before, :create) if creating
134
+ execute_callback(:before, :update) unless creating
158
135
  execute_callback(:before, :save)
159
136
 
160
- initialize_id
161
-
162
- mutex do
163
- create_model_membership
164
- write
165
- add_to_indices
137
+ super
166
138
 
167
- execute_callback(:after, :create)
168
- execute_callback(:after, :save)
169
- end
139
+ execute_callback(:after, :create) if creating
140
+ execute_callback(:after, :update) unless creating
141
+ execute_callback(:after, :save)
170
142
  end
171
143
 
172
- # The overridden save of Ohm::Model. It checks if the model
173
- # is valid, and executes all before :save callbacks.
174
- #
175
- # If the save also succeeds, all after :save callbacks are
176
- # executed.
177
- def save
178
- return create if new?
179
- return unless valid?
180
-
181
- execute_callback(:before, :save)
182
- execute_callback(:before, :update)
183
-
184
- mutex do
185
- write
186
- update_indices
187
-
188
- execute_callback(:after, :save)
189
- execute_callback(:after, :update)
190
- end
144
+ def create
145
+ @creating = true
146
+ super
147
+ @creating = false
191
148
  end
192
149
 
193
150
  def delete
194
151
  execute_callback(:before, :delete)
195
-
196
- super.tap do |is_deleted|
197
- execute_callback(:after, :delete) if is_deleted
198
- end
152
+ super
153
+ execute_callback(:after, :delete)
199
154
  end
200
155
 
201
156
  protected
data/lib/ohm/contrib.rb CHANGED
@@ -1,21 +1,13 @@
1
+ require_relative "callbacks"
2
+ require_relative "datatypes"
3
+ require_relative "locking"
4
+ require_relative "scope"
5
+ require_relative "slug"
6
+ require_relative "softdelete"
7
+ require_relative "timestamping"
8
+
1
9
  module Ohm
2
10
  module Contrib
3
- VERSION = "0.1.2"
11
+ VERSION = "1.0.rc0"
4
12
  end
5
-
6
- autoload :ActiveModelExtension, "ohm/contrib/active_model_extension"
7
- autoload :Boundaries, "ohm/contrib/boundaries"
8
- autoload :Timestamping, "ohm/contrib/timestamping"
9
- autoload :LengthValidations, "ohm/contrib/length_validations"
10
- autoload :WebValidations, "ohm/contrib/web_validations"
11
- autoload :NumberValidations, "ohm/contrib/number_validations"
12
- autoload :DateValidations, "ohm/contrib/date_validations"
13
- autoload :ExtraValidations, "ohm/contrib/extra_validations"
14
- autoload :Typecast, "ohm/contrib/typecast"
15
- autoload :Locking, "ohm/contrib/locking"
16
- autoload :Callbacks, "ohm/contrib/callbacks"
17
- autoload :Slug, "ohm/contrib/slug"
18
- autoload :Scope, "ohm/contrib/scope"
19
- autoload :SoftDelete, "ohm/contrib/soft_delete"
20
- autoload :FulltextSearching, "ohm/contrib/fulltext_searching"
21
13
  end
@@ -0,0 +1,38 @@
1
+ require "bigdecimal"
2
+ require "date"
3
+ require "json"
4
+ require "time"
5
+
6
+ module Ohm
7
+ module DataTypes
8
+ module Type
9
+ Integer = lambda { |x| x.to_i }
10
+ Decimal = lambda { |x| x && BigDecimal(x.to_s) }
11
+ Float = lambda { |x| x.to_f }
12
+ Boolean = lambda { |x| !!x }
13
+ Time = lambda { |t| t && (t.kind_of?(::Time) ? t : ::Time.parse(t)) }
14
+ Date = lambda { |d| d && (d.kind_of?(::Date) ? d : ::Date.parse(d)) }
15
+ Timestamp = lambda { |t| t && UnixTime.at(t.to_i) }
16
+ Hash = lambda { |h| h && (h.kind_of?(::Hash) ? SerializedHash[h] : JSON(h)) }
17
+ Array = lambda { |a| a && (a.kind_of?(::Array) ? SerializedArray.new(a) : JSON(a)) }
18
+ end
19
+
20
+ class UnixTime < Time
21
+ def to_s
22
+ to_i.to_s
23
+ end
24
+ end
25
+
26
+ class SerializedHash < Hash
27
+ def to_s
28
+ JSON.dump(self)
29
+ end
30
+ end
31
+
32
+ class SerializedArray < Array
33
+ def to_s
34
+ JSON.dump(self)
35
+ end
36
+ end
37
+ end
38
+ end
@@ -52,5 +52,4 @@ module Ohm
52
52
  lock.to_f < Time.now.to_f
53
53
  end
54
54
  end
55
- end
56
-
55
+ end
data/lib/ohm/plugin.rb ADDED
@@ -0,0 +1,54 @@
1
+ module Ohm
2
+ module Plugin
3
+ def plugin(name)
4
+ mixin = Plugin[name]
5
+
6
+ include mixin
7
+ extend mixin::ClassMethods if mixin.const_defined?(:ClassMethods, false)
8
+
9
+ mixin.setup(self) if mixin.respond_to?(:setup)
10
+ end
11
+
12
+ def self.[](plugin)
13
+ case plugin
14
+ when Module
15
+ plugin
16
+ when Symbol
17
+ name, path = Plugin.registry[plugin]
18
+
19
+ raise Unknown.new(plugin) if name.nil?
20
+
21
+ require path
22
+ Ohm.const_get(name, false)
23
+ else
24
+ raise Unknown.new(plugin)
25
+ end
26
+ end
27
+
28
+ def self.register(name, name_and_path)
29
+ registry[name] = name_and_path
30
+ end
31
+
32
+ def self.registry
33
+ @registry ||= {
34
+ :callbacks => [:Callbacks, "ohm/contrib/callbacks"],
35
+ :datatypes => [:DataTypes, "ohm/contrib/data_types"],
36
+ :locking => [:Locking, "ohm/contrib/locking"],
37
+ :scope => [:Scope, "ohm/contrib/scope"],
38
+ :slug => [:Slug, "ohm/contrib/slug"],
39
+ :softdelete => [:SoftDelete, "ohm/contrib/soft_delete"],
40
+ :timestamping => [:Timestamping, "ohm/contrib/timestamping"]
41
+ }
42
+ end
43
+
44
+ class Unknown < StandardError
45
+ def initialize(plugin)
46
+ @plugin = plugin
47
+ end
48
+
49
+ def message
50
+ "Unknown plugin: #{@plugin}"
51
+ end
52
+ end
53
+ end
54
+ end
@@ -1,14 +1,14 @@
1
1
  module Ohm
2
2
  module Scope
3
- def self.included(base)
4
- unless defined?(base::DefinedScopes)
5
- base.const_set(:DefinedScopes, Module.new)
3
+ def self.included(model)
4
+ unless model.const_defined?(:DefinedScopes, false)
5
+ model.const_set(:DefinedScopes, Module.new)
6
6
  end
7
7
 
8
- base.extend Macros
8
+ model.extend ClassMethods
9
9
  end
10
10
 
11
- module Macros
11
+ module ClassMethods
12
12
  def scope(scope = nil, &block)
13
13
  self::DefinedScopes.module_eval(&block) if block_given?
14
14
  self::DefinedScopes.send(:include, scope) if scope
@@ -25,5 +25,6 @@ module Ohm
25
25
 
26
26
  Ohm::Model::Set.send :include, OverloadedSet
27
27
  end
28
- end
29
28
 
29
+ Model::Set.send :include, Scope::OverloadedSet
30
+ end
@@ -1,17 +1,17 @@
1
1
  module Ohm
2
2
  module Slug
3
- def self.included(base)
4
- base.extend FinderOverride
3
+ def self.included(model)
4
+ model.extend ClassMethods
5
5
  end
6
6
 
7
- module FinderOverride
7
+ module ClassMethods
8
8
  def [](id)
9
9
  super(id.to_i)
10
10
  end
11
11
  end
12
12
 
13
13
  def slug(str = to_s)
14
- ret = iconv(str)
14
+ ret = transcode(str)
15
15
  ret.gsub!("'", "")
16
16
  ret.gsub!(/\p{^Alnum}/u, " ")
17
17
  ret.strip!
@@ -20,16 +20,21 @@ module Ohm
20
20
  end
21
21
  module_function :slug
22
22
 
23
- def iconv(str)
23
+ def transcode(str)
24
24
  begin
25
+ # TODO: replace with a String#encode version which will
26
+ # contain proper transliteration tables. For now, Iconv
27
+ # still wins because we get that for free.
28
+ v, $VERBOSE = $VERBOSE, nil
25
29
  require "iconv"
30
+ $VERBOSE = v
26
31
 
27
32
  Iconv.iconv("ascii//translit//ignore", "utf-8", str)[0]
28
33
  rescue LoadError
29
34
  return str
30
35
  end
31
36
  end
32
- module_function :iconv
37
+ module_function :transcode
33
38
 
34
39
  def to_param
35
40
  "#{ id }-#{ slug }"
@@ -4,7 +4,7 @@ module Ohm
4
4
  # @example
5
5
  #
6
6
  # class Post < Ohm::Model
7
- # include Ohm::SoftDelete
7
+ # plugin :softdelete
8
8
  #
9
9
  # attribute :title
10
10
  # index :title
@@ -23,7 +23,7 @@ module Ohm
23
23
  # Post.all.empty?
24
24
  # # => true
25
25
  #
26
- # Post.find(:title => 'Title').empty?
26
+ # Post.find(:title => 'Title').include?(post)
27
27
  # # => true
28
28
  #
29
29
  # Post.exists?(post.id)
@@ -34,35 +34,39 @@ module Ohm
34
34
  # post.deleted?
35
35
  # # => true
36
36
  module SoftDelete
37
- IS_DELETED = "1"
37
+ DELETED_FLAG = "1"
38
38
 
39
- def self.included(base)
40
- base.attribute :deleted
41
- base.index :deleted
42
- base.extend ClassMethods
39
+ def self.included(model)
40
+ model.attribute :deleted
41
+
42
+ model.extend ClassMethods
43
43
  end
44
44
 
45
45
  def delete
46
- update(:deleted => IS_DELETED)
47
- end
46
+ self.class.all.delete(self)
47
+ self.class.deleted.add(self)
48
48
 
49
- def deleted?
50
- deleted == IS_DELETED
49
+ update(deleted: DELETED_FLAG)
51
50
  end
52
51
 
53
- private
54
- def create_model_membership
55
- self.class.key[:all].sadd(self.id)
52
+ def restore
53
+ self.class.all.add(self)
54
+ self.class.deleted.delete(self)
55
+
56
+ update(deleted: nil)
56
57
  end
57
58
 
58
- def delete_model_membership
59
- key.del
60
- self.class.key[:all].srem(self.id)
59
+ def deleted?
60
+ deleted == DELETED_FLAG
61
61
  end
62
62
 
63
63
  module ClassMethods
64
- def all
65
- super.except(:deleted => IS_DELETED)
64
+ def deleted
65
+ Model::Set.new(key[:deleted], Model::Wrapper.wrap(self))
66
+ end
67
+
68
+ def exists?(id)
69
+ super || key[:deleted].sismember(id)
66
70
  end
67
71
  end
68
72
  end
@@ -0,0 +1,41 @@
1
+ require_relative "callbacks"
2
+ require_relative "datatypes"
3
+
4
+ module Ohm
5
+ # Provides created_at / updated_at timestamps.
6
+ #
7
+ # @example
8
+ #
9
+ # class Post < Ohm::Model
10
+ # include Ohm::Timestamping
11
+ # end
12
+ #
13
+ # post = Post.create
14
+ # post.created_at.to_s == Time.now.utc.to_s
15
+ # # => true
16
+ #
17
+ # post = Post[post.id]
18
+ # post.save
19
+ # post.updated_at.to_s == Time.now.utc.to_s
20
+ # # => true
21
+ module Timestamping
22
+ def self.included(model)
23
+ model.send :include, Callbacks
24
+
25
+ model.attribute :created_at, DataTypes::Type::Timestamp
26
+ model.attribute :updated_at, DataTypes::Type::Timestamp
27
+
28
+ model.before :create, :set_created_at
29
+ model.before :save, :set_updated_at
30
+ end
31
+
32
+ protected
33
+ def set_created_at
34
+ self.created_at ||= Time.now.utc.to_i
35
+ end
36
+
37
+ def set_updated_at
38
+ self.updated_at = Time.now.utc.to_i
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,30 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = "ohm-contrib"
3
+ s.version = "1.0.rc0"
4
+ s.summary = %{A collection of decoupled drop-in modules for Ohm.}
5
+ s.description = %{Includes a couple of core functions such as callbacks, timestamping, typecasting and lots of generic validation routines.}
6
+ s.author = "Cyril David"
7
+ s.email = "cyx.ucron@gmail.com"
8
+ s.homepage = "http://github.com/cyx/ohm-contrib"
9
+
10
+ s.specification_version = 2 if s.respond_to? :specification_version=
11
+
12
+ s.files = Dir[
13
+ "LICENSE",
14
+ "README.markdown",
15
+ "rakefile",
16
+ "lib/**/*.rb",
17
+ "*.gemspec",
18
+ "test/*.*",
19
+ ]
20
+
21
+ s.require_paths = ["lib"]
22
+ s.rubyforge_project = "ohm-contrib"
23
+
24
+ s.has_rdoc = false
25
+ s.add_dependency "ohm"
26
+
27
+ s.add_development_dependency "cutest"
28
+ s.add_development_dependency "redis"
29
+ s.add_development_dependency "override"
30
+ end
data/rakefile ADDED
@@ -0,0 +1,6 @@
1
+ task :test do
2
+ require 'cutest'
3
+ $:.unshift('./lib', './test')
4
+
5
+ Cutest.run(Dir['./test/*.rb'])
6
+ end
@@ -1,6 +1,6 @@
1
1
  # encoding: UTF-8
2
2
 
3
- require File.expand_path("./helper", File.dirname(__FILE__))
3
+ require_relative "helper"
4
4
 
5
5
  class Post < Ohm::Model
6
6
  include Ohm::Callbacks
@@ -22,8 +22,6 @@ class Post < Ohm::Model
22
22
  end
23
23
 
24
24
  protected
25
- def before_validate() incr(:do_before_validate) end
26
- def after_validate() incr(:do_after_validate) end
27
25
  def before_create() incr(:do_before_create) end
28
26
  def after_create() incr(:do_after_create) end
29
27
  def before_save() incr(:do_before_save) end
@@ -33,7 +31,6 @@ protected
33
31
  def before_delete() incr(:do_before_delete) end
34
32
  def after_delete() incr(:do_after_delete) end
35
33
 
36
-
37
34
  def incr(action)
38
35
  val = instance_variable_get("@#{ action }")
39
36
  val ||= 0
@@ -43,5 +40,4 @@ protected
43
40
  end
44
41
  end
45
42
 
46
- load File.expand_path('./callbacks_lint.rb', File.dirname(__FILE__))
47
-
43
+ load File.expand_path('lint/callbacks.rb', File.dirname(__FILE__))
@@ -7,9 +7,6 @@ class Post < Ohm::Model
7
7
 
8
8
  attribute :body
9
9
 
10
- before :validate, :do_before_validate
11
- after :validate, :do_after_validate
12
-
13
10
  before :create, :do_before_create
14
11
  after :create, :do_after_create
15
12
 
@@ -23,8 +20,6 @@ class Post < Ohm::Model
23
20
  after :update, :do_after_update
24
21
 
25
22
  def validate
26
- super
27
-
28
23
  assert_present :body
29
24
  end
30
25
 
@@ -37,8 +32,6 @@ class Post < Ohm::Model
37
32
  end
38
33
 
39
34
  protected
40
- def do_before_validate() incr(:do_before_validate) end
41
- def do_after_validate() incr(:do_after_validate) end
42
35
  def do_before_create() incr(:do_before_create) end
43
36
  def do_after_create() incr(:do_after_create) end
44
37
  def do_before_save() incr(:do_before_save) end
@@ -58,5 +51,4 @@ protected
58
51
  end
59
52
  end
60
53
 
61
- load File.expand_path('./callbacks_lint.rb', File.dirname(__FILE__))
62
-
54
+ load File.expand_path('lint/callbacks.rb', File.dirname(__FILE__))