This repository has been archived by the owner on May 18, 2018. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 9a000bc
Showing
17 changed files
with
636 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
.yardoc | ||
.project | ||
spec/tmp | ||
doc | ||
*.gem |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
--colour |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
source "http://rubygems.org" | ||
|
||
gemspec | ||
|
||
group :test do | ||
gem "rspec" | ||
gem "rspec-rails", :require => false | ||
gem "sqlite3-ruby" | ||
gem "shoulda-matchers" | ||
gem "actionpack", :require => false | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
PATH | ||
remote: . | ||
specs: | ||
preferable (0.1.0) | ||
abstract | ||
activerecord (~> 3.0.0) | ||
activesupport (~> 3.0.0) | ||
|
||
GEM | ||
remote: http://rubygems.org/ | ||
specs: | ||
abstract (1.0.0) | ||
actionpack (3.0.9) | ||
activemodel (= 3.0.9) | ||
activesupport (= 3.0.9) | ||
builder (~> 2.1.2) | ||
erubis (~> 2.6.6) | ||
i18n (~> 0.5.0) | ||
rack (~> 1.2.1) | ||
rack-mount (~> 0.6.14) | ||
rack-test (~> 0.5.7) | ||
tzinfo (~> 0.3.23) | ||
activemodel (3.0.9) | ||
activesupport (= 3.0.9) | ||
builder (~> 2.1.2) | ||
i18n (~> 0.5.0) | ||
activerecord (3.0.9) | ||
activemodel (= 3.0.9) | ||
activesupport (= 3.0.9) | ||
arel (~> 2.0.10) | ||
tzinfo (~> 0.3.23) | ||
activesupport (3.0.9) | ||
arel (2.0.10) | ||
builder (2.1.2) | ||
diff-lcs (1.1.2) | ||
erubis (2.6.6) | ||
abstract (>= 1.0.0) | ||
i18n (0.5.0) | ||
rack (1.2.3) | ||
rack-mount (0.6.14) | ||
rack (>= 1.0.0) | ||
rack-test (0.5.7) | ||
rack (>= 1.0) | ||
railties (3.0.9) | ||
actionpack (= 3.0.9) | ||
activesupport (= 3.0.9) | ||
rake (>= 0.8.7) | ||
rdoc (~> 3.4) | ||
thor (~> 0.14.4) | ||
rake (0.9.2) | ||
rdoc (3.8) | ||
rspec (2.6.0) | ||
rspec-core (~> 2.6.0) | ||
rspec-expectations (~> 2.6.0) | ||
rspec-mocks (~> 2.6.0) | ||
rspec-core (2.6.4) | ||
rspec-expectations (2.6.0) | ||
diff-lcs (~> 1.1.2) | ||
rspec-mocks (2.6.0) | ||
rspec-rails (2.6.1) | ||
actionpack (~> 3.0) | ||
activesupport (~> 3.0) | ||
railties (~> 3.0) | ||
rspec (~> 2.6.0) | ||
shoulda-matchers (1.0.0.beta2) | ||
sqlite3 (1.3.3) | ||
sqlite3-ruby (1.3.3) | ||
sqlite3 (>= 1.3.3) | ||
thor (0.14.6) | ||
tzinfo (0.3.29) | ||
|
||
PLATFORMS | ||
ruby | ||
|
||
DEPENDENCIES | ||
actionpack | ||
preferable! | ||
rspec | ||
rspec-rails | ||
shoulda-matchers | ||
sqlite3-ruby |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
# Preferable | ||
|
||
TODO |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
require 'rake' | ||
require 'rspec/mocks/version' | ||
require 'rspec/core/rake_task' | ||
|
||
RSpec::Core::RakeTask.new(:spec) | ||
|
||
desc 'Default: run specs.' | ||
task :default => :spec |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
require "active_support/core_ext" | ||
require "active_record" | ||
|
||
module Preferable | ||
autoload :Model, "preferable/model" | ||
autoload :Schema, "preferable/schema" | ||
autoload :Field, "preferable/field" | ||
autoload :Set, "preferable/set" | ||
end | ||
|
||
ActiveRecord::Base.class_eval do | ||
include ::Preferable::Model | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
class Preferable::Field | ||
TYPES = [:string, :integer, :float, :boolean, :date, :datetime, :array].to_set.freeze | ||
TRUE_VALUES = [true, 1, '1', 't', 'T', 'true', 'TRUE'].to_set | ||
|
||
attr_reader :name, :type, :options, :default | ||
|
||
# Create a new Field. | ||
# | ||
# Params: | ||
# name: The name symbol | ||
# type: The type symbol, see Preferable::Field::TYPES | ||
# options: The options hash (keys as symbols) | ||
# | ||
# Options: | ||
# default: The default value. | ||
# if: Value assignment proc. Value is only assigned if true is yielded. | ||
# unless: Value assignment proc. Value is only assigned if false is yielded. | ||
# cast: Only relevant for :array type. Specify the type of the array contents. | ||
# | ||
# Examples: | ||
# Field.new :color, :string, :if => lambda {|v| v =~ /^[A-F0-9]{6}$/ } | ||
# Field.new :holidays, :array, :cast => :date | ||
# Field.new :newsletter, :boolean, :default => false | ||
# Field.new :age, :integer, :unless => &:zero? | ||
# | ||
def initialize(name, type, options = {}) | ||
raise ArgumentError, "Unknown type '#{type}', available types are: #{TYPES.map(&:to_s).join(', ')}" unless TYPES.include?(type.to_sym) | ||
|
||
@name = name.to_sym | ||
@type = type.to_sym | ||
@options = options.dup | ||
@default = type_cast @options.delete(:default) | ||
end | ||
|
||
# Returns true if a value is assignable, else false. Assumes given value is already type-casted. | ||
def valid?(value) | ||
result = true | ||
result = options[:if].call(value) if result && options[:if] | ||
result = !options[:unless].call(value) if result && options[:unless] | ||
result | ||
end | ||
|
||
# Opposite of #valid? | ||
def invalid?(value) | ||
!valid?(value) | ||
end | ||
|
||
# Is value equal to the default. . Assumes given value is already type-casted. | ||
def default?(value) | ||
value == default | ||
end | ||
|
||
# Converts a value. | ||
def type_cast(value, to = self.type) | ||
return nil unless value | ||
|
||
case to | ||
when :string | ||
value.to_s | ||
when :integer | ||
value.to_i | ||
when :boolean | ||
TRUE_VALUES.include?(value) | ||
when :datetime | ||
to_time(value) | ||
when :date | ||
to_time(value).try(:to_date) | ||
when :float | ||
value.to_f | ||
when :array | ||
Array.wrap(value).tap do |wrap| | ||
wrap.map! {|item| type_cast(item, options[:cast]) } if options[:cast] | ||
end | ||
else | ||
value | ||
end | ||
end | ||
|
||
private | ||
|
||
def to_time(value) | ||
case value | ||
when Time | ||
value.in_time_zone | ||
when Date | ||
value.beginning_of_day | ||
else | ||
Time.zone.parse(value) rescue nil | ||
end | ||
end | ||
|
||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
# Includable module, for ActiveRecord::Base | ||
module Preferable::Model | ||
extend ActiveSupport::Concern | ||
|
||
included do | ||
class_attribute :_preferable | ||
end | ||
|
||
module ClassMethods | ||
|
||
# Preferable definition for a model. Example: | ||
# | ||
# class User < ActiveRecord::Base | ||
# | ||
# preferable do | ||
# integer :theme_id | ||
# boolean :accessible, :default => false | ||
# string :font_color, :default => "444444", :if => lambda {|value| value =~ /^[A-F0-9]{6}$/ } | ||
# end | ||
# | ||
# end | ||
# | ||
def preferable(&block) | ||
unless _preferable | ||
self._preferable = Preferable::Schema.new | ||
serialize :preferences, Preferable::Set | ||
include PreferableMethods | ||
end | ||
self._preferable.instance_eval(&block) if block | ||
self._preferable | ||
end | ||
|
||
end | ||
|
||
module PreferableMethods | ||
|
||
# Accessor to preferences. Examples: | ||
# | ||
# user = User.find(1) | ||
# user.preferences[:theme_id] # => 8 | ||
# user.preferences[:theme_id] = 3 | ||
# | ||
def preferences | ||
value = read_attribute(:preferences) | ||
value.is_a?(Preferable::Set) ? value : write_attribute(:preferences, Preferable::Set.new(self.class.name)) | ||
end | ||
|
||
# Preferences writer. Updates existing preferences (doesn't replace them!) | ||
def preferences=(hash) | ||
preferences.update(hash) if hash.is_a?(Hash) | ||
preferences | ||
end | ||
|
||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
require 'set' | ||
|
||
class Preferable::Schema < Hash | ||
|
||
def initialize | ||
super | ||
end | ||
|
||
def field(name, type, options = {}) | ||
item = Preferable::Field.new(name, type, options) | ||
self[item.name] = item | ||
end | ||
|
||
Preferable::Field::TYPES.each do |sym| | ||
define_method sym do |name, options| | ||
field name, sym, options | ||
end | ||
end | ||
|
||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
class Preferable::Set < Hash | ||
MODEL_KEY = "_".freeze | ||
PRIVATE = [:rehash, :fetch, :store, :shift, :delete, :delete_if, :keep_if].to_set | ||
|
||
# Make destructive methods private | ||
(public_instance_methods - Hash.superclass.public_instance_methods).each do |m| | ||
PRIVATE.include?(m.to_sym) || m.to_s.ends_with?('!') | ||
end | ||
|
||
def initialize(model_name) | ||
super() | ||
store MODEL_KEY, "::#{model_name}" | ||
end | ||
|
||
def model | ||
@model ||= fetch(MODEL_KEY).constantize | ||
end | ||
|
||
def [](name) | ||
field = find_field(name) | ||
super(field.name) || field.default if field | ||
end | ||
|
||
def []=(name, value) | ||
field = find_field(name) || return | ||
value = field.type_cast(value) | ||
|
||
if value.nil? || field.invalid?(value) || field.default?(value) | ||
delete field.name | ||
else | ||
super field.name, value | ||
end | ||
end | ||
|
||
def set(pairs) | ||
pairs.each {|k, v| self[k] = v } | ||
self | ||
end | ||
|
||
private | ||
|
||
def find_field(name) | ||
model.preferable[name.to_sym] | ||
end | ||
|
||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
# -*- encoding: utf-8 -*- | ||
Gem::Specification.new do |s| | ||
s.platform = Gem::Platform::RUBY | ||
s.required_ruby_version = '>= 1.8.7' | ||
s.required_rubygems_version = ">= 1.3.6" | ||
|
||
s.name = "preferable" | ||
s.summary = "Simple preferences store for ActiveRecord" | ||
s.description = "Great e.g. for storing user preferences" | ||
s.version = '0.1.0' | ||
|
||
s.authors = ["Dimitrij Denissenko"] | ||
s.email = "dimitrij@blacksquaremedia.com" | ||
s.homepage = "https://github.com/bsm/preferable" | ||
|
||
s.require_path = 'lib' | ||
s.files = Dir['LICENSE', 'README.markdown', 'lib/**/*'] | ||
|
||
s.add_dependency "abstract" | ||
s.add_dependency "activerecord", "~> 3.0.0" | ||
s.add_dependency "activesupport", "~> 3.0.0" | ||
end |
Oops, something went wrong.