modis 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.coveralls.yml +1 -0
- data/.gitignore +17 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +8 -0
- data/Gemfile +12 -0
- data/LICENSE.txt +22 -0
- data/README.md +45 -0
- data/Rakefile +15 -0
- data/lib/modis/attributes.rb +136 -0
- data/lib/modis/configuration.rb +12 -0
- data/lib/modis/errors.rb +14 -0
- data/lib/modis/finders.rb +43 -0
- data/lib/modis/model.rb +50 -0
- data/lib/modis/persistence.rb +162 -0
- data/lib/modis/transaction.rb +13 -0
- data/lib/modis/version.rb +3 -0
- data/lib/modis.rb +27 -0
- data/lib/tasks/spec.rake +18 -0
- data/redis_model.gemspec +23 -0
- data/spec/attributes_spec.rb +179 -0
- data/spec/errors_spec.rb +18 -0
- data/spec/finders_spec.rb +84 -0
- data/spec/persistence_spec.rb +266 -0
- data/spec/spec_helper.rb +20 -0
- data/spec/support/simplecov_helper.rb +19 -0
- data/spec/support/simplecov_quality_formatter.rb +8 -0
- data/spec/transaction_spec.rb +14 -0
- data/spec/validations_spec.rb +49 -0
- metadata +123 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: dcfeb39afdb8de9cd3ffe9ec674b286eb70d06de
|
4
|
+
data.tar.gz: d7249376be524136201103ab7e9e3252311691e1
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: ca3a68ca27c5b974b150683440d1d5112ab1c78ea171219c05667c434015fbc9cd27e8e30f3d32fbd6eeb04626fff6d156196e037df1da343c232207c640cc79
|
7
|
+
data.tar.gz: f268573540e3e4c02cfe12c91fdf824a7c6550efd8092fc0ea5345c3bf9c63bfd2f3d0a61fafed693019bbad04f60c641f8a4313bf91f5f21219fca1eda05c7a
|
data/.coveralls.yml
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
service-name: travis-ci
|
data/.gitignore
ADDED
data/.ruby-gemset
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
modis
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
ruby-2.1.1
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 Ian Leitch
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
[![Build Status](https://secure.travis-ci.org/ileitch/modis.png?branch=master)](http://travis-ci.org/ileitch/modis)
|
2
|
+
[![Code Climate](https://codeclimate.com/github/ileitch/modis.png)](https://codeclimate.com/github/ileitch/modis)
|
3
|
+
[![Coverage Status](https://coveralls.io/repos/ileitch/modis/badge.png?branch=master)](https://coveralls.io/r/ileitch/modis?branch=master)
|
4
|
+
|
5
|
+
# Modis
|
6
|
+
|
7
|
+
ActiveModel + Redis with the aim to mimic ActiveRecord where possible.
|
8
|
+
|
9
|
+
## Installation
|
10
|
+
|
11
|
+
Add this line to your application's Gemfile:
|
12
|
+
|
13
|
+
gem 'modis'
|
14
|
+
|
15
|
+
And then execute:
|
16
|
+
|
17
|
+
$ bundle
|
18
|
+
|
19
|
+
Or install it yourself as:
|
20
|
+
|
21
|
+
$ gem install modis
|
22
|
+
|
23
|
+
## Usage
|
24
|
+
|
25
|
+
```ruby
|
26
|
+
class MyModel
|
27
|
+
include Modis::Models
|
28
|
+
attribute :name, String
|
29
|
+
attribute :age, Integer
|
30
|
+
end
|
31
|
+
|
32
|
+
MyModel.create!(:name => 'Ian', :age => 28)
|
33
|
+
```
|
34
|
+
|
35
|
+
## Supported Features
|
36
|
+
|
37
|
+
TODO.
|
38
|
+
|
39
|
+
## Contributing
|
40
|
+
|
41
|
+
1. Fork it
|
42
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
43
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
44
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
45
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require "rake"
|
2
|
+
require "bundler/gem_tasks"
|
3
|
+
require "rspec/core/rake_task"
|
4
|
+
Dir["lib/tasks/*.rake"].each { |rake| load rake }
|
5
|
+
|
6
|
+
RSpec::Core::RakeTask.new(:spec) do |spec|
|
7
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
8
|
+
spec.rspec_opts = ['--backtrace']
|
9
|
+
end
|
10
|
+
|
11
|
+
if RUBY_VERSION > '1.9' && defined?(RUBY_ENGINE) && RUBY_ENGINE == 'ruby'
|
12
|
+
task :default => 'spec:cane'
|
13
|
+
else
|
14
|
+
task :default => 'spec'
|
15
|
+
end
|
@@ -0,0 +1,136 @@
|
|
1
|
+
module Modis
|
2
|
+
module Attributes
|
3
|
+
TYPES = [:string, :integer, :float, :timestamp, :boolean, :array, :hash]
|
4
|
+
|
5
|
+
def self.included(base)
|
6
|
+
base.extend ClassMethods
|
7
|
+
|
8
|
+
base.instance_eval do
|
9
|
+
bootstrap_attributes
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
module ClassMethods
|
14
|
+
def bootstrap_attributes
|
15
|
+
class << self
|
16
|
+
attr_accessor :attributes
|
17
|
+
end
|
18
|
+
|
19
|
+
self.attributes = {}
|
20
|
+
|
21
|
+
attribute :id, :integer
|
22
|
+
end
|
23
|
+
|
24
|
+
def attribute(name, type = :string, options = {})
|
25
|
+
name = name.to_s
|
26
|
+
return if attributes.keys.include?(name)
|
27
|
+
raise UnsupportedAttributeType.new(type) unless TYPES.include?(type)
|
28
|
+
|
29
|
+
attributes[name] = options.update({ :type => type })
|
30
|
+
define_attribute_methods [name]
|
31
|
+
class_eval <<-EOS, __FILE__, __LINE__
|
32
|
+
def #{name}
|
33
|
+
attributes['#{name}']
|
34
|
+
end
|
35
|
+
|
36
|
+
def #{name}=(value)
|
37
|
+
value = coerce_to_type('#{name}', value)
|
38
|
+
#{name}_will_change! unless value == attributes['#{name}']
|
39
|
+
attributes['#{name}'] = value
|
40
|
+
end
|
41
|
+
EOS
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def attributes
|
46
|
+
@attributes ||= Hash[self.class.attributes.keys.zip]
|
47
|
+
end
|
48
|
+
|
49
|
+
def assign_attributes(hash)
|
50
|
+
hash.each do |k, v|
|
51
|
+
setter = "#{k}="
|
52
|
+
send(setter, v) if respond_to?(setter)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def write_attribute(key, value)
|
57
|
+
attributes[key.to_s] = value
|
58
|
+
end
|
59
|
+
|
60
|
+
protected
|
61
|
+
|
62
|
+
def set_sti_type
|
63
|
+
if self.class.sti_child?
|
64
|
+
assign_attributes(type: self.class.name)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def reset_changes
|
69
|
+
@changed_attributes.clear if @changed_attributes
|
70
|
+
end
|
71
|
+
|
72
|
+
def apply_defaults
|
73
|
+
defaults = {}
|
74
|
+
self.class.attributes.each do |attribute, options|
|
75
|
+
defaults[attribute] = options[:default] if options[:default]
|
76
|
+
end
|
77
|
+
assign_attributes(defaults)
|
78
|
+
end
|
79
|
+
|
80
|
+
def coerce_to_string(attribute, value)
|
81
|
+
attribute = attribute.to_s
|
82
|
+
return if value.blank?
|
83
|
+
type = self.class.attributes[attribute][:type]
|
84
|
+
if type == :array || type == :hash
|
85
|
+
MultiJson.encode(value) if value
|
86
|
+
elsif type == :timestamp
|
87
|
+
value.iso8601
|
88
|
+
else
|
89
|
+
value.to_s
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def coerce_to_type(attribute, value)
|
94
|
+
# TODO: allow an attribute to be set to nil
|
95
|
+
return if value.blank?
|
96
|
+
attribute = attribute.to_s
|
97
|
+
type = self.class.attributes[attribute][:type]
|
98
|
+
strict = self.class.attributes[attribute][:strict] != false
|
99
|
+
|
100
|
+
if type == :string
|
101
|
+
value.to_s
|
102
|
+
elsif type == :integer
|
103
|
+
value.to_i
|
104
|
+
elsif type == :float
|
105
|
+
value.to_f
|
106
|
+
elsif type == :timestamp
|
107
|
+
return value if value.kind_of?(Time)
|
108
|
+
Time.parse(value)
|
109
|
+
elsif type == :boolean
|
110
|
+
return true if [true, 'true'].include?(value)
|
111
|
+
return false if [false, 'false'].include?(value)
|
112
|
+
raise AttributeCoercionError.new("'#{value}' cannot be coerced to a :boolean.")
|
113
|
+
elsif type == :array
|
114
|
+
decode_json(value, Array, attribute, strict)
|
115
|
+
elsif type == :hash
|
116
|
+
decode_json(value, Hash, attribute, strict)
|
117
|
+
else
|
118
|
+
value
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def decode_json(value, type, attribute, strict)
|
123
|
+
return value if value.kind_of?(type)
|
124
|
+
begin
|
125
|
+
value = MultiJson.decode(value) if value.kind_of?(String)
|
126
|
+
rescue MultiJson::ParseError
|
127
|
+
raise if strict
|
128
|
+
end
|
129
|
+
if strict
|
130
|
+
return value if value.kind_of?(type)
|
131
|
+
raise AttributeCoercionError.new("Expected #{attribute} to be an #{type}, got #{value.class} instead.")
|
132
|
+
end
|
133
|
+
value
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
data/lib/modis/errors.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
module Modis
|
2
|
+
class ModisError < StandardError; end
|
3
|
+
class RecordNotSaved < ModisError; end
|
4
|
+
class RecordNotFound < ModisError; end
|
5
|
+
class RecordInvalid < ModisError; end
|
6
|
+
class UnsupportedAttributeType < ModisError; end
|
7
|
+
class AttributeCoercionError < ModisError; end
|
8
|
+
|
9
|
+
module Errors
|
10
|
+
def errors
|
11
|
+
@errors ||= ActiveModel::Errors.new(self)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Modis
|
2
|
+
module Finders
|
3
|
+
def self.included(base)
|
4
|
+
base.extend ClassMethods
|
5
|
+
end
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
def find(id)
|
9
|
+
record = attributes_for(id)
|
10
|
+
model_class(record).new(record, new_record: false)
|
11
|
+
end
|
12
|
+
|
13
|
+
def all
|
14
|
+
ids = Modis.redis.smembers(key_for(:all))
|
15
|
+
records = Modis.redis.pipelined do
|
16
|
+
ids.map { |id| Modis.redis.hgetall(key_for(id)) }
|
17
|
+
end
|
18
|
+
records.map do |record|
|
19
|
+
klass = model_class(record)
|
20
|
+
klass.new(record, new_record: false)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def attributes_for(id)
|
25
|
+
if id.nil?
|
26
|
+
raise RecordNotFound, "Couldn't find #{name} without an ID"
|
27
|
+
end
|
28
|
+
values = Modis.redis.hgetall(key_for(id))
|
29
|
+
unless values['id'].present?
|
30
|
+
raise RecordNotFound, "Couldn't find #{name} with id=#{id}"
|
31
|
+
end
|
32
|
+
values
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def model_class(record)
|
38
|
+
return self if record["type"].blank?
|
39
|
+
return record["type"].constantize
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
data/lib/modis/model.rb
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
module Modis
|
2
|
+
module Model
|
3
|
+
def self.included(base)
|
4
|
+
base.instance_eval do
|
5
|
+
include ActiveModel::Dirty
|
6
|
+
include ActiveModel::Validations
|
7
|
+
include ActiveModel::Serialization
|
8
|
+
|
9
|
+
extend ActiveModel::Naming
|
10
|
+
extend ActiveModel::Callbacks
|
11
|
+
|
12
|
+
define_model_callbacks :save
|
13
|
+
define_model_callbacks :create
|
14
|
+
define_model_callbacks :update
|
15
|
+
define_model_callbacks :destroy
|
16
|
+
|
17
|
+
include Modis::Errors
|
18
|
+
include Modis::Transaction
|
19
|
+
include Modis::Persistence
|
20
|
+
include Modis::Finders
|
21
|
+
include Modis::Attributes
|
22
|
+
|
23
|
+
base.extend(ClassMethods)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
module ClassMethods
|
28
|
+
def inherited(child)
|
29
|
+
super
|
30
|
+
bootstrap_sti(self, child)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def initialize(record=nil, options={})
|
35
|
+
set_sti_type
|
36
|
+
apply_defaults
|
37
|
+
assign_attributes(record.symbolize_keys) if record
|
38
|
+
reset_changes
|
39
|
+
|
40
|
+
if options.key?(:new_record)
|
41
|
+
instance_variable_set('@new_record', options[:new_record])
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def ==(other)
|
46
|
+
super || other.instance_of?(self.class) && id.present? && other.id == id
|
47
|
+
end
|
48
|
+
alias :eql? :==
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,162 @@
|
|
1
|
+
module Modis
|
2
|
+
module Persistence
|
3
|
+
def self.included(base)
|
4
|
+
base.extend ClassMethods
|
5
|
+
end
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
# :nodoc:
|
9
|
+
def bootstrap_sti(parent, child)
|
10
|
+
child.instance_eval do
|
11
|
+
parent.instance_eval do
|
12
|
+
class << self
|
13
|
+
attr_accessor :sti_parent
|
14
|
+
end
|
15
|
+
attribute :type, :string
|
16
|
+
end
|
17
|
+
|
18
|
+
class << self
|
19
|
+
delegate :attributes, to: :sti_parent
|
20
|
+
end
|
21
|
+
|
22
|
+
@sti_child = true
|
23
|
+
@sti_parent = parent
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# :nodoc:
|
28
|
+
def sti_child?
|
29
|
+
@sti_child == true
|
30
|
+
end
|
31
|
+
|
32
|
+
def namespace
|
33
|
+
return sti_parent.namespace if sti_child?
|
34
|
+
return @namespace if @namespace
|
35
|
+
@namespace = name.split('::').map(&:underscore).join(':')
|
36
|
+
end
|
37
|
+
|
38
|
+
def namespace=(value)
|
39
|
+
@namespace = value
|
40
|
+
end
|
41
|
+
|
42
|
+
def absolute_namespace
|
43
|
+
parts = [Modis.config.namespace, namespace]
|
44
|
+
@absolute_namespace = parts.compact.join(':')
|
45
|
+
end
|
46
|
+
|
47
|
+
def key_for(id)
|
48
|
+
"#{absolute_namespace}:#{id}"
|
49
|
+
end
|
50
|
+
|
51
|
+
def create(attrs)
|
52
|
+
model = new(attrs)
|
53
|
+
model.save
|
54
|
+
model
|
55
|
+
end
|
56
|
+
|
57
|
+
def create!(attrs)
|
58
|
+
model = new(attrs)
|
59
|
+
model.save!
|
60
|
+
model
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def persisted?
|
65
|
+
true
|
66
|
+
end
|
67
|
+
|
68
|
+
def key
|
69
|
+
new_record? ? nil : self.class.key_for(id)
|
70
|
+
end
|
71
|
+
|
72
|
+
def new_record?
|
73
|
+
defined?(@new_record) ? @new_record : true
|
74
|
+
end
|
75
|
+
|
76
|
+
def save(args={})
|
77
|
+
begin
|
78
|
+
create_or_update(args)
|
79
|
+
rescue Modis::RecordInvalid
|
80
|
+
false
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def save!(args={})
|
85
|
+
create_or_update(args) || (raise RecordNotSaved)
|
86
|
+
end
|
87
|
+
|
88
|
+
def destroy
|
89
|
+
self.class.transaction do
|
90
|
+
run_callbacks :destroy do
|
91
|
+
Modis.redis.del(key)
|
92
|
+
untrack(id)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def reload
|
98
|
+
new_attributes = self.class.attributes_for(id)
|
99
|
+
initialize(new_attributes)
|
100
|
+
self
|
101
|
+
end
|
102
|
+
|
103
|
+
def update_attribute(name, value)
|
104
|
+
assign_attributes(name => value)
|
105
|
+
save(validate: false)
|
106
|
+
end
|
107
|
+
|
108
|
+
def update_attributes(attrs)
|
109
|
+
assign_attributes(attrs)
|
110
|
+
save
|
111
|
+
end
|
112
|
+
|
113
|
+
def update_attributes!(attrs)
|
114
|
+
assign_attributes(attrs)
|
115
|
+
save!
|
116
|
+
end
|
117
|
+
|
118
|
+
protected
|
119
|
+
|
120
|
+
def create_or_update(args={})
|
121
|
+
skip_validate = args.key?(:validate) && args[:validate] == false
|
122
|
+
if !skip_validate && !valid?
|
123
|
+
raise Modis::RecordInvalid, errors.full_messages.join(', ')
|
124
|
+
end
|
125
|
+
|
126
|
+
future = nil
|
127
|
+
set_id if new_record?
|
128
|
+
|
129
|
+
self.class.transaction do
|
130
|
+
run_callbacks :save do
|
131
|
+
callback = new_record? ? :create : :update
|
132
|
+
run_callbacks callback do
|
133
|
+
attrs = []
|
134
|
+
attributes.each { |k, v| attrs << k << coerce_to_string(k, v) }
|
135
|
+
future = Modis.redis.hmset(self.class.key_for(id), attrs)
|
136
|
+
track(id) if new_record?
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
if future && future.value == 'OK'
|
142
|
+
reset_changes
|
143
|
+
@new_record = false
|
144
|
+
true
|
145
|
+
else
|
146
|
+
false
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
def set_id
|
151
|
+
self.id = Modis.redis.incr("#{self.class.absolute_namespace}_id_seq")
|
152
|
+
end
|
153
|
+
|
154
|
+
def track(id)
|
155
|
+
Modis.redis.sadd(self.class.key_for(:all), id)
|
156
|
+
end
|
157
|
+
|
158
|
+
def untrack(id)
|
159
|
+
Modis.redis.srem(self.class.key_for(:all), id)
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
data/lib/modis.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'redis'
|
2
|
+
require 'active_model'
|
3
|
+
require 'active_support/all'
|
4
|
+
require 'multi_json'
|
5
|
+
|
6
|
+
require 'modis/version'
|
7
|
+
require 'modis/configuration'
|
8
|
+
require 'modis/attributes'
|
9
|
+
require 'modis/errors'
|
10
|
+
require 'modis/persistence'
|
11
|
+
require 'modis/transaction'
|
12
|
+
require 'modis/finders'
|
13
|
+
require 'modis/model'
|
14
|
+
|
15
|
+
module Modis
|
16
|
+
@mutex = Mutex.new
|
17
|
+
|
18
|
+
def self.redis
|
19
|
+
return @redis if @redis
|
20
|
+
@mutex.synchronize { @redis = Redis.new }
|
21
|
+
@redis
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.redis=(redis)
|
25
|
+
@redis = redis
|
26
|
+
end
|
27
|
+
end
|
data/lib/tasks/spec.rake
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
begin
|
2
|
+
require 'cane/rake_task'
|
3
|
+
|
4
|
+
desc "Run cane to check quality metrics"
|
5
|
+
Cane::RakeTask.new(:quality) do |cane|
|
6
|
+
cane.add_threshold 'coverage/covered_percent', :>=, 99
|
7
|
+
cane.no_style = false
|
8
|
+
cane.style_measure = 1000
|
9
|
+
cane.no_doc = true
|
10
|
+
cane.abc_max = 25
|
11
|
+
end
|
12
|
+
|
13
|
+
namespace :spec do
|
14
|
+
task :cane => ['spec', 'quality']
|
15
|
+
end
|
16
|
+
rescue LoadError
|
17
|
+
warn "cane not available, quality task not provided."
|
18
|
+
end
|
data/redis_model.gemspec
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'modis/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = "modis"
|
8
|
+
gem.version = Modis::VERSION
|
9
|
+
gem.authors = ["Ian Leitch"]
|
10
|
+
gem.email = ["port001@gmail.com"]
|
11
|
+
gem.description = "ActiveModel + Redis"
|
12
|
+
gem.summary = "ActiveModel + Redis"
|
13
|
+
gem.homepage = ""
|
14
|
+
|
15
|
+
gem.files = `git ls-files`.split($/)
|
16
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
17
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
18
|
+
gem.require_paths = ["lib"]
|
19
|
+
|
20
|
+
gem.add_dependency 'activemodel', '>= 3.0'
|
21
|
+
gem.add_dependency 'activesupport', '>= 3.0'
|
22
|
+
gem.add_dependency 'redis', '>= 3.0'
|
23
|
+
end
|