prefered 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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