prefered 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,83 @@
1
+ = Prefered
2
+
3
+ Prefered allows you to add preferences / settings to any model that extends ActiveRecord::Base in a Rails 3 project
4
+
5
+ == Installation
6
+
7
+ === Gem
8
+
9
+ You can let bundler to install Prefered by adding this line to your application's Gemfile:
10
+
11
+ gem 'prefered'
12
+
13
+ And then execute:
14
+
15
+ bundle install
16
+
17
+ Or install it yourself as:
18
+
19
+ gem install prefered
20
+
21
+ === DB
22
+
23
+ Next you will need to run the generator which will create the db migration for you:
24
+
25
+ rails g prefered:preferences_migration
26
+
27
+ Then migrate your db:
28
+
29
+ rake db:migrate
30
+
31
+ == Usage
32
+
33
+ Now your all installed you can add preferences to your active record model, for instance for a User model:
34
+
35
+ class User < ActiveRecord::Base
36
+ has_preferences do
37
+ preference :colour, :default => 'green'
38
+ group :email
39
+ preference :html, :default => false
40
+ end
41
+ end
42
+ end
43
+
44
+ This would add the following to your user model:
45
+
46
+ user.preferences.colour #=> 'green'
47
+ user.preferences.email.html #=> false
48
+
49
+ To change preferences you simply use a setter for each preference:
50
+
51
+ user.preferences.colour= 'yellow'
52
+ user.preferences.email.html= false
53
+
54
+ And to persist them just save the user model:
55
+
56
+ user.save
57
+
58
+ == Thanks
59
+
60
+ Thanks to anyone who gives this a try, peace & love, Rob :)
61
+
62
+ == License
63
+
64
+ Copyright (c) 2010 Robert Oles
65
+
66
+ Permission is hereby granted, free of charge, to any person obtaining
67
+ a copy of this software and associated documentation files (the
68
+ "Software"), to deal in the Software without restriction, including
69
+ without limitation the rights to use, copy, modify, merge, publish,
70
+ distribute, sublicense, and/or sell copies of the Software, and to
71
+ permit persons to whom the Software is furnished to do so, subject to
72
+ the following conditions:
73
+
74
+ The above copyright notice and this permission notice shall be
75
+ included in all copies or substantial portions of the Software.
76
+
77
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
78
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
79
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
80
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
81
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
82
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
83
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,4 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ Dir["#{File.dirname(__FILE__)}/lib/tasks/*.rake"].sort.each { |ext| load ext }
@@ -0,0 +1,26 @@
1
+ require 'rails/generators'
2
+ require 'rails/generators/migration'
3
+
4
+ module Prefered
5
+ module Generators
6
+ class PreferencesMigrationGenerator < Rails::Generators::Base
7
+ include Rails::Generators::Migration
8
+
9
+ def self.source_root
10
+ @source_root ||= File.join(File.dirname(__FILE__), 'templates')
11
+ end
12
+
13
+ def self.next_migration_number(dirname)
14
+ if ActiveRecord::Base.timestamped_migrations
15
+ Time.now.utc.strftime("%Y%m%d%H%M%S")
16
+ else
17
+ "%.3d" % (current_migration_number(dirname) + 1)
18
+ end
19
+ end
20
+
21
+ def generate_migration
22
+ migration_template 'migration.rb', 'db/migrate/create_preferences_table.rb'
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,14 @@
1
+ class CreatePreferencesTable < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :preferences do |t|
4
+ t.string :owner_type
5
+ t.integer :owner_id
6
+ t.text :data
7
+ t.timestamps
8
+ end
9
+ end
10
+
11
+ def self.down
12
+ drop_table :preferences
13
+ end
14
+ end
@@ -0,0 +1,29 @@
1
+ require 'prefered/preference'
2
+
3
+ module Prefered
4
+ class << self
5
+ def included base
6
+ base.extend ClassMethods
7
+ end
8
+ end
9
+
10
+ module ClassMethods
11
+ def has_preferences(&block)
12
+ has_one :preferences, :class_name => "Prefered::Preference", :as => :owner, :autosave => true, :dependent => :destroy
13
+ alias_method :settings, :preferences
14
+ after_initialize do
15
+ build_preferences(:owner => self) unless preferences
16
+ # would prefer to pass block straight to preference model through delegation, however rails assoication_proxy later
17
+ # breaks instance_eval for the block, and the block binding becomes broken, so we have to 'pass' the block through
18
+ # this instance variable
19
+ preferences.instance_variable_set(:@preference_settings_block, block)
20
+ preferences.init_preferences
21
+ end
22
+ end
23
+ alias_method :has_settings, :has_preferences
24
+ end
25
+ end
26
+
27
+ if Object.const_defined?("ActiveRecord")
28
+ ActiveRecord::Base.send(:include, Prefered)
29
+ end
@@ -0,0 +1,118 @@
1
+ class Array
2
+ def to_h(&block)
3
+ Hash[*self.collect { |v|
4
+ [v, block.call(v)]
5
+ }.flatten]
6
+ end
7
+ end
8
+
9
+ module Prefered
10
+ class PreferenceSettings
11
+ class << self
12
+ def evaluate(&block)
13
+ settings = self.new
14
+ settings.instance_eval &block
15
+ settings
16
+ end
17
+ end
18
+
19
+ def initialize
20
+ @preferences = {}
21
+ @groups = {}
22
+ end
23
+ attr_accessor :preferences, :groups
24
+
25
+ def preference(name, options = {})
26
+ @preferences[name.to_sym] = options
27
+ end
28
+
29
+ def group(name, &block)
30
+ @groups[name.to_sym] = PreferenceSettings.evaluate(&block)
31
+ end
32
+ end
33
+
34
+ module PreferenceEngine
35
+ def serialize_preferences(settings)
36
+ preferences_data = settings.preferences.keys.to_h do |preference|
37
+ send(:"#{preference}")
38
+ end
39
+ groups_data = settings.groups.keys.to_h do |group|
40
+ send(:"#{group}").serialize_preferences(settings.groups[group])
41
+ end
42
+ preferences_data.merge(groups_data)
43
+ end
44
+
45
+ def deserialize_preferences(settings, raw_data)
46
+ settings.preferences.keys.each do |preference|
47
+ if raw_data.include?(preference)
48
+ send(:"#{preference}=", raw_data[preference])
49
+ end
50
+ end
51
+ settings.groups.keys.each do |group|
52
+ if raw_data.include?(group)
53
+ send(:"#{group}").deserialize_preferences(settings.groups[group], raw_data[group])
54
+ end
55
+ end
56
+ end
57
+
58
+ private
59
+ def init_methods(settings)
60
+ settings.preferences.each do |preference, options|
61
+ self.instance_variable_set(:"@#{preference}", options[:default])
62
+ self.instance_eval do
63
+ send(:class).send(:attr_accessor, preference)
64
+ end
65
+ end
66
+
67
+ settings.groups.each do |group, group_settings|
68
+ self.instance_variable_set(:"@#{group}", PreferenceGroup.new(group, group_settings))
69
+ self.instance_eval do
70
+ send(:class).send(:attr_reader, group)
71
+ end
72
+ end
73
+ end
74
+ end
75
+
76
+ class PreferenceGroup
77
+ include PreferenceEngine
78
+
79
+ def initialize(name, settings)
80
+ @name = name
81
+ @settings = settings
82
+ init_methods(settings)
83
+ end
84
+ end
85
+
86
+ class Preference < ActiveRecord::Base
87
+ include PreferenceEngine
88
+ serialize :data, Hash
89
+ belongs_to :owner, :polymorphic => true
90
+
91
+ after_initialize :init_data
92
+ before_save :serialize_data
93
+
94
+ def init_preferences
95
+ settings = interpret_settings(&@preference_settings_block) # hack, see prefered.rb
96
+ init_methods(settings)
97
+ deserialize_data(settings)
98
+ end
99
+
100
+ private
101
+ def init_data
102
+ self.data = {} unless self.data
103
+ end
104
+
105
+ def interpret_settings(&block)
106
+ PreferenceSettings.evaluate(&block)
107
+ end
108
+
109
+ def deserialize_data(settings)
110
+ deserialize_preferences(settings, self.data)
111
+ end
112
+
113
+ def serialize_data
114
+ settings = interpret_settings(&@preference_settings_block)
115
+ self.data = serialize_preferences(settings)
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,17 @@
1
+ begin
2
+ require 'jeweler'
3
+ Jeweler::Tasks.new do |s|
4
+ s.name = "prefered"
5
+ s.version = '0.1.0'
6
+ s.summary = "Allows you to add preferences to any active record model"
7
+ s.email = "robertoles@me.com"
8
+ s.homepage = "http://github.com/robertoles/prefered"
9
+ s.description = "Allows you to add preferences to any active record model"
10
+ s.authors = ['Robert Oles']
11
+ s.add_dependency("rails", ">= 3.0.0")
12
+ s.files = FileList["[A-Z]*", "{lib}/**/*"]
13
+ end
14
+ Jeweler::GemcutterTasks.new
15
+ rescue LoadError
16
+ puts "Jeweler not available. Install it with: gem install jeweler"
17
+ end
@@ -0,0 +1,4 @@
1
+ require 'rspec/core'
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
@@ -0,0 +1,52 @@
1
+ require 'spec_helper'
2
+
3
+ describe Prefered::Preference do
4
+ before do
5
+ rebuild_model do
6
+ preference :colour, :default => 'yellow'
7
+ group :sizes do
8
+ preference :width
9
+ preference :height, :default => 20
10
+ end
11
+ end
12
+ end
13
+
14
+ let(:owner) {Dummy.create}
15
+ let(:preference) {owner.preferences}
16
+ subject {preference}
17
+
18
+ it {should belong_to(:owner)}
19
+ it {should serialize(:data)}
20
+
21
+ describe "#colour" do
22
+ its(:colour) {should == 'yellow'}
23
+ end
24
+
25
+ describe "#colour=" do
26
+ before {subject.colour= 'green'}
27
+ its(:colour) {should == 'green'}
28
+ end
29
+
30
+ describe "#group#width" do
31
+ before {subject.sizes.width=10}
32
+ it {subject.sizes.width.should==10}
33
+ end
34
+
35
+ describe "#group#height" do
36
+ it {subject.sizes.height.should==20}
37
+ end
38
+
39
+ context "save and reload" do
40
+ before do
41
+ preference.colour='red'
42
+ preference.sizes.width=200
43
+ preference.sizes.height=400
44
+ preference.save
45
+ end
46
+ subject {Dummy.find(owner.id).preferences}
47
+
48
+ its(:colour) {should == 'red'}
49
+ it {subject.sizes.width.should == 200}
50
+ it {subject.sizes.height.should == 400}
51
+ end
52
+ end
@@ -0,0 +1,20 @@
1
+ require 'spec_helper'
2
+
3
+ describe Prefered do
4
+ before {rebuild_model {}}
5
+
6
+ context "ActiveRecord::Base extension" do
7
+ subject {Dummy}
8
+
9
+ its(:included_modules) {should include(Prefered)}
10
+ it {should respond_to(:has_preferences)}
11
+ it {should respond_to(:has_settings)}
12
+ end
13
+
14
+ context "Instance" do
15
+ subject {Dummy.new}
16
+
17
+ it {should have_one(:preferences)}
18
+ it {should respond_to(:settings)}
19
+ end
20
+ end
@@ -0,0 +1,18 @@
1
+ require 'rubygems'
2
+ require 'rspec'
3
+ require 'shoulda'
4
+
5
+ begin
6
+ require 'ruby-debug'
7
+ rescue LoadError
8
+ puts "ruby-debug not loaded"
9
+ end
10
+
11
+ ROOT = File.join(File.dirname(__FILE__), '..')
12
+
13
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
14
+ Dir["#{File.dirname(__FILE__)}/../lib/**/*.rb"].each {|f| require f}
15
+
16
+ RSpec.configure do |config|
17
+ config.include RSpec::Prefered::ModelHelpers
18
+ end
@@ -0,0 +1,8 @@
1
+ require 'rails'
2
+ require 'active_record'
3
+ require 'active_support'
4
+ require 'sqlite3'
5
+
6
+ config = YAML::load(IO.read(File.dirname(__FILE__) + '/../database.yml'))
7
+ ActiveRecord::Base.logger = Logger.new(File.dirname(__FILE__) + "/debug.log")
8
+ ActiveRecord::Base.establish_connection(config['test'])
@@ -0,0 +1,26 @@
1
+ module RSpec
2
+ module Prefered
3
+ module ModelHelpers
4
+ def rebuild_model(&block)
5
+ ActiveRecord::Base.connection.create_table :dummies, :force => true do |table|
6
+ end
7
+ ActiveRecord::Base.connection.create_table :preferences, :force => true do |table|
8
+ table.column :owner_type, :string
9
+ table.column :owner_id, :integer
10
+ table.column :data, :text
11
+ end
12
+ rebuild_class(&block)
13
+ end
14
+
15
+ def rebuild_class(&block)
16
+ ActiveRecord::Base.send(:include, Prefered)
17
+ Object.send(:remove_const, "Dummy") rescue nil
18
+ Object.const_set("Dummy", Class.new(ActiveRecord::Base))
19
+ Dummy.class_eval do
20
+ include Prefered
21
+ has_preferences &block
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,5 @@
1
+ RSpec::Matchers.define :serialize do |attribute_name|
2
+ match do |target|
3
+ target.class.serialized_attributes.symbolize_keys.include?(attribute_name.to_sym)
4
+ end
5
+ end
metadata ADDED
@@ -0,0 +1,100 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: prefered
3
+ version: !ruby/object:Gem::Version
4
+ hash: 27
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 0
10
+ version: 0.1.0
11
+ platform: ruby
12
+ authors:
13
+ - Robert Oles
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-09-13 00:00:00 +01:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: rails
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 7
30
+ segments:
31
+ - 3
32
+ - 0
33
+ - 0
34
+ version: 3.0.0
35
+ type: :runtime
36
+ version_requirements: *id001
37
+ description: Allows you to add preferences to any active record model
38
+ email: robertoles@me.com
39
+ executables: []
40
+
41
+ extensions: []
42
+
43
+ extra_rdoc_files:
44
+ - README.rdoc
45
+ files:
46
+ - README.rdoc
47
+ - Rakefile
48
+ - lib/generators/prefered/preferences_migration_generator.rb
49
+ - lib/generators/prefered/templates/migration.rb
50
+ - lib/prefered.rb
51
+ - lib/prefered/preference.rb
52
+ - lib/tasks/gem.rake
53
+ - lib/tasks/rspec.rake
54
+ - spec/lib/prefered/preference_spec.rb
55
+ - spec/lib/prefered_spec.rb
56
+ - spec/spec_helper.rb
57
+ - spec/support/initialize_active_record.rb
58
+ - spec/support/macros/model_helpers.rb
59
+ - spec/support/matchers/serialize_matcher.rb
60
+ has_rdoc: true
61
+ homepage: http://github.com/robertoles/prefered
62
+ licenses: []
63
+
64
+ post_install_message:
65
+ rdoc_options:
66
+ - --charset=UTF-8
67
+ require_paths:
68
+ - lib
69
+ required_ruby_version: !ruby/object:Gem::Requirement
70
+ none: false
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ hash: 3
75
+ segments:
76
+ - 0
77
+ version: "0"
78
+ required_rubygems_version: !ruby/object:Gem::Requirement
79
+ none: false
80
+ requirements:
81
+ - - ">="
82
+ - !ruby/object:Gem::Version
83
+ hash: 3
84
+ segments:
85
+ - 0
86
+ version: "0"
87
+ requirements: []
88
+
89
+ rubyforge_project:
90
+ rubygems_version: 1.3.7
91
+ signing_key:
92
+ specification_version: 3
93
+ summary: Allows you to add preferences to any active record model
94
+ test_files:
95
+ - spec/lib/prefered/preference_spec.rb
96
+ - spec/lib/prefered_spec.rb
97
+ - spec/spec_helper.rb
98
+ - spec/support/initialize_active_record.rb
99
+ - spec/support/macros/model_helpers.rb
100
+ - spec/support/matchers/serialize_matcher.rb