├── test
├── dummy
│ ├── log
│ │ └── .gitkeep
│ ├── app
│ │ ├── mailers
│ │ │ └── .gitkeep
│ │ ├── models
│ │ │ └── .gitkeep
│ │ ├── helpers
│ │ │ ├── application_helper.rb
│ │ │ └── advert_selector_helper.rb
│ │ ├── controllers
│ │ │ ├── examples_controller.rb
│ │ │ └── application_controller.rb
│ │ ├── views
│ │ │ ├── layouts
│ │ │ │ └── application.html.erb
│ │ │ └── examples
│ │ │ │ └── index.html.erb
│ │ └── assets
│ │ │ ├── stylesheets
│ │ │ ├── application.css
│ │ │ └── scaffold.css
│ │ │ └── javascripts
│ │ │ └── application.js
│ ├── lib
│ │ ├── assets
│ │ │ └── .gitkeep
│ │ └── templates
│ │ │ └── erb
│ │ │ └── scaffold
│ │ │ └── _form.html.erb
│ ├── public
│ │ ├── favicon.ico
│ │ ├── 500.html
│ │ ├── 422.html
│ │ └── 404.html
│ ├── test
│ │ ├── fixtures
│ │ └── unit
│ │ │ └── helpers
│ │ │ └── examples_helper_test.rb
│ ├── config.ru
│ ├── config
│ │ ├── environment.rb
│ │ ├── locales
│ │ │ ├── en.yml
│ │ │ └── simple_form.en.yml
│ │ ├── initializers
│ │ │ ├── mime_types.rb
│ │ │ ├── backtrace_silencers.rb
│ │ │ ├── session_store.rb
│ │ │ ├── wrap_parameters.rb
│ │ │ ├── inflections.rb
│ │ │ ├── secret_token.rb
│ │ │ └── simple_form.rb
│ │ ├── routes.rb
│ │ ├── boot.rb
│ │ ├── database.yml
│ │ ├── environments
│ │ │ ├── development.rb
│ │ │ ├── test.rb
│ │ │ └── production.rb
│ │ └── application.rb
│ ├── Rakefile
│ ├── script
│ │ └── rails
│ ├── db
│ │ └── schema.rb
│ └── README.rdoc
├── unit
│ ├── helpers
│ │ └── advert_selector
│ │ │ ├── banners_helper_test.rb
│ │ │ └── placements_helper_test.rb
│ └── advert_selector
│ │ ├── helper_item_test.rb
│ │ ├── placement_test.rb
│ │ └── banner_test.rb
├── advert_selector_test.rb
├── integration
│ ├── navigation_test.rb
│ └── banner_shows_test.rb
├── functional
│ ├── examples_controller_test.rb
│ └── advert_selector
│ │ ├── main_controller_test.rb
│ │ ├── placements_controller_test.rb
│ │ └── banners_controller_test.rb
├── fixtures
│ └── advert_selector
│ │ ├── placements.yml
│ │ ├── helper_items.yml
│ │ └── banners.yml
└── test_helper.rb
├── app
├── assets
│ ├── images
│ │ └── advert_selector
│ │ │ └── .gitkeep
│ ├── javascripts
│ │ └── advert_selector
│ │ │ └── application.js
│ └── stylesheets
│ │ └── advert_selector
│ │ └── application.css
├── views
│ ├── advert_selector
│ │ ├── banners
│ │ │ ├── new.html.erb
│ │ │ ├── show.html.erb
│ │ │ ├── index.html.erb
│ │ │ ├── edit.html.erb
│ │ │ └── _form.html.erb
│ │ ├── placements
│ │ │ ├── new.html.erb
│ │ │ ├── edit.html.erb
│ │ │ ├── show.html.erb
│ │ │ ├── index.html.erb
│ │ │ └── _form.html.erb
│ │ ├── helper_items
│ │ │ ├── new.html.erb
│ │ │ ├── edit.html.erb
│ │ │ ├── show.html.erb
│ │ │ ├── index.html.erb
│ │ │ └── _form.html.erb
│ │ └── main
│ │ │ └── index.html.erb
│ └── layouts
│ │ └── advert_selector
│ │ └── application.html.erb
├── controllers
│ └── advert_selector
│ │ ├── main_controller.rb
│ │ ├── application_controller.rb
│ │ ├── placements_controller.rb
│ │ └── banners_controller.rb
├── models
│ └── advert_selector
│ │ ├── helper_item.rb
│ │ ├── placement.rb
│ │ └── banner.rb
└── helpers
│ └── advert_selector
│ └── application_helper.rb
├── lib
├── advert_selector
│ ├── version.rb
│ ├── errors_cache.rb
│ └── engine.rb
├── tasks
│ └── advert_selector_tasks.rake
└── advert_selector.rb
├── .travis.yml
├── script
├── package
├── rails
└── release
├── .gitignore
├── CHANGELOG.md
├── db
└── migrate
│ ├── 20120924165938_create_advert_selector_placements.rb
│ ├── 20120926132649_create_advert_selector_helper_items.rb
│ └── 20120925122858_create_advert_selector_banners.rb
├── config
└── routes.rb
├── Rakefile
├── Gemfile
├── MIT-LICENSE
├── advert_selector.gemspec
└── README.md
/test/dummy/log/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/test/dummy/app/mailers/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/test/dummy/app/models/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/test/dummy/lib/assets/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/test/dummy/public/favicon.ico:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/assets/images/advert_selector/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/test/dummy/test/fixtures:
--------------------------------------------------------------------------------
1 | ../../fixtures/
--------------------------------------------------------------------------------
/test/dummy/app/helpers/application_helper.rb:
--------------------------------------------------------------------------------
1 | module ApplicationHelper
2 | end
3 |
--------------------------------------------------------------------------------
/lib/advert_selector/version.rb:
--------------------------------------------------------------------------------
1 | module AdvertSelector
2 | VERSION = "3.0.1"
3 | end
4 |
--------------------------------------------------------------------------------
/test/dummy/app/controllers/examples_controller.rb:
--------------------------------------------------------------------------------
1 | class ExamplesController < ApplicationController
2 | def index
3 | end
4 | end
5 |
--------------------------------------------------------------------------------
/test/dummy/test/unit/helpers/examples_helper_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | class ExamplesHelperTest < ActionView::TestCase
4 | end
5 |
--------------------------------------------------------------------------------
/app/views/advert_selector/banners/new.html.erb:
--------------------------------------------------------------------------------
1 |
New banner
2 |
3 | <%= render 'form' %>
4 |
5 | <%= link_to 'Back', banners_path %>
6 |
--------------------------------------------------------------------------------
/lib/tasks/advert_selector_tasks.rake:
--------------------------------------------------------------------------------
1 | # desc "Explaining what the task does"
2 | # task :advert_selector do
3 | # # Task goes here
4 | # end
5 |
--------------------------------------------------------------------------------
/test/dummy/app/controllers/application_controller.rb:
--------------------------------------------------------------------------------
1 | class ApplicationController < ActionController::Base
2 | protect_from_forgery
3 |
4 | end
5 |
--------------------------------------------------------------------------------
/app/views/advert_selector/placements/new.html.erb:
--------------------------------------------------------------------------------
1 | New placement
2 |
3 | <%= render 'form' %>
4 |
5 | <%= link_to 'Back', placements_path %>
6 |
--------------------------------------------------------------------------------
/app/views/advert_selector/helper_items/new.html.erb:
--------------------------------------------------------------------------------
1 | New helper_item
2 |
3 | <%= render 'form' %>
4 |
5 | <%= link_to 'Back', helper_items_path %>
6 |
--------------------------------------------------------------------------------
/test/unit/helpers/advert_selector/banners_helper_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | module AdvertSelector
4 | class BannersHelperTest < ActionView::TestCase
5 | end
6 | end
7 |
--------------------------------------------------------------------------------
/test/dummy/config.ru:
--------------------------------------------------------------------------------
1 | # This file is used by Rack-based servers to start the application.
2 |
3 | require ::File.expand_path('../config/environment', __FILE__)
4 | run Dummy::Application
5 |
--------------------------------------------------------------------------------
/test/unit/helpers/advert_selector/placements_helper_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | module AdvertSelector
4 | class PlacementsHelperTest < ActionView::TestCase
5 | end
6 | end
7 |
--------------------------------------------------------------------------------
/app/views/advert_selector/placements/edit.html.erb:
--------------------------------------------------------------------------------
1 | Editing placement
2 |
3 | <%= render 'form' %>
4 |
5 | <%= link_to 'Show', @placement %> |
6 | <%= link_to 'Back', placements_path %>
7 |
--------------------------------------------------------------------------------
/test/advert_selector_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | class AdvertSelectorTest < ActiveSupport::TestCase
4 | test "truth" do
5 | assert_kind_of Module, AdvertSelector
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/test/dummy/config/environment.rb:
--------------------------------------------------------------------------------
1 | # Load the rails application
2 | require File.expand_path('../application', __FILE__)
3 |
4 | # Initialize the rails application
5 | Dummy::Application.initialize!
6 |
--------------------------------------------------------------------------------
/app/views/advert_selector/helper_items/edit.html.erb:
--------------------------------------------------------------------------------
1 | Editing helper_item
2 |
3 | <%= render 'form' %>
4 |
5 | <%= link_to 'Show', @helper_item %> |
6 | <%= link_to 'Back', helper_items_path %>
7 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | rvm:
2 | - 2.4.3
3 | # - 2.5.0
4 | before_script:
5 | - 'RAILS_ENV=test bundle exec rake db:create db:schema:load --trace'
6 | env:
7 | - "RAILS_VERSION=5.0.6"
8 | - "RAILS_VERSION=5.1.2"
9 |
--------------------------------------------------------------------------------
/script/package:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | # Usage: script/package
3 | # Updates the gemspec and builds a new gem in the pkg directory.
4 |
5 | mkdir -p pkg
6 | chmod -R a+r *
7 | gem build *.gemspec
8 | mv *.gem pkg
9 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .bundle/
2 | log/*.log
3 | pkg/
4 | test/dummy/db/*.sqlite3
5 | test/dummy/log/*.log
6 | test/dummy/tmp/
7 | test/dummy/.sass-cache
8 |
9 | .idea
10 | .rvmrc
11 |
12 | *.gem
13 | .bundle
14 | Gemfile.lock
15 |
--------------------------------------------------------------------------------
/test/integration/navigation_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | class NavigationTest < ActionDispatch::IntegrationTest
4 | # fixtures :all
5 |
6 | # test "the truth" do
7 | # assert true
8 | # end
9 | end
10 |
11 |
--------------------------------------------------------------------------------
/test/unit/advert_selector/helper_item_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | module AdvertSelector
4 | class HelperItemTest < ActiveSupport::TestCase
5 | # test "the truth" do
6 | # assert true
7 | # end
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/test/dummy/Rakefile:
--------------------------------------------------------------------------------
1 | # Add your own tasks in files placed in lib/tasks ending in .rake,
2 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
3 |
4 | require_relative 'config/application'
5 |
6 | Rails.application.load_tasks
--------------------------------------------------------------------------------
/test/dummy/config/locales/en.yml:
--------------------------------------------------------------------------------
1 | # Sample localization file for English. Add more files in this directory for other locales.
2 | # See https://github.com/svenfuchs/rails-i18n/tree/master/rails%2Flocale for starting points.
3 |
4 | en:
5 | hello: "Hello world"
6 |
--------------------------------------------------------------------------------
/test/dummy/config/initializers/mime_types.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Add new mime types for use in respond_to blocks:
4 | # Mime::Type.register "text/richtext", :rtf
5 | # Mime::Type.register_alias "text/html", :iphone
6 |
--------------------------------------------------------------------------------
/test/dummy/config/routes.rb:
--------------------------------------------------------------------------------
1 | Rails.application.routes.draw do
2 | #get "examples/index"
3 |
4 | mount AdvertSelector::Engine => "/advert_selector"
5 |
6 | # match '(:action)' => 'examples', via: [:get, :post]
7 | match '/' => 'examples#index', via: [:get, :post]
8 |
9 | end
10 |
--------------------------------------------------------------------------------
/test/dummy/config/boot.rb:
--------------------------------------------------------------------------------
1 | require 'rubygems'
2 | gemfile = File.expand_path('../../../../Gemfile', __FILE__)
3 |
4 | if File.exist?(gemfile)
5 | ENV['BUNDLE_GEMFILE'] = gemfile
6 | require 'bundler'
7 | Bundler.setup
8 | end
9 |
10 | $:.unshift File.expand_path('../../../../lib', __FILE__)
--------------------------------------------------------------------------------
/test/functional/examples_controller_test.rb:
--------------------------------------------------------------------------------
1 | require File.dirname(__FILE__) + '/../test_helper'
2 |
3 | class ExamplesControllerTest < ActionController::TestCase
4 |
5 | test "render video once per session" do
6 | get :index
7 | assert_response :success
8 |
9 | #binding.pry
10 |
11 | end
12 |
13 | end
14 |
--------------------------------------------------------------------------------
/test/dummy/script/rails:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | # This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application.
3 |
4 | APP_PATH = File.expand_path('../../config/application', __FILE__)
5 | require File.expand_path('../../config/boot', __FILE__)
6 | require 'rails/commands'
7 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## 3.0.1 (2018-03-20)
2 |
3 | - Rails 5 support, dropped Rails 4 support
4 |
5 | ## 2.0.1 (2015-05-15)
6 |
7 | - Gemspec updates, see https://github.com/holli/advert_selector/issues/8 and https://github.com/holli/advert_selector/pull/10
8 |
9 | ## 2.0.0 (2014-07-29)
10 |
11 | - Rails 4 support (dropped Rails 3 officially)
12 |
13 |
14 |
--------------------------------------------------------------------------------
/script/rails:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | # This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application.
3 |
4 | ENGINE_ROOT = File.expand_path('../..', __FILE__)
5 | ENGINE_PATH = File.expand_path('../../lib/advert_selector/engine', __FILE__)
6 |
7 | require 'rails/all'
8 | require 'rails/engine/commands'
9 |
--------------------------------------------------------------------------------
/app/controllers/advert_selector/main_controller.rb:
--------------------------------------------------------------------------------
1 | require_dependency "advert_selector/application_controller"
2 |
3 | module AdvertSelector
4 | class MainController < ApplicationController
5 | def index
6 | end
7 |
8 | def clear_errors_log
9 | AdvertSelector::ErrorsCache.clear
10 | redirect_to :action => :index
11 | end
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/db/migrate/20120924165938_create_advert_selector_placements.rb:
--------------------------------------------------------------------------------
1 | class CreateAdvertSelectorPlacements < ActiveRecord::Migration
2 | def change
3 | create_table :advert_selector_placements do |t|
4 | t.string :name, :null => false
5 | t.boolean :only_once_per_session
6 | t.text :conflicting_placements_array
7 |
8 | t.timestamps
9 | end
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/test/fixtures/advert_selector/placements.yml:
--------------------------------------------------------------------------------
1 | # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/Fixtures.html
2 |
3 | parade:
4 | name: Parade
5 | conflicting_placements_array: leaderboard,video
6 |
7 | leaderboard:
8 | name: Leaderboard
9 | conflicting_placements_array: parade
10 |
11 | video:
12 | name: Video
13 | only_once_per_session: true
14 | conflicting_placements_array: parade
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/test/dummy/lib/templates/erb/scaffold/_form.html.erb:
--------------------------------------------------------------------------------
1 | <%%= simple_form_for(@<%= singular_table_name %>) do |f| %>
2 | <%%= f.error_notification %>
3 |
4 |
5 | <%- attributes.each do |attribute| -%>
6 | <%%= f.<%= attribute.reference? ? :association : :input %> :<%= attribute.name %> %>
7 | <%- end -%>
8 |
9 |
10 |
11 | <%%= f.button :submit %>
12 |
13 | <%% end %>
14 |
--------------------------------------------------------------------------------
/config/routes.rb:
--------------------------------------------------------------------------------
1 | AdvertSelector::Engine.routes.draw do
2 |
3 |
4 | get "/" => "main#index", :as => :main
5 | get "/clear_errors_log" => "main#clear_errors_log", :as => :clear_errors_log
6 |
7 |
8 | resources :banners do
9 | #resources :helper_items
10 | put 'update_running_view_count', :on => :member
11 | end
12 |
13 | resources :placements
14 |
15 |
16 | #match '/' => redirect {|params, request| '/advert_selector/placements' }
17 | end
18 |
--------------------------------------------------------------------------------
/test/dummy/config/initializers/backtrace_silencers.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces.
4 | # Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ }
5 |
6 | # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code.
7 | # Rails.backtrace_cleaner.remove_silencers!
8 |
--------------------------------------------------------------------------------
/test/dummy/config/initializers/session_store.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | Dummy::Application.config.session_store :cookie_store, :key => '_dummy_session'
4 |
5 | # Use the database for sessions instead of the cookie-based default,
6 | # which shouldn't be used to store highly confidential information
7 | # (create the session table with "rails generate session_migration")
8 | # Dummy::Application.config.session_store :active_record_store
9 |
--------------------------------------------------------------------------------
/app/views/advert_selector/placements/show.html.erb:
--------------------------------------------------------------------------------
1 | <%= notice %>
2 |
3 |
4 | Name:
5 | <%= @placement.name %>
6 |
7 |
8 |
9 | Development code:
10 | <%= @placement.development_code %>
11 |
12 |
13 |
14 | Conflicting placements array:
15 | <%= @placement.conflicting_placements_array %>
16 |
17 |
18 |
19 | <%= link_to 'Edit', edit_placement_path(@placement) %> |
20 | <%= link_to 'Back', placements_path %>
21 |
--------------------------------------------------------------------------------
/lib/advert_selector/errors_cache.rb:
--------------------------------------------------------------------------------
1 | module AdvertSelector
2 | class ErrorsCache
3 | def self.cache_key
4 | 'advert_selector_errors'
5 | end
6 |
7 | def self.errors
8 | arr = Rails.cache.read(cache_key)
9 | arr.blank? ? [] : arr.first(10)
10 | end
11 |
12 | def self.add(str)
13 | Rails.cache.write(cache_key, errors.push(str))
14 | end
15 |
16 | def self.clear()
17 | Rails.cache.write(cache_key, [])
18 | end
19 |
20 | end
21 | end
22 |
--------------------------------------------------------------------------------
/lib/advert_selector/engine.rb:
--------------------------------------------------------------------------------
1 | module AdvertSelector
2 | class Engine < ::Rails::Engine
3 | isolate_namespace AdvertSelector
4 |
5 | config.to_prepare do
6 | ::ApplicationController.helper(AdvertSelector::ApplicationHelper)
7 | end
8 |
9 | #initializer 'advert_selector.action_controller' do |app|
10 | # ActiveSupport.on_load :action_controller do
11 | # helper AdvertSelector::PlacementsHelper
12 | # end
13 | #end
14 | end
15 | end
16 |
17 | #ActionView::Base.send :include, AdvertSelector::PlacementsHelper
18 |
19 |
--------------------------------------------------------------------------------
/test/dummy/config/initializers/wrap_parameters.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 | #
3 | # This file contains settings for ActionController::ParamsWrapper which
4 | # is enabled by default.
5 |
6 | # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array.
7 | ActiveSupport.on_load(:action_controller) do
8 | wrap_parameters :format => [:json]
9 | end
10 |
11 | # Disable root element in JSON by default.
12 | ActiveSupport.on_load(:active_record) do
13 | self.include_root_in_json = false
14 | end
15 |
--------------------------------------------------------------------------------
/test/dummy/app/views/layouts/application.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Dummy
5 |
6 |
7 |
8 |
9 | <%= stylesheet_link_tag "application", :media => "all" %>
10 | <%= javascript_include_tag "application" %>
11 | <%= csrf_meta_tags %>
12 |
13 |
14 |
15 |
16 |
17 | <%= yield %>
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/db/migrate/20120926132649_create_advert_selector_helper_items.rb:
--------------------------------------------------------------------------------
1 | class CreateAdvertSelectorHelperItems < ActiveRecord::Migration
2 | def change
3 | create_table :advert_selector_helper_items do |t|
4 | t.integer :banner_id
5 |
6 | #t.references :master, :polymorphic => true
7 |
8 | t.integer :position
9 | t.string :name
10 | t.boolean :content_for
11 | t.text :content
12 |
13 | t.timestamps
14 | end
15 |
16 | add_index(:advert_selector_helper_items, [:banner_id, :position], :name => 'index_banner_position')
17 | end
18 | end
19 |
--------------------------------------------------------------------------------
/test/dummy/app/helpers/advert_selector_helper.rb:
--------------------------------------------------------------------------------
1 | module AdvertSelectorHelper
2 |
3 | def advert_selector_targeting_age?(age_string)
4 | age_string == 'adult'
5 | #age_string == 'senior'
6 | end
7 |
8 | def advert_selector_raise_error(placement)
9 | 1/0
10 | end
11 |
12 | def advert_selector_leaderboard_testing(placement)
13 | content_for(:content_test) do
14 | "advert_selector_leaderboard_testing_content"
15 | end
16 | return true
17 | end
18 |
19 | def advert_selector_always_false(placement)
20 | return false
21 | end
22 |
23 | end
24 |
--------------------------------------------------------------------------------
/app/views/advert_selector/helper_items/show.html.erb:
--------------------------------------------------------------------------------
1 | <%= notice %>
2 |
3 |
4 | Master:
5 | <%= @helper_item.master_id %>
6 |
7 |
8 |
9 | Master type:
10 | <%= @helper_item.master_type %>
11 |
12 |
13 |
14 | Name:
15 | <%= @helper_item.name %>
16 |
17 |
18 |
19 | Content for:
20 | <%= @helper_item.content_for %>
21 |
22 |
23 |
24 | Content:
25 | <%= @helper_item.content %>
26 |
27 |
28 |
29 | <%= link_to 'Edit', edit_helper_item_path(@helper_item) %> |
30 | <%= link_to 'Back', helper_items_path %>
31 |
--------------------------------------------------------------------------------
/app/models/advert_selector/helper_item.rb:
--------------------------------------------------------------------------------
1 | module AdvertSelector
2 | class HelperItem < ActiveRecord::Base
3 | # attr_accessible :master_id, :master_type, :name, :position, :content_for, :content
4 |
5 | #belongs_to :master, :polymorphic => true
6 | #acts_as_list :scope => [:master_id, :master_type]
7 |
8 | belongs_to :banner, :class_name => 'AdvertSelector::Banner', inverse_of: :helper_items
9 | acts_as_list :scope => :banner_id
10 |
11 |
12 | def name_sym
13 | @name_sym ||= name.downcase.to_sym
14 | end
15 |
16 | def blank?
17 | name.blank?
18 | end
19 |
20 | end
21 | end
22 |
--------------------------------------------------------------------------------
/test/dummy/config/initializers/inflections.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Add new inflection rules using the following format
4 | # (all these examples are active by default):
5 | # ActiveSupport::Inflector.inflections do |inflect|
6 | # inflect.plural /^(ox)$/i, '\1en'
7 | # inflect.singular /^(ox)en/i, '\1'
8 | # inflect.irregular 'person', 'people'
9 | # inflect.uncountable %w( fish sheep )
10 | # end
11 | #
12 | # These inflection rules are supported but not enabled by default:
13 | # ActiveSupport::Inflector.inflections do |inflect|
14 | # inflect.acronym 'RESTful'
15 | # end
16 |
--------------------------------------------------------------------------------
/script/release:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | # Usage: script/release
3 | # Build the package, tag a commit, push it to origin, and then release the
4 | # package publicly.
5 |
6 | set -e
7 |
8 | version="$(script/package | grep Version: | awk '{print $2}')"
9 | [ -n "$version" ] || exit 1
10 |
11 | echo $version
12 | git commit --allow-empty -a -m "Release $version"
13 | git tag "v$version"
14 | git push origin
15 | git push origin "v$version"
16 | echo "NOT YET PUSHING GEM AUTOMATICALLY, RUN: gem push pkg/*-${version}.gem"
17 | echo "NOT YET PUSHING GEM AUTOMATICALLY, RUN: gem push pkg/*-${version}.gem"
18 | # gem push pkg/*-${version}.gem
19 |
--------------------------------------------------------------------------------
/test/dummy/app/assets/stylesheets/application.css:
--------------------------------------------------------------------------------
1 | /*
2 | * This is a manifest file that'll be compiled into application.css, which will include all the files
3 | * listed below.
4 | *
5 | * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
6 | * or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path.
7 | *
8 | * You're free to add application-wide styles to this file and they'll appear at the top of the
9 | * compiled file, but it's generally better to create a new file per style scope.
10 | *
11 | *= require_self
12 | *= require_tree .
13 | */
14 |
--------------------------------------------------------------------------------
/app/views/advert_selector/main/index.html.erb:
--------------------------------------------------------------------------------
1 | Advert Selector Main
2 |
3 | <% unless AdvertSelector::ErrorsCache.errors.blank? %>
4 |
5 |
Latest errors - <%= link_to 'Clear errors log.', clear_errors_log_url %>
6 |
7 | <% AdvertSelector::ErrorsCache.errors.each do |error| %>
8 | <%= simple_format(h(error)) %>
9 | <% end %>
10 |
11 |
12 | <% else %>
13 |
14 | No errors currently.
15 |
16 |
17 | <% end %>
18 |
19 |
20 | Find me in app/views/advert_selector/main/index.html.erb
21 |
--------------------------------------------------------------------------------
/test/fixtures/advert_selector/helper_items.yml:
--------------------------------------------------------------------------------
1 | coke_content:
2 | banner: coke
3 | name: banner_leaderboard
4 | content_for: true
5 | content: " "
6 | position: 1
7 |
8 | pepsi_only_param_true:
9 | banner: pepsi
10 | name: request_params_include?
11 | content_for: false
12 | content: pepsi=true
13 |
14 | pepsi_content:
15 | banner: pepsi
16 | position: 2
17 | name: banner_leaderboard
18 | content_for: true
19 | content: " "
20 |
21 | parade_content:
22 | banner: parade_banner
23 | position: 1
24 | name: banner_parade
25 | content_for: true
26 | content: " "
27 |
28 |
29 |
--------------------------------------------------------------------------------
/test/dummy/app/views/examples/index.html.erb:
--------------------------------------------------------------------------------
1 | Examples#index
2 |
3 | Find me in app/views/examples/index.html.erb
4 |
5 | <%= link_to 'nopriority', '/' %> - <%= link_to 'priority=true', '/?priority=true' %>
6 |
7 |
8 |
9 |
10 | <%= advert_selector_initialize %>
11 |
12 |
13 |
14 | content_for :banner_leaderboard
15 |
16 | <%= content_for(:banner_leaderboard) %>
17 |
18 |
19 | content_for :banner_parade
20 |
21 | <%= content_for(:banner_parade) %>
22 |
23 |
24 |
25 | content_for :content_test
26 |
27 | <%= content_for(:content_test) %>
28 |
29 |
30 |
31 |
32 |
33 |
34 | <%= advert_selector_force_test_infos %>
--------------------------------------------------------------------------------
/app/assets/javascripts/advert_selector/application.js:
--------------------------------------------------------------------------------
1 | // This is a manifest file that'll be compiled into application.js, which will include all the files
2 | // listed below.
3 | //
4 | // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
5 | // or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
6 | //
7 | // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
8 | // the compiled file.
9 | //
10 | // WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD
11 | // GO AFTER THE REQUIRES BELOW.
12 | //
13 | //
14 | //= require_tree .
15 |
--------------------------------------------------------------------------------
/test/dummy/config/database.yml:
--------------------------------------------------------------------------------
1 | # SQLite version 3.x
2 | # gem install sqlite3
3 | #
4 | # Ensure the SQLite 3 gem is defined in your Gemfile
5 | # gem 'sqlite3'
6 | development:
7 | adapter: sqlite3
8 | database: db/development.sqlite3
9 | pool: 5
10 | timeout: 5000
11 |
12 | # Warning: The database defined as "test" will be erased and
13 | # re-generated from your development database when you run "rake".
14 | # Do not set this db to the same as development or production.
15 | test:
16 | adapter: sqlite3
17 | database: db/test.sqlite3
18 | pool: 5
19 | timeout: 5000
20 |
21 | production:
22 | adapter: sqlite3
23 | database: db/production.sqlite3
24 | pool: 5
25 | timeout: 5000
26 |
--------------------------------------------------------------------------------
/app/controllers/advert_selector/application_controller.rb:
--------------------------------------------------------------------------------
1 | module AdvertSelector
2 | class ApplicationController < ActionController::Base
3 |
4 | before_action :admin_access_only
5 | before_action :set_time_zone
6 | before_action :set_locale
7 |
8 | def admin_access_only
9 | if AdvertSelector.admin_access_class.send(:admin_access, self)
10 | return true
11 | else
12 | render :plain => "Forbidden, only for admins", :status => 403
13 | return false
14 | end
15 | end
16 |
17 | def set_time_zone
18 | Time.zone = AdvertSelector.default_time_zone
19 | end
20 |
21 | def set_locale
22 | I18n.locale = :en
23 | end
24 |
25 | end
26 | end
27 |
--------------------------------------------------------------------------------
/test/dummy/config/initializers/secret_token.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Your secret key for verifying the integrity of signed cookies.
4 | # If you change this key, all old signed cookies will become invalid!
5 | # Make sure the secret is at least 30 characters and all random,
6 | # no regular words or you'll be exposed to dictionary attacks.
7 | Dummy::Application.config.secret_token = '42fb79fe9baf04a90a6caf05f98d4950b157d77f5ca4079104f4c5378607c705f60ac5421670f2384948a0860a6a5d7c41ce5e8015f7830c0d3290726d716808'
8 | Dummy::Application.config.secret_key_base = '7d5bb216d35a2613ff87254b811a64ccb8debcf172677f8111873da5b4b02b8169aba8fedf900cf21bf8d988914cf114d5848fbcf132484aef59f9a910d3d48c'
9 |
--------------------------------------------------------------------------------
/test/dummy/app/assets/javascripts/application.js:
--------------------------------------------------------------------------------
1 | // This is a manifest file that'll be compiled into application.js, which will include all the files
2 | // listed below.
3 | //
4 | // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
5 | // or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
6 | //
7 | // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
8 | // the compiled file.
9 | //
10 | // WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD
11 | // GO AFTER THE REQUIRES BELOW.
12 | //
13 | //= require jquery
14 | //= require jquery_ujs
15 | //= require_tree .
16 |
--------------------------------------------------------------------------------
/app/views/advert_selector/placements/index.html.erb:
--------------------------------------------------------------------------------
1 | Listing placements
2 |
3 |
4 |
5 | Name
6 | Conflicting placements array
7 |
8 |
9 |
10 |
11 |
12 | <% @placements.each do |placement| %>
13 |
14 | <%= placement.name %>
15 | <%= placement.conflicting_placements_array %>
16 | <%= link_to 'Show', placement %>
17 | <%= link_to 'Edit', edit_placement_path(placement) %>
18 | <%= link_to 'Destroy', placement, :method => :delete, :data => { :confirm => 'Are you sure?' } %>
19 |
20 | <% end %>
21 |
22 |
23 |
24 |
25 | <%= link_to 'New Placement', new_placement_path %>
26 |
--------------------------------------------------------------------------------
/lib/advert_selector.rb:
--------------------------------------------------------------------------------
1 | require 'acts_as_list'
2 | require 'simple_form'
3 |
4 | require "advert_selector/engine"
5 | require "advert_selector/errors_cache"
6 |
7 | module AdvertSelector
8 |
9 | mattr_accessor :default_banner_test_url
10 | mattr_accessor :default_time_zone
11 | mattr_accessor :admin_access_class
12 |
13 | self.default_banner_test_url = "http://localhost:3000/?"
14 | self.default_time_zone = 'UTC'
15 |
16 | class AdminAccessClassDefault
17 | def self.admin_access(controller)
18 | Rails.env.development?
19 | end
20 | end
21 | class AdminAccessClassAlwaysTrue
22 | def self.admin_access(controller)
23 | true
24 | end
25 | end
26 |
27 | self.admin_access_class = AdminAccessClassDefault
28 |
29 | end
30 |
--------------------------------------------------------------------------------
/test/dummy/public/500.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | We're sorry, but something went wrong (500)
5 |
17 |
18 |
19 |
20 |
21 |
22 |
We're sorry, but something went wrong.
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/test/dummy/public/422.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | The change you wanted was rejected (422)
5 |
17 |
18 |
19 |
20 |
21 |
22 |
The change you wanted was rejected.
23 |
Maybe you tried to change something you didn't have access to.
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
1 | begin
2 | require 'bundler/setup'
3 | rescue LoadError
4 | puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5 | end
6 |
7 | require 'rdoc/task'
8 |
9 | RDoc::Task.new(:rdoc) do |rdoc|
10 | rdoc.rdoc_dir = 'rdoc'
11 | rdoc.title = 'AdvertSelector'
12 | rdoc.options << '--line-numbers'
13 | rdoc.rdoc_files.include('README.rdoc')
14 | rdoc.rdoc_files.include('lib/**/*.rb')
15 | end
16 |
17 | APP_RAKEFILE = File.expand_path("../test/dummy/Rakefile", __FILE__)
18 | load 'rails/tasks/engine.rake'
19 |
20 |
21 | load 'rails/tasks/statistics.rake'
22 |
23 |
24 |
25 | require 'bundler/gem_tasks'
26 |
27 | require 'rake/testtask'
28 |
29 | Rake::TestTask.new(:test) do |t|
30 | t.libs << 'test'
31 | t.pattern = 'test/**/*_test.rb'
32 | t.verbose = false
33 | end
34 |
35 |
36 | task default: :test
37 |
--------------------------------------------------------------------------------
/test/dummy/public/404.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | The page you were looking for doesn't exist (404)
5 |
17 |
18 |
19 |
20 |
21 |
22 |
The page you were looking for doesn't exist.
23 |
You may have mistyped the address or the page may have moved.
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/test/dummy/config/locales/simple_form.en.yml:
--------------------------------------------------------------------------------
1 | en:
2 | simple_form:
3 | "yes": 'Yes'
4 | "no": 'No'
5 | required:
6 | text: 'required'
7 | mark: '*'
8 | # You can uncomment the line below if you need to overwrite the whole required html.
9 | # When using html, text and mark won't be used.
10 | # html: '* '
11 | error_notification:
12 | default_message: "Please review the problems below:"
13 | # Labels and hints examples
14 | # labels:
15 | # defaults:
16 | # password: 'Password'
17 | # user:
18 | # new:
19 | # email: 'E-mail to sign in.'
20 | # edit:
21 | # email: 'E-mail.'
22 | # hints:
23 | # defaults:
24 | # username: 'User name to sign in.'
25 | # password: 'No special characters, please.'
26 |
27 |
--------------------------------------------------------------------------------
/app/views/advert_selector/banners/show.html.erb:
--------------------------------------------------------------------------------
1 | <%= notice %>
2 |
3 |
4 | Name:
5 | <%= @banner.name %>
6 |
7 |
8 |
9 | Start time:
10 | <%= @banner.start_time %>
11 |
12 |
13 |
14 | End time:
15 | <%= @banner.end_time %>
16 |
17 |
18 |
19 | Target view count:
20 | <%= @banner.target_view_count %>
21 |
22 |
23 |
24 | Frequency:
25 | <%= @banner.frequency %>
26 |
27 |
28 |
29 | Delay requests:
30 | <%= @banner.delay_requests %>
31 |
32 |
33 |
34 | Comment:
35 | <%= @banner.comment %>
36 |
37 |
38 |
39 | Confirmed:
40 | <%= @banner.confirmed %>
41 |
42 |
43 |
44 | Placement:
45 | <%= @banner.placement_id %>
46 |
47 |
48 |
49 | <%= link_to 'Edit', edit_banner_path(@banner) %> |
50 | <%= link_to 'Back', banners_path %>
51 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/advert_selector/application.css:
--------------------------------------------------------------------------------
1 | /*
2 | * This is a manifest file that'll be compiled into application.css, which will include all the files
3 | * listed below.
4 | *
5 | * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
6 | * or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path.
7 | *
8 | * You're free to add application-wide styles to this file and they'll appear at the top of the
9 | * compiled file, but it's generally better to create a new file per style scope.
10 | *
11 | *= require_self
12 | *= require_tree .
13 | */
14 |
15 |
16 |
17 | body {
18 | padding-top: 60px;
19 | padding-bottom: 40px;
20 | }
21 | .sidebar-nav {
22 | padding: 9px 0;
23 | }
24 |
25 | .form-horizontal .control-group { margin-bottom: 10px; }
26 |
27 | label, input, button, select, textarea { font-size: 12px !important; }
28 |
29 |
30 |
--------------------------------------------------------------------------------
/app/views/advert_selector/helper_items/index.html.erb:
--------------------------------------------------------------------------------
1 | Listing helper_items
2 |
3 |
4 |
5 | Master
6 | Master type
7 | Name
8 | Content for
9 | Content
10 |
11 |
12 |
13 |
14 |
15 | <% @helper_items.each do |helper_item| %>
16 |
17 | <%= helper_item.master_id %>
18 | <%= helper_item.master_type %>
19 | <%= helper_item.name %>
20 | <%= helper_item.content_for %>
21 | <%= helper_item.content %>
22 | <%= link_to 'Show', helper_item %>
23 | <%= link_to 'Edit', edit_helper_item_path(helper_item) %>
24 | <%= link_to 'Destroy', helper_item, :method => :delete, :data => { :confirm => 'Are you sure?' } %>
25 |
26 | <% end %>
27 |
28 |
29 |
30 |
31 | <%= link_to 'New Helper item', new_helper_item_path %>
32 |
--------------------------------------------------------------------------------
/db/migrate/20120925122858_create_advert_selector_banners.rb:
--------------------------------------------------------------------------------
1 | class CreateAdvertSelectorBanners < ActiveRecord::Migration
2 | def change
3 | create_table :advert_selector_banners do |t|
4 | t.string :name, :null => false
5 | t.datetime :start_time
6 | t.datetime :end_time
7 | t.integer :priority, :null => false, :default => 0
8 | t.integer :target_view_count
9 | t.integer :running_view_count, :null => false, :default => 0
10 | t.integer :frequency
11 | t.boolean :fast_mode, :null => false, :default => false
12 | t.text :comment
13 | t.boolean :confirmed, :null => false, :default => false
14 | t.integer :placement_id, :null => false
15 | #t.boolean :in_serve_cache, :null => false, :default => false
16 |
17 | t.timestamps
18 | end
19 |
20 | #add_index(:advert_selector_banners, [:in_serve_cache, :priority])
21 | add_index(:advert_selector_banners, [:end_time])
22 | end
23 | end
24 |
--------------------------------------------------------------------------------
/test/functional/advert_selector/main_controller_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | module AdvertSelector
4 | class MainControllerTest < ActionController::TestCase
5 |
6 | setup do
7 | AdvertSelector.admin_access_class = AdvertSelector::AdminAccessClassAlwaysTrue
8 | @routes = AdvertSelector::Engine.routes # This would be same as calling get :index, :use_route => :advert_selector
9 | end
10 |
11 | test "should get index" do
12 | get :index
13 | assert_response :success
14 | end
15 |
16 | test "should clear errors log" do
17 | AdvertSelector::ErrorsCache.add('str')
18 | get :clear_errors_log
19 | assert_response :redirect
20 | assert AdvertSelector::ErrorsCache.errors.blank?
21 | end
22 |
23 | test "forbidden with default access" do
24 | AdvertSelector.admin_access_class = AdvertSelector::AdminAccessClassDefault
25 | get :index
26 | assert_response 403
27 | end
28 |
29 | end
30 | end
31 |
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | source "http://rubygems.org"
2 |
3 | # Declare your gem's dependencies in advert_selector.gemspec.
4 | # Bundler will treat runtime dependencies like base dependencies, and
5 | # development dependencies will be added by default to the :development group.
6 | gemspec
7 |
8 | # jquery-rails is used by the dummy application
9 | # gem "jquery-rails"
10 |
11 | # Declare any dependencies that are still in development here instead of in
12 | # your gemspec. These might include edge Rails or gems from your path or
13 | # Git. Remember to move these dependencies to your gemspec before releasing
14 | # your gem to rubygems.org.
15 |
16 | #gem 'pry'
17 | gem 'timecop'
18 | gem 'mocha', :require => false
19 | gem 'rails-controller-testing'
20 |
21 |
22 | # For travis testing
23 | # http://schneems.com/post/50991826838/testing-against-multiple-rails-versions
24 | rails_version = ENV["RAILS_VERSION"] || "default"
25 |
26 | case rails_version
27 | when "default"
28 | gem "rails"
29 | else
30 | gem "rails", "~> #{rails_version}"
31 | end
32 |
33 |
--------------------------------------------------------------------------------
/MIT-LICENSE:
--------------------------------------------------------------------------------
1 | Copyright 2012 YOURNAME
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining
4 | a copy of this software and associated documentation files (the
5 | "Software"), to deal in the Software without restriction, including
6 | without limitation the rights to use, copy, modify, merge, publish,
7 | distribute, sublicense, and/or sell copies of the Software, and to
8 | permit persons to whom the Software is furnished to do so, subject to
9 | the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be
12 | included in all copies or substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/app/views/advert_selector/banners/index.html.erb:
--------------------------------------------------------------------------------
1 | Listing banners
2 |
3 |
4 |
5 | Name
6 | Start time
7 | End time
8 | Target view count
9 | Frequency
10 | Delay requests
11 | Comment
12 | Confirmed
13 | Placement
14 |
15 |
16 |
17 |
18 |
19 | <% @banners.each do |banner| %>
20 |
21 | <%= banner.name %>
22 | <%= banner.start_time %>
23 | <%= banner.end_time %>
24 | <%= banner.target_view_count %>
25 | <%= banner.frequency %>
26 | <%= banner.comment %>
27 | <%= banner.confirmed %>
28 | <%= banner.placement_id %>
29 | <%= link_to 'Show', banner %>
30 | <%= link_to 'Edit', edit_banner_path(banner) %>
31 | <%= link_to 'Destroy', banner, :method => :delete, :data => { :confirm => 'Are you sure?' } %>
32 |
33 | <% end %>
34 |
35 |
36 |
37 |
38 | <%= link_to 'New Banner', new_banner_path %>
39 |
--------------------------------------------------------------------------------
/app/views/advert_selector/helper_items/_form.html.erb:
--------------------------------------------------------------------------------
1 | <%= form_for(@helper_item) do |f| %>
2 | <% if @helper_item.errors.any? %>
3 |
4 |
<%= pluralize(@helper_item.errors.count, "error") %> prohibited this helper_item from being saved:
5 |
6 |
7 | <% @helper_item.errors.full_messages.each do |msg| %>
8 | <%= msg %>
9 | <% end %>
10 |
11 |
12 | <% end %>
13 |
14 |
15 | <%= f.label :master_id %>
16 | <%= f.number_field :master_id %>
17 |
18 |
19 | <%= f.label :master_type %>
20 | <%= f.text_field :master_type %>
21 |
22 |
23 | <%= f.label :name %>
24 | <%= f.text_field :name %>
25 |
26 |
27 | <%= f.label :content_for %>
28 | <%= f.check_box :content_for %>
29 |
30 |
31 | <%= f.label :content %>
32 | <%= f.text_area :content %>
33 |
34 |
35 | <%= f.submit %>
36 |
37 | <% end %>
38 |
--------------------------------------------------------------------------------
/app/views/advert_selector/placements/_form.html.erb:
--------------------------------------------------------------------------------
1 | <%= simple_form_for(@placement, :html => {:class => 'form-horizontal'}) do |f| %>
2 |
3 | <%= f.error_notification %>
4 |
5 |
6 |
7 | <%= f.input :name %>
8 |
9 | <%= f.input :only_once_per_session %>
10 |
11 | <%= f.input :conflicting_placements_array, :input_html => {:rows => 5} %>
12 |
13 |
14 |
Conflicting links:
15 |
16 | <% @placement.conflicting_placements.each do |name| %>
17 |
18 | <% if place = AdvertSelector::Placement.by_name(name).first %>
19 | <%= link_to place.name, place %>
20 | <% else %>
21 | <%= name %> - NOT FOUND
22 | <% end %>
23 |
24 | <% end %>
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 | <%= f.button :submit %>
37 |
38 |
39 | <% end %>
40 |
41 |
--------------------------------------------------------------------------------
/test/dummy/config/environments/development.rb:
--------------------------------------------------------------------------------
1 | Dummy::Application.configure do
2 | # Settings specified here will take precedence over those in config/application.rb
3 |
4 | # In the development environment your application's code is reloaded on
5 | # every request. This slows down response time but is perfect for development
6 | # since you don't have to restart the web server when you make code changes.
7 | config.cache_classes = false
8 |
9 | # Show full error reports and disable caching
10 | config.consider_all_requests_local = true
11 | config.action_controller.perform_caching = false
12 |
13 | # Don't care if the mailer can't send
14 | config.action_mailer.raise_delivery_errors = false
15 |
16 | # Print deprecation notices to the Rails logger
17 | config.active_support.deprecation = :stderr
18 |
19 | # Only use best-standards-support built into browsers
20 | config.action_dispatch.best_standards_support = :builtin
21 |
22 | # Do not compress assets
23 | config.assets.compress = false
24 |
25 | # Expands the lines which load the assets
26 | config.assets.debug = true
27 |
28 | config.eager_load = false
29 | end
30 |
--------------------------------------------------------------------------------
/test/fixtures/advert_selector/banners.yml:
--------------------------------------------------------------------------------
1 | # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/Fixtures.html
2 |
3 | coke:
4 | placement: leaderboard
5 | name: Coke
6 | start_time: <%= 5.days.ago.at_beginning_of_day.to_s(:db) %>
7 | end_time: <%= 5.days.from_now.at_beginning_of_day.to_s(:db) %>
8 | target_view_count: 1000
9 | running_view_count: 100
10 | frequency: nil
11 | fast_mode: false
12 | comment: "Coke banner"
13 | confirmed: true
14 | priority: 50
15 |
16 | pepsi:
17 | placement: leaderboard
18 | name: Pepsi
19 | start_time: <%= Time.now.at_beginning_of_day.to_s(:db) %>
20 | end_time: <%= 10.days.from_now.at_beginning_of_day.to_s(:db) %>
21 | target_view_count: 1000
22 | frequency: nil
23 | comment: "Pepsi banner"
24 | fast_mode: true
25 | confirmed: true
26 | priority: 100
27 |
28 | parade_banner:
29 | placement: parade
30 | name: parade_banner
31 | start_time: <%= Time.now.at_beginning_of_day.to_s(:db) %>
32 | end_time: <%= 10.days.from_now.at_beginning_of_day.to_s(:db) %>
33 | target_view_count: 1000
34 | frequency: 1
35 | fast_mode: true
36 | confirmed: false
37 | priority: 1000
38 |
39 |
--------------------------------------------------------------------------------
/test/dummy/app/assets/stylesheets/scaffold.css:
--------------------------------------------------------------------------------
1 | body { background-color: #fff; color: #333; }
2 |
3 | body, p, ol, ul, td {
4 | font-family: verdana, arial, helvetica, sans-serif;
5 | font-size: 13px;
6 | line-height: 18px;
7 | }
8 |
9 | pre {
10 | background-color: #eee;
11 | padding: 10px;
12 | font-size: 11px;
13 | }
14 |
15 | a { color: #000; }
16 | a:visited { color: #666; }
17 | a:hover { color: #fff; background-color:#000; }
18 |
19 | div.field, div.actions {
20 | margin-bottom: 10px;
21 | }
22 |
23 | #notice {
24 | color: green;
25 | }
26 |
27 | .field_with_errors {
28 | padding: 2px;
29 | background-color: red;
30 | display: table;
31 | }
32 |
33 | #error_explanation {
34 | width: 450px;
35 | border: 2px solid red;
36 | padding: 7px;
37 | padding-bottom: 0;
38 | margin-bottom: 20px;
39 | background-color: #f0f0f0;
40 | }
41 |
42 | #error_explanation h2 {
43 | text-align: left;
44 | font-weight: bold;
45 | padding: 5px 5px 5px 15px;
46 | font-size: 12px;
47 | margin: -7px;
48 | margin-bottom: 0px;
49 | background-color: #c00;
50 | color: #fff;
51 | }
52 |
53 | #error_explanation ul li {
54 | font-size: 12px;
55 | list-style: square;
56 | }
57 |
--------------------------------------------------------------------------------
/app/views/advert_selector/banners/edit.html.erb:
--------------------------------------------------------------------------------
1 | Banner <%= @banner.name %> - <%= link_to('Duplicate banner', new_banner_url(:duplicate_id => @banner.id)) %>
2 |
3 | <% unless @banner.target_view_count.nil? %>
4 | Current view count: <%= @banner.running_view_count %> / <%= @banner.target_view_count %>
5 | |
6 | <% end %>
7 |
8 |
9 |
10 | Test banner in url :
11 |
12 | ">
13 | Test
14 |
15 |
16 |
17 | <%= render 'form' %>
18 |
19 |
20 |
21 |
22 | <%= simple_form_for(@banner, :url => update_running_view_count_banner_url(@banner), :html => {:class => 'form-inline'}) do |f| %>
23 |
24 | Running view count:
25 |
26 |
27 | <%= f.button :submit, 'Change running view count' %>
28 |
29 | <% end %>
30 |
--------------------------------------------------------------------------------
/advert_selector.gemspec:
--------------------------------------------------------------------------------
1 | $:.push File.expand_path("../lib", __FILE__)
2 |
3 | # Maintain your gem's version:
4 | require "advert_selector/version"
5 |
6 | # Describe your gem and declare its dependencies:
7 | Gem::Specification.new do |s|
8 | s.name = "advert_selector"
9 | s.version = AdvertSelector::VERSION
10 | s.authors = ["Olli Huotari"]
11 | s.email = ["olli.huotari@iki.fi"]
12 | s.homepage = "https://github.com/holli/advert_selector/"
13 | s.licenses = ['MIT']
14 | s.summary = "Rails adserver tool for selecting a smaller subset of banners from all possible banners with differing banner placement combinations."
15 | s.description = "Rails adserver tool for selecting a smaller subset of banners from all possible banners with differing banner placement combinations. Gem includes admin tools for handling banners in live environment. Includes basic targeting, viewcount, frequency etc setups."
16 |
17 | s.files = Dir["{app,config,db,lib}/**/*"] + ["MIT-LICENSE", "Rakefile", "README.md"]
18 | s.test_files = Dir["test/**/*"]
19 |
20 | s.add_dependency "rails", ">= 5"
21 | s.add_dependency "simple_form", ">= 3"
22 | s.add_dependency "acts_as_list", ">= 0.6"
23 |
24 | s.add_development_dependency "sqlite3", ">= 1.3"
25 | #s.add_development_dependency 'rails-controller-testing'
26 | #s.add_development_dependency "mocha"
27 | #s.add_development_dependency "timecop"
28 |
29 | end
30 |
--------------------------------------------------------------------------------
/test/dummy/config/environments/test.rb:
--------------------------------------------------------------------------------
1 | Dummy::Application.configure do
2 | # Settings specified here will take precedence over those in config/application.rb
3 |
4 | # The test environment is used exclusively to run your application's
5 | # test suite. You never need to work with it otherwise. Remember that
6 | # your test database is "scratch space" for the test suite and is wiped
7 | # and recreated between test runs. Don't rely on the data there!
8 | config.cache_classes = true
9 |
10 | # Configure static asset server for tests with Cache-Control for performance
11 | config.public_file_server.enabled = false
12 |
13 | config.public_file_server.headers = { 'Cache-Control' => 'public, max-age=3600' }
14 |
15 | # Show full error reports and disable caching
16 | config.consider_all_requests_local = true
17 | config.action_controller.perform_caching = false
18 |
19 | # Raise exceptions instead of rendering exception templates
20 | config.action_dispatch.show_exceptions = false
21 |
22 | # Disable request forgery protection in test environment
23 | config.action_controller.allow_forgery_protection = false
24 |
25 | # Tell Action Mailer not to deliver emails to the real world.
26 | # The :test delivery method accumulates sent emails in the
27 | # ActionMailer::Base.deliveries array.
28 | config.action_mailer.delivery_method = :test
29 |
30 | # Print deprecation notices to the stderr
31 | # config.active_support.deprecation = :stderr
32 | # config.active_support.deprecation = :debug
33 | config.active_support.deprecation = :log
34 |
35 | config.eager_load = false
36 | config.active_support.test_order = :random
37 |
38 | end
39 |
--------------------------------------------------------------------------------
/app/views/advert_selector/banners/_form.html.erb:
--------------------------------------------------------------------------------
1 | <%= simple_form_for(@banner, :html => {:class => 'form-horizontal'}) do |f| %>
2 | <%= f.error_notification %>
3 |
4 |
5 |
6 |
7 | <%= f.input :name %>
8 |
9 | <%= f.input :start_time, :as => 'string', :input_html => {:value => (@banner.start_time ? @banner.start_time.iso8601 : '')} %>
10 | <%= f.input :end_time, :as => 'string', :input_html => {:value => (@banner.end_time ? @banner.end_time.iso8601 : '')} %>
11 | <%= f.input :priority %>
12 | <%= f.input :target_view_count %>
13 | <%= f.input :fast_mode %>
14 | <%= f.input :frequency %>
15 | <%= f.input :comment, :input_html => {:rows => 5} %>
16 | <%= f.input :confirmed, :label_html => {:style => ((@banner.new_record? || @banner.confirmed?) ? '' : 'color: red; font-weight: bold')} %>
17 | <%= f.association :placement %>
18 |
19 |
20 |
21 |
22 | <% @banner.helper_items.build
23 | @banner.helper_items.build if @banner.helper_items.size < 2
24 | %>
25 | <%= f.fields_for :helper_items do |f_hi| %>
26 |
27 | <%= f_hi.input :name, :label => 'HelperItem Name' %>
28 | <%= f_hi.input :content_for %>
29 | <%= f_hi.input :position %>
30 | <%= f_hi.input :content, :input_html => {:rows => (f_hi.object.content_for? ? 5 : 1) } %>
31 |
32 | <% end %>
33 |
34 |
35 |
36 |
37 |
38 | <%= f.button :submit %>
39 |
40 |
41 |
42 | <% end %>
43 |
--------------------------------------------------------------------------------
/test/unit/advert_selector/placement_test.rb:
--------------------------------------------------------------------------------
1 | require File.dirname(__FILE__) + '/../../test_helper'
2 | #require 'test_helper'
3 |
4 | module AdvertSelector
5 | class PlacementTest < ActiveSupport::TestCase
6 |
7 | setup do
8 | @parade = advert_selector_placements(:parade)
9 | @leaderboard = advert_selector_placements(:leaderboard)
10 | end
11 |
12 | test "name_sym" do
13 | assert_equal :parade, @parade.name_sym
14 | end
15 |
16 | test "conflicting_placements" do
17 | assert_equal 'leaderboard,video', @parade.conflicting_placements_array
18 | assert_equal [:parade, :leaderboard, :video], @parade.conflicting_placements
19 | end
20 |
21 | test "conflicting_placements string handling" do
22 | arr = Placement.conflicting_placements("a, b c,a,d")
23 | assert_equal [:a,:b,:c,:d], arr
24 | end
25 |
26 | test "conflicting_with" do
27 | assert @parade.conflicting_with?(@parade), "should conflict with itself"
28 |
29 | assert @parade.conflicting_placements_array.include?("leaderboard"), "wrong setup"
30 | assert @parade.conflicting_with?(@leaderboard)
31 |
32 | dummy = Placement.new(:name => 'test1')
33 | assert !@parade.conflicting_with?([dummy])
34 | assert @parade.conflicting_with?([dummy, @leaderboard])
35 | end
36 |
37 | test "conflicting_placements_array=" do
38 | assert_equal 'leaderboard,video', @parade.conflicting_placements_array
39 | @parade.conflicting_placements_array = 'video,not_found'
40 | assert @parade.save
41 |
42 | new_parade = Placement.find(@parade.id)
43 | assert_equal [:parade, :not_found, :video], new_parade.conflicting_placements
44 |
45 | new_leaderboard = Placement.find(@leaderboard.id)
46 | assert new_leaderboard.conflicting_placements_array.blank?
47 | end
48 |
49 | end
50 | end
51 |
--------------------------------------------------------------------------------
/test/test_helper.rb:
--------------------------------------------------------------------------------
1 |
2 | # Configure Rails Environment
3 | ENV["RAILS_ENV"] = "test"
4 | require File.expand_path("../../test/dummy/config/environment.rb", __FILE__)
5 | ActiveRecord::Migrator.migrations_paths = [File.expand_path("../../test/dummy/db/migrate", __FILE__)]
6 | ActiveRecord::Migrator.migrations_paths << File.expand_path('../../db/migrate', __FILE__)
7 | require "rails/test_help"
8 | require "mocha/setup"
9 |
10 |
11 | # Filter out Minitest backtrace while allowing backtrace from other libraries
12 | # to be shown.
13 | Minitest.backtrace_filter = Minitest::BacktraceFilter.new
14 |
15 |
16 |
17 |
18 | # Load fixtures from the engine
19 | if ActiveSupport::TestCase.respond_to?(:fixture_path=)
20 | ActiveSupport::TestCase.fixture_path = File.expand_path("../fixtures", __FILE__)
21 | ActionDispatch::IntegrationTest.fixture_path = ActiveSupport::TestCase.fixture_path
22 | ActiveSupport::TestCase.file_fixture_path = ActiveSupport::TestCase.fixture_path + "/files"
23 | ActiveSupport::TestCase.fixtures :all
24 | end
25 |
26 |
27 |
28 | class ActiveSupport::TestCase
29 |
30 | # Note: You'll currently still have to declare fixtures explicitly in integration tests
31 | # -- they do not yet inherit this setting
32 | fixtures :all
33 |
34 | setup do
35 |
36 | $advert_selector_banners_load_time = nil # force reload of banners in every tests
37 | @coke = advert_selector_banners(:coke)
38 | @pepsi = advert_selector_banners(:pepsi)
39 | @parade_banner = advert_selector_banners(:parade_banner)
40 |
41 | Timecop.return
42 | Timecop.travel( Time.now.at_midnight + 12.hours ) unless [6..20].include?(Time.now.hour)
43 | end
44 |
45 | teardown do
46 | Timecop.return
47 | $advert_selector_avoid_cache = false
48 | @coke.reset_cache
49 | @pepsi.reset_cache
50 | @parade_banner.reset_cache
51 | AdvertSelector::ErrorsCache.clear
52 | end
53 |
54 | end
--------------------------------------------------------------------------------
/test/functional/advert_selector/placements_controller_test.rb:
--------------------------------------------------------------------------------
1 | require File.dirname(__FILE__) + '/../../test_helper'
2 |
3 | module AdvertSelector
4 | class PlacementsControllerTest < ActionController::TestCase
5 | # fixtures :all
6 |
7 | setup do
8 | AdvertSelector.admin_access_class = AdvertSelector::AdminAccessClassAlwaysTrue
9 | @routes = AdvertSelector::Engine.routes # This would be same as calling get :index, :use_route => :advert_selector
10 |
11 | @placement = advert_selector_placements(:leaderboard)
12 | end
13 |
14 | test "should get index" do
15 | get :index
16 | assert_response :success
17 | assert_not_nil assigns(:placements)
18 | end
19 |
20 | test "should get new" do
21 | get :new
22 | assert_response :success
23 | end
24 |
25 | test "should create placement" do
26 | assert_difference('Placement.count') do
27 | #post :create, :placement => { :conflicting_placements_array => @placement.conflicting_placements_array, :name => @placement.name }
28 | post :create, params: {:placement => { :name => "new placement" }}
29 | end
30 |
31 | assert_redirected_to placement_path(assigns(:placement))
32 | end
33 |
34 | test "should show placement" do
35 | get :show, params: {:id => @placement}
36 | assert_response :redirect
37 | end
38 |
39 | test "should get edit" do
40 | get :edit, params: {:id => @placement}
41 | assert_response :success
42 | end
43 |
44 | test "should update placement" do
45 | put :update, params: {:id => @placement, :placement => { :name => @placement.name }}
46 | #assert_redirected_to placement_path(assigns(:placement))
47 | assert_redirected_to placement_path(assigns(:placement))
48 | end
49 |
50 | test "should destroy placement" do
51 | assert_difference('Placement.count', -1) do
52 | delete :destroy, params: {:id => @placement}
53 | end
54 |
55 | assert_redirected_to placements_path
56 | end
57 | end
58 | end
59 |
--------------------------------------------------------------------------------
/test/functional/advert_selector/banners_controller_test.rb:
--------------------------------------------------------------------------------
1 | require File.dirname(__FILE__) + '/../../test_helper'
2 |
3 | module AdvertSelector
4 | class BannersControllerTest < ActionController::TestCase
5 | setup do
6 | AdvertSelector.admin_access_class = AdvertSelector::AdminAccessClassAlwaysTrue
7 | @routes = AdvertSelector::Engine.routes # This would be same as calling get :index, :use_route => :advert_selector
8 |
9 | @banner = advert_selector_banners(:pepsi)
10 | end
11 |
12 | test "should get index" do
13 | get :index
14 | assert_response :success
15 | assert_not_nil assigns(:banners)
16 | end
17 |
18 | test "should get new" do
19 | get :new
20 | assert_response :success
21 | end
22 |
23 | test "should create banner" do
24 | assert_difference('Banner.count') do
25 | post :create, params: {:banner => { :comment => @banner.comment, :end_time => @banner.end_time, :frequency => @banner.frequency, :name => @banner.name, :placement_id => @banner.placement_id, :start_time => @banner.start_time, :target_view_count => @banner.target_view_count }}
26 | end
27 |
28 | assert_redirected_to banner_path(assigns(:banner))
29 | end
30 |
31 | test "should show banner" do
32 | get :show, params: {:id => @banner}
33 | assert_response :redirect
34 | end
35 |
36 | test "should get edit" do
37 | get :edit, params: {:id => @banner}
38 | assert_response :success
39 | end
40 |
41 | test "should update banner" do
42 | put :update, params: {:id => @banner, :banner => { :comment => @banner.comment }}
43 | assert_redirected_to banner_path(assigns(:banner))
44 | end
45 |
46 | test "should update banner running view count" do
47 | @banner.add_one_viewcount
48 | @banner.save
49 | put :update_running_view_count, params: {:id => @banner, :banner => { :running_view_count => 99 }}
50 | assert_response :redirect
51 | assert_equal 99, @banner.running_view_count
52 | assert_equal 99, AdvertSelector::Banner.find(@banner.id)[:running_view_count]
53 |
54 | end
55 |
56 | test "should destroy banner" do
57 | assert_difference('Banner.count', -1) do
58 | delete :destroy, params: {:id => @banner}
59 | end
60 |
61 | assert_redirected_to banners_path
62 | end
63 | end
64 | end
65 |
--------------------------------------------------------------------------------
/test/dummy/config/application.rb:
--------------------------------------------------------------------------------
1 | require File.expand_path('../boot', __FILE__)
2 |
3 | require 'rails/all'
4 |
5 | Bundler.require
6 | require "advert_selector"
7 |
8 | module Dummy
9 | class Application < Rails::Application
10 | # Settings in config/environments/* take precedence over those specified here.
11 | # Application configuration should go into files in config/initializers
12 | # -- all .rb files in that directory are automatically loaded.
13 |
14 | # Custom directories with classes and modules you want to be autoloadable.
15 | # config.autoload_paths += %W(#{config.root}/extras)
16 |
17 | # Only load the plugins named here, in the order given (default is alphabetical).
18 | # :all can be used as a placeholder for all plugins not explicitly named.
19 | # config.plugins = [ :exception_notification, :ssl_requirement, :all ]
20 |
21 | # Activate observers that should always be running.
22 | # config.active_record.observers = :cacher, :garbage_collector, :forum_observer
23 |
24 | # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
25 | # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.
26 | # config.time_zone = 'Central Time (US & Canada)'
27 |
28 | # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
29 | # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
30 | # config.i18n.default_locale = :de
31 |
32 | # Configure the default encoding used in templates for Ruby 1.9.
33 | config.encoding = "utf-8"
34 |
35 | # Configure sensitive parameters which will be filtered from the log file.
36 | config.filter_parameters += [:password]
37 |
38 | # Enable escaping HTML in JSON.
39 | config.active_support.escape_html_entities_in_json = true
40 |
41 | # Use SQL instead of Active Record's schema dumper when creating the database.
42 | # This is necessary if your schema can't be completely dumped by the schema dumper,
43 | # like if you have constraints or database-specific column types
44 | # config.active_record.schema_format = :sql
45 |
46 | # Enable the asset pipeline
47 | config.assets.enabled = true
48 |
49 | # Version of your assets, change this if you want to expire all your assets
50 | config.assets.version = '1.0'
51 | end
52 | end
53 |
54 |
--------------------------------------------------------------------------------
/app/models/advert_selector/placement.rb:
--------------------------------------------------------------------------------
1 | module AdvertSelector
2 | class Placement < ActiveRecord::Base
3 | #attr_accessible :conflicting_placements_array, :name, :comment, :request_delay, :only_once_per_session
4 |
5 | has_many :banners, :inverse_of => :placement
6 |
7 | scope :by_name, lambda {|name| where("LOWER(name) = ?", name.to_s.downcase)}
8 |
9 | def name_sym
10 | @name_sym ||= name.to_s.downcase.to_sym unless name.blank?
11 | end
12 |
13 |
14 | def conflicting_with?(placement_syms)
15 | placement_syms = [placement_syms.name_sym] if placement_syms.is_a?(Placement)
16 | placement_syms = placement_syms.collect{|plac| plac.name_sym} if placement_syms.first.is_a?(Placement)
17 |
18 | !(placement_syms & conflicting_placements).empty?
19 | end
20 |
21 | def self.conflicting_placements(string)
22 | string.to_s.split(/[ ,]/).collect{|name| name.strip}.reject{|name| name.blank?}.collect{|name| name.to_sym}.sort_by{|sym| sym.to_s}.uniq
23 | end
24 |
25 | def conflicting_placements
26 | @conflicting_placements ||= ([self.name_sym] + Placement.conflicting_placements(conflicting_placements_array))
27 | end
28 |
29 | def conflicting_placements_array=(string)
30 | string = string.join(",") if string.is_a?(Array)
31 | arr = Placement.conflicting_placements( string )
32 | arr.delete(name_sym)
33 | self[:conflicting_placements_array] = arr.join(",")
34 | end
35 |
36 | after_save :after_save_update_conflicting_placements_info
37 | def after_save_update_conflicting_placements_info
38 | if conflicting_placements_array_changed?
39 | saved_org = Placement.conflicting_placements(conflicting_placements_array_change.first)
40 | saved_new = Placement.conflicting_placements(conflicting_placements_array_change.last)
41 |
42 | (saved_org-saved_new).each do |name|
43 | if placement = Placement.by_name(name).first
44 | placement.conflicting_placements_array = placement.conflicting_placements.reject{|var| var == name_sym}
45 | placement.save if placement.conflicting_placements_array_changed?
46 | end
47 | end
48 | saved_new.each do |name|
49 | if placement = Placement.where(:name => name).first
50 | placement.conflicting_placements_array = placement.conflicting_placements.push(name_sym)
51 | placement.save if placement.conflicting_placements_array_changed?
52 | end
53 | end
54 | end
55 | end
56 |
57 | end
58 | end
59 |
--------------------------------------------------------------------------------
/test/dummy/db/schema.rb:
--------------------------------------------------------------------------------
1 | # encoding: UTF-8
2 | # This file is auto-generated from the current state of the database. Instead
3 | # of editing this file, please use the migrations feature of Active Record to
4 | # incrementally modify your database, and then regenerate this schema definition.
5 | #
6 | # Note that this schema.rb definition is the authoritative source for your
7 | # database schema. If you need to create the application database on another
8 | # system, you should be using db:schema:load, not running all the migrations
9 | # from scratch. The latter is a flawed and unsustainable approach (the more migrations
10 | # you'll amass, the slower it'll run and the greater likelihood for issues).
11 | #
12 | # It's strongly recommended to check this file into your version control system.
13 |
14 | ActiveRecord::Schema.define(:version => 20120926132649) do
15 |
16 | create_table "advert_selector_banners", :force => true do |t|
17 | t.string "name", :null => false
18 | t.datetime "start_time"
19 | t.datetime "end_time"
20 | t.integer "priority", :default => 0, :null => false
21 | t.integer "target_view_count"
22 | t.integer "running_view_count", :default => 0, :null => false
23 | t.integer "frequency"
24 | t.boolean "fast_mode", :default => false, :null => false
25 | t.text "comment"
26 | t.boolean "confirmed", :default => false, :null => false
27 | t.integer "placement_id", :null => false
28 | t.datetime "created_at", :null => false
29 | t.datetime "updated_at", :null => false
30 | end
31 |
32 | add_index "advert_selector_banners", ["end_time"], :name => "index_advert_selector_banners_on_end_time"
33 |
34 | create_table "advert_selector_helper_items", :force => true do |t|
35 | t.integer "banner_id"
36 | t.integer "position"
37 | t.string "name"
38 | t.boolean "content_for"
39 | t.text "content"
40 | t.datetime "created_at", :null => false
41 | t.datetime "updated_at", :null => false
42 | end
43 |
44 | add_index "advert_selector_helper_items", ["banner_id", "position"], :name => "index_banner_position"
45 |
46 | create_table "advert_selector_placements", :force => true do |t|
47 | t.string "name", :null => false
48 | t.boolean "only_once_per_session"
49 | t.text "conflicting_placements_array"
50 | t.datetime "created_at", :null => false
51 | t.datetime "updated_at", :null => false
52 | end
53 |
54 | end
55 |
--------------------------------------------------------------------------------
/test/dummy/config/environments/production.rb:
--------------------------------------------------------------------------------
1 | Dummy::Application.configure do
2 | # Settings specified here will take precedence over those in config/application.rb
3 |
4 | # Code is not reloaded between requests
5 | config.cache_classes = true
6 |
7 | # Full error reports are disabled and caching is turned on
8 | config.consider_all_requests_local = false
9 | config.action_controller.perform_caching = true
10 |
11 | # Disable Rails's static asset server (Apache or nginx will already do this)
12 | config.serve_static_assets = false
13 |
14 | # Compress JavaScripts and CSS
15 | config.assets.compress = true
16 |
17 | # Don't fallback to assets pipeline if a precompiled asset is missed
18 | config.assets.compile = false
19 |
20 | # Generate digests for assets URLs
21 | config.assets.digest = true
22 |
23 | # Defaults to nil and saved in location specified by config.assets.prefix
24 | # config.assets.manifest = YOUR_PATH
25 |
26 | # Specifies the header that your server uses for sending files
27 | # config.action_dispatch.x_sendfile_header = "X-Sendfile" # for apache
28 | # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for nginx
29 |
30 | # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
31 | # config.force_ssl = true
32 |
33 | # See everything in the log (default is :info)
34 | # config.log_level = :debug
35 |
36 | # Prepend all log lines with the following tags
37 | # config.log_tags = [ :subdomain, :uuid ]
38 |
39 | # Use a different logger for distributed setups
40 | # config.logger = ActiveSupport::TaggedLogging.new(SyslogLogger.new)
41 |
42 | # Use a different cache store in production
43 | # config.cache_store = :mem_cache_store
44 |
45 | # Enable serving of images, stylesheets, and JavaScripts from an asset server
46 | # config.action_controller.asset_host = "http://assets.example.com"
47 |
48 | # Precompile additional assets (application.js, application.css, and all non-JS/CSS are already added)
49 | # config.assets.precompile += %w( search.js )
50 |
51 | # Disable delivery errors, bad email addresses will be ignored
52 | # config.action_mailer.raise_delivery_errors = false
53 |
54 | # Enable threaded mode
55 | # config.threadsafe!
56 |
57 | # Enable locale fallbacks for I18n (makes lookups for any locale fall back to
58 | # the I18n.default_locale when a translation can not be found)
59 | config.i18n.fallbacks = true
60 |
61 | # Send deprecation notices to registered listeners
62 | config.active_support.deprecation = :notify
63 |
64 | # Log the query plan for queries taking more than this (works
65 | # with SQLite, MySQL, and PostgreSQL)
66 | # config.active_record.auto_explain_threshold_in_seconds = 0.5
67 |
68 | config.eager_load = true
69 | end
70 |
--------------------------------------------------------------------------------
/app/controllers/advert_selector/placements_controller.rb:
--------------------------------------------------------------------------------
1 | require_dependency "advert_selector/application_controller"
2 |
3 | module AdvertSelector
4 | class PlacementsController < ApplicationController
5 | # GET /placements
6 | # GET /placements.json
7 | def index
8 | @placements = Placement.all
9 |
10 | respond_to do |format|
11 | format.html # index.html.erb
12 | format.json { render :json => @placements }
13 | end
14 | end
15 |
16 | # GET /placements/1
17 | # GET /placements/1.json
18 | def show
19 | redirect_to edit_placement_url(params[:id])
20 |
21 | #@placement = Placement.find(params[:id])
22 | #
23 | #respond_to do |format|
24 | # format.html # show.html.erb
25 | # format.json { render :json => @placement }
26 | #end
27 | end
28 |
29 | # GET /placements/new
30 | # GET /placements/new.json
31 | def new
32 | @placement = Placement.new
33 |
34 | respond_to do |format|
35 | format.html # new.html.erb
36 | format.json { render :json => @placement }
37 | end
38 | end
39 |
40 | # GET /placements/1/edit
41 | def edit
42 | @placement = Placement.find(params[:id])
43 | end
44 |
45 | # POST /placements
46 | # POST /placements.json
47 | def create
48 | @placement = Placement.new(placement_params)
49 |
50 | respond_to do |format|
51 | if @placement.save
52 | format.html { redirect_to @placement, :notice => 'Placement was successfully created.' }
53 | format.json { render :json => @placement, :status => :created, :location => @placement }
54 | else
55 | format.html { render :action => "new" }
56 | format.json { render :json => @placement.errors, :status => :unprocessable_entity }
57 | end
58 | end
59 | end
60 |
61 | # PUT /placements/1
62 | # PUT /placements/1.json
63 | def update
64 | @placement = Placement.find(params[:id])
65 |
66 | respond_to do |format|
67 | if @placement.update(placement_params)
68 | format.html { redirect_to @placement, :notice => 'Placement was successfully updated.' }
69 | format.json { head :no_content }
70 | else
71 | format.html { render :action => "edit" }
72 | format.json { render :json => @placement.errors, :status => :unprocessable_entity }
73 | end
74 | end
75 | end
76 |
77 | # DELETE /placements/1
78 | # DELETE /placements/1.json
79 | def destroy
80 | @placement = Placement.find(params[:id])
81 | @placement.destroy
82 |
83 | respond_to do |format|
84 | format.html { redirect_to placements_url }
85 | format.json { head :no_content }
86 | end
87 | end
88 |
89 | private
90 | def placement_params
91 | params.require(:placement).permit(:conflicting_placements_array, :name, :comment, :request_delay, :only_once_per_session)
92 | end
93 |
94 | end
95 | end
96 |
--------------------------------------------------------------------------------
/app/views/layouts/advert_selector/application.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | AdvertSelector
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | <%= stylesheet_link_tag "advert_selector/application", :media => "all" %>
15 | <%= javascript_include_tag "advert_selector/application" %>
16 | <%= csrf_meta_tags %>
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
Advert selector
30 |
31 |
32 | Logged in as Username
33 |
34 |
35 | <% [['Placements', placements_url],
36 | ['Banners', banners_url],
37 | ['About this gem', 'https://github.com/holli/advert_selector']
38 | ].each do |str, url| %>
39 | <%= link_to str, url %>
40 | <% end %>
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
70 |
71 |
72 | <% unless AdvertSelector::ErrorsCache.errors.blank? %>
73 |
74 |
Advertisement causes errors. <%= link_to 'See all', main_url %>
75 | <%= simple_format(AdvertSelector::ErrorsCache.errors.last) %>
76 |
77 | <% end %>
78 |
79 |
80 |
81 | <%= yield %>
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
--------------------------------------------------------------------------------
/app/models/advert_selector/banner.rb:
--------------------------------------------------------------------------------
1 | module AdvertSelector
2 | class Banner < ActiveRecord::Base
3 | #attr_accessible :comment, :confirmed, :start_time, :end_time,
4 | # :frequency, :name, :placement_id, :target_view_count, :priority,
5 | # :fast_mode, :helper_items_attributes
6 |
7 | belongs_to :placement, :inverse_of => :banners
8 |
9 | has_many :helper_items, -> { order(:position) }, :dependent => :destroy, inverse_of: :banner
10 | accepts_nested_attributes_for :helper_items
11 |
12 | scope :find_future, lambda {
13 | order('priority desc').
14 | where('end_time > ? OR end_time IS NULL', Time.now).
15 | includes(:placement, :helper_items)
16 | }
17 | scope :find_current, lambda {
18 | find_future.
19 | where('start_time < ? OR start_time IS NULL', 1.hour.from_now).
20 | where('target_view_count IS NULL OR target_view_count > running_view_count')
21 | }
22 |
23 | # todo validates
24 | # validate placement
25 |
26 | def name_sym
27 | @name_sym ||= name.downcase.to_sym
28 | end
29 |
30 | def has_frequency?
31 | !frequency.nil? && frequency > 0
32 | end
33 |
34 | def show_today_has_viewcounts?(current_view_count = nil)
35 | return true if target_view_count.nil?
36 |
37 | current_view_count = running_view_count if current_view_count.nil?
38 |
39 | return false if current_view_count >= target_view_count
40 | return true if fast_mode?
41 |
42 | @show_now_today_target ||=
43 | if target_view_count.nil? || end_time.nil? || end_time < 24.hours.from_now
44 | true
45 | else
46 | total_hours = ((end_time - start_time - 12.hours)/1.hour).round
47 | hourly_view_count = target_view_count/total_hours
48 |
49 | hours_ending_now = ((Time.now.end_of_hour - start_time)/1.hour).ceil
50 | hours_ending_now * hourly_view_count
51 | end
52 |
53 | @show_now_today_target == true || current_view_count < @show_now_today_target
54 | end
55 |
56 | def show_now_basics?(use_time_limits = true)
57 | confirmed? &&
58 | (!use_time_limits || start_time.nil? || start_time < Time.now) &&
59 | (!use_time_limits || end_time.nil? || Time.now < end_time) &&
60 | show_today_has_viewcounts?
61 | end
62 |
63 | def reload
64 | super
65 | reset_cache
66 | end
67 |
68 | def cache_key
69 | "AdvertSelectorBanner_#{id}"
70 | end
71 |
72 | def running_view_count
73 | counter = Rails.cache.read(cache_key).to_i
74 | counter = 0 if defined?($advert_selector_avoid_cache) && $advert_selector_avoid_cache
75 | counter = self[:running_view_count] if counter < self[:running_view_count]
76 | counter
77 | end
78 |
79 | def reset_cache
80 | Rails.cache.write(cache_key, nil, :expires_in => 2.weeks)
81 | @show_now_today_target = nil
82 | @name_sym = nil
83 | end
84 |
85 | def add_one_viewcount
86 | unless self.target_view_count.nil?
87 |
88 | counter = running_view_count + 1
89 | Rails.cache.write(cache_key, counter, :expires_in => 2.weeks)
90 | self[:running_view_count] = counter
91 |
92 | since_update = running_view_count_change.last - running_view_count_change.first
93 | self.save if since_update >= 500 || counter >= target_view_count
94 | end
95 | end
96 |
97 | after_save :after_save_destroy_empty_helpers
98 | def after_save_destroy_empty_helpers
99 | helper_items.each do |hi|
100 | hi.destroy if hi.blank?
101 | end
102 | end
103 |
104 | end
105 |
106 | end
107 |
--------------------------------------------------------------------------------
/app/controllers/advert_selector/banners_controller.rb:
--------------------------------------------------------------------------------
1 | require_dependency "advert_selector/application_controller"
2 |
3 | module AdvertSelector
4 | class BannersController < ApplicationController
5 | # GET /banners
6 | # GET /banners.json
7 | def index
8 | @banners = Banner.all
9 |
10 | respond_to do |format|
11 | format.html # index.html.erb
12 | format.json { render :json => @banners }
13 | end
14 | end
15 |
16 | # GET /banners/1
17 | # GET /banners/1.json
18 | def show
19 | redirect_to edit_banner_url(params[:id])
20 |
21 | #@banner = Banner.find(params[:id])
22 | #
23 | #respond_to do |format|
24 | # format.html # show.html.erb
25 | # format.json { render :json => @banner }
26 | #end
27 | end
28 |
29 | # GET /banners/new
30 | # GET /banners/new.json
31 | def new
32 | if params[:duplicate_id] && banner_dup = Banner.find(params[:duplicate_id])
33 | @banner = banner_dup.dup
34 | @banner.name += " (copy)"
35 | @banner.confirmed = false
36 |
37 | banner_dup.helper_items.each do |hi|
38 | @banner.helper_items << hi.dup
39 | end
40 |
41 | else
42 | @banner = Banner.new
43 | @banner.start_time = Time.now.at_midnight
44 | @banner.end_time = 1.week.from_now.end_of_day
45 | end
46 |
47 | respond_to do |format|
48 | format.html # new.html.erb
49 | format.json { render :json => @banner }
50 | end
51 | end
52 |
53 | # GET /banners/1/edit
54 | def edit
55 | @banner = Banner.find(params[:id])
56 | end
57 |
58 | # POST /banners
59 | # POST /banners.json
60 | def create
61 | @banner = Banner.new(banner_params)
62 |
63 | respond_to do |format|
64 | if @banner.save
65 | format.html { redirect_to @banner, :notice => 'Banner was successfully created.' }
66 | format.json { render :json => @banner, :status => :created, :location => @banner }
67 | else
68 | format.html { render :action => "new" }
69 | format.json { render :json => @banner.errors, :status => :unprocessable_entity }
70 | end
71 | end
72 | end
73 |
74 | # PUT /banners/1
75 | # PUT /banners/1.json
76 | def update
77 | @banner = Banner.find(params[:id])
78 |
79 | respond_to do |format|
80 | if @banner.update(banner_params)
81 | format.html { redirect_to @banner, :notice => 'Banner was successfully updated.' }
82 | format.json { head :no_content }
83 | else
84 | format.html { render :action => "edit" }
85 | format.json { render :json => @banner.errors, :status => :unprocessable_entity }
86 | end
87 | end
88 | end
89 |
90 |
91 | def update_running_view_count
92 | @banner = Banner.find(params[:id])
93 | if !(@count = params['banner']["running_view_count"]).blank?
94 | @count = @count.to_i
95 | Banner.where(:id => @banner.id).update_all(:running_view_count => @count)
96 | 50.times do
97 | # We are trying to make sure that no other process will overwrite this value
98 | Rails.cache.write(@banner.cache_key, @count, :expires_in => 2.weeks)
99 | sleep(0.02)
100 | end
101 | Banner.where(:id => @banner.id).update_all(:running_view_count => @count)
102 | #@banner[:running_view_count] = @count
103 | #@banner.save
104 | end
105 |
106 | redirect_to @banner
107 | end
108 |
109 | # DELETE /banners/1
110 | # DELETE /banners/1.json
111 | def destroy
112 | @banner = Banner.find(params[:id])
113 | @banner.destroy
114 |
115 | respond_to do |format|
116 | format.html { redirect_to banners_url }
117 | format.json { head :no_content }
118 | end
119 | end
120 |
121 | private
122 | def banner_params
123 | params
124 | .require(:banner)
125 | .permit(
126 | :comment, :end_time, :frequency, :name, :placement_id, :start_time, :target_view_count, :priority, :confirmed, :fast_mode,
127 | :helper_items_attributes => [:id, :name, :content_for, :position, :content]
128 | )
129 | end
130 | end
131 | end
132 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # AdvertSelector
2 |
3 | Rails adserver tool for selecting a smaller subset of banners from all
4 | possible banners with differing banner placement combinations. Gem
5 | includes admin tools for handling banners in live
6 | environment. Includes basic targeting, viewcount, frequency etc
7 | setups.
8 |
9 | Good for deciding e.g. what height of header banner you will have
10 | during the initial requests without extra javascript calls to
11 | adserver. This helps to avoid problems of browser not knowing what
12 | size of banner there is and enables browser to render the whole page
13 | faster.
14 |
15 | [ ](http://travis-ci.org/holli/advert_selector)
16 |
17 | ## Features
18 |
19 | - selecting small subset of (advert) banners from multiple banners
20 | - banners can be also be any kind of widgets
21 | - defining banners
22 | - setting filters to banners in a rails friendly way
23 | - defining placements
24 | - setting conflicting placements
25 | - setting filters for placements
26 | - admin tools
27 | - editing everything
28 | - testing banners before setting it to live env
29 | - showing information in banner testing
30 | - showing error log if there is a problem in banner filters
31 |
32 | ## Install
33 |
34 | ```
35 | in /gemfile
36 | gem 'advert_selector'
37 |
38 | # Run following
39 | # bundle
40 | # bundle exec rake railties:install:migrations
41 | # rake db:migrate
42 |
43 | in top of views/layouts/your_layout
44 | <% advert_selector_initialize(:all) %>
45 | and if you want testing info then at the bottom
46 | <%= advert_selector_force_test_infos %>
47 |
48 | set to somewhere in your layout e.g.
49 | <%= content_for :banner_header %>
50 |
51 | in config/routes
52 | mount AdvertSelector::Engine => "/advert_selector"
53 |
54 | test the admin tool in url
55 | http://localhost:3000/advert_selector/
56 |
57 | If you have problems, try to install simple_form again by setting `gem simple_form` to .Gemfile.
58 |
59 | ```
60 |
61 | ## Configuration
62 |
63 |
64 | ```
65 | set extra_configuration in /config/initializers/advert_selector.rb
66 |
67 |
68 | AdvertSelector.default_banner_test_url = "http://yourdomain.com/?"
69 | self.default_time_zone = 'Helsinki'
70 |
71 | class AdminAccessToGemTools
72 | def self.admin_access(controller)
73 | if !controller.session[:admin_logged].blank? || Rails.env.development?
74 | return true
75 | else
76 | return false
77 | end
78 | end
79 | end
80 | AdvertSelector.admin_access_class = AdminAccessToGemTools
81 |
82 |
83 | ```
84 |
85 |
86 | ## Targeting / Content / HelperItem
87 |
88 | All contents and targetings are done through HelperItem-model.
89 |
90 | ### For targeting define custom helpers alongside your normal viewhelpers. Start by name advert_selector. E.g
91 |
92 | ```
93 | module AdvertSelectorHelper
94 | def advert_selector_targeting_gender?(helper_item)
95 | params[:gender] == helper_item.content
96 | end
97 | end
98 |
99 | And in banner define HelperItem with name 'targeting_age?' and content 'male'. After that the banner would be shown only with
100 | requests that has param[:gender]='male'.
101 |
102 | ```
103 |
104 | ### For displaying banner content
105 |
106 | HelperItems that are tagged with content_for are used with content_for method in rails. E.g. HelperItem.name = :banner_header
107 | and then in your views you display results by <%= content_for :banner_header %>
108 |
109 | ## Inside
110 |
111 | Banners are read to ruby processes memory once every 10 minutes from sql database.
112 | Gem uses Rails.cache to cache viewcount of banners. Viewcount is updated to db once in a while.
113 |
114 | Remember that this is not the real viewcount that more advanced banner handling systems have. Bots e.g. will increase the viewcount number
115 |
116 |
117 | ## Some links you might also consider
118 |
119 | - http://www.openx.com/
120 | - http://www.google.com/dfp/info/sb/index.html
121 |
122 |
123 | ## Support
124 |
125 | Submit suggestions or feature requests as a GitHub Issue or Pull Request. Remember to update tests. Tests are quite extensive.
126 |
127 | Check travis for what environments are supported http://travis-ci.org/#!/holli/advert_selector
128 |
129 | [ ](http://travis-ci.org/holli/advert_selector)
130 |
131 | ## Licence
132 |
133 | This project rocks and uses MIT-LICENSE.
134 |
--------------------------------------------------------------------------------
/test/integration/banner_shows_test.rb:
--------------------------------------------------------------------------------
1 | require File.dirname(__FILE__) + '/../test_helper'
2 | #require 'test/test_helper'
3 |
4 | class BannerShowsTest < ActionDispatch::IntegrationTest
5 | # fixtures :all
6 |
7 | setup do
8 | @coke.reload
9 | @coke.fast_mode = true
10 | @coke.save!
11 | assert @coke.show_now_basics?, "fixtures problem"
12 | @pepsi.reload
13 | @pepsi.update({:confirmed => false})
14 | assert !@pepsi.show_now_basics?, "fixtures prolbem 2"
15 | AdvertSelector.admin_access_class = AdvertSelector::AdminAccessClassAlwaysTrue
16 | end
17 |
18 | def response_includes_banner?(banner)
19 | @response.body.include?(banner.helper_items.where(:content_for => true).last.content)
20 | end
21 | def assert_response_includes_banner(banner)
22 | assert response_includes_banner?(banner), "should have default banner content in response"
23 | end
24 |
25 | test "normal request and banner loading" do
26 | AdvertSelector::Banner.expects(:find_current).twice.returns(AdvertSelector::Banner.find_future)
27 |
28 | get '/'
29 | assert_response :success
30 |
31 | assert_response_includes_banner(@coke)
32 |
33 | assert $advert_selector_banners_load_time > 1.minute.ago
34 | original_time = $advert_selector_banners_load_time
35 |
36 | #assert_equal $advert_selector_banners, AdvertSelector::Banner.find_current
37 |
38 | get '/'
39 | assert_response :success
40 |
41 | assert_equal $advert_selector_banners_load_time, original_time
42 |
43 | Timecop.travel( 15.minutes.from_now ) do
44 | get '/'
45 | assert $advert_selector_banners_load_time != original_time
46 | assert $advert_selector_banners_load_time > 1.minute.ago
47 | end
48 | end
49 |
50 | test "only_once_per_session banners" do
51 | placement = @coke.placement
52 | placement.only_once_per_session = true
53 | placement.save!
54 |
55 | get '/'
56 | assert_response :success
57 | assert_response_includes_banner(@coke)
58 |
59 | assert_equal ["Leaderboard"], session["advert_selector_session_shown"], "should have info in session"
60 |
61 | get '/'
62 | assert_response :success
63 | assert !response_includes_banner?(@coke), "should not include banner on second view"
64 | end
65 |
66 | test "banner_frequency, limit frequency within one week" do
67 | @coke.update({:frequency => 2})
68 |
69 | get '/'
70 | assert_response :success
71 | assert_response_includes_banner(@coke)
72 |
73 | cookie_expiration_date_org = cookies.send(:eval, '@cookies').first.send(:eval, '@options')["expires"].to_time
74 | assert 6.days.from_now < cookie_expiration_date_org
75 | assert 7.days.from_now > cookie_expiration_date_org
76 |
77 | Timecop.travel( 3.days.from_now )
78 |
79 | get '/'
80 | assert_response :success
81 | assert_response_includes_banner(@coke)
82 |
83 | cookie_expiration_date_new = cookies.send(:eval, '@cookies').first.send(:eval, '@options')["expires"].to_time
84 | assert_equal cookie_expiration_date_new, cookie_expiration_date_org
85 |
86 | get '/'
87 | assert_response :success
88 | assert !response_includes_banner?(@coke), "should not include banner after expiration date"
89 |
90 | end
91 |
92 | test "HelperItem runned" do
93 | get '/'
94 | assert_response :success
95 | assert_response_includes_banner(@coke)
96 |
97 | $advert_selector_banners_load_time = nil
98 |
99 | @coke.helper_items.create!(:position => 0, :name => 'always_false')
100 | get '/'
101 | assert_response :success
102 | assert !response_includes_banner?(@coke), "should not include banner if helper_item returned false"
103 |
104 | end
105 |
106 | test "HelperItem with raising error and common error displays" do
107 |
108 | @coke.helper_items.create!(:position => 0, :name => 'raise_error')
109 | get '/'
110 | assert_response :success
111 | assert !response_includes_banner?(@coke), "should not include banner if helper_item raised error"
112 |
113 | get '/advert_selector/placements'
114 | assert_response :success
115 | assert_select '.alert-error', :text => /Error with banner Coke in placement Leaderboard/
116 |
117 | end
118 |
119 |
120 | test "complex setup conflicting banners placements" do
121 | @parade_banner.update({:confirmed => true, :frequency => 1})
122 | get '/'
123 | assert_response :success
124 | assert response_includes_banner?(@parade_banner)
125 | assert !response_includes_banner?(@coke)
126 |
127 | get '/'
128 | assert_response :success
129 | assert !response_includes_banner?(@parade_banner)
130 | assert response_includes_banner?(@coke)
131 | end
132 |
133 | test "complex setup multiple banners placements" do
134 | @parade_banner.update({:confirmed => true, :frequency => 1})
135 | @parade_banner.placement.conflicting_placements_array = ""
136 | @parade_banner.placement.save!
137 |
138 | get '/'
139 | assert_response :success
140 | assert response_includes_banner?(@parade_banner)
141 | assert response_includes_banner?(@coke)
142 | end
143 |
144 | test "banner preview url forces banner and displays information" do
145 | get '/'
146 | assert !response_includes_banner?(@parade_banner)
147 | assert_select '#advert_selector_info', :count => 0
148 |
149 | get "/?advert_selector_force=#{@parade_banner.id}&advert_selector_force_stamp=#{@parade_banner.start_time.to_i}"
150 | assert_response :success
151 | assert response_includes_banner?(@parade_banner)
152 |
153 | assert_select '#advert_selector_info', :count => 1
154 | end
155 |
156 | end
157 |
158 |
--------------------------------------------------------------------------------
/app/helpers/advert_selector/application_helper.rb:
--------------------------------------------------------------------------------
1 | module AdvertSelector
2 | module ApplicationHelper
3 |
4 | def advert_selector_initialize(available_placements = :all)
5 | Rails.logger.tagged('AdvertSelector') do
6 |
7 | Rails.logger.debug("AdvertSelection initialized")
8 |
9 | @advert_selector_banners_selected = []
10 |
11 | if params[:advert_selector_force]
12 | $advert_selector_banners_load_time = nil # reload everything
13 | if (banner_found = Banner.find_by_id(params[:advert_selector_force])) && banner_found.start_time.to_i.to_s == params[:advert_selector_force_stamp]
14 | advert_selector_banner_force(banner_found)
15 | end
16 | end
17 |
18 | advert_selector_banners.each do |banner_iter|
19 | if available_placements == :all || available_placements.include?(banner_iter.placement.name_sym)
20 | advert_selector_banner_try(banner_iter)
21 | end
22 | end
23 |
24 | Rails.logger.debug("AdvertSelection finished")
25 | end
26 | ""
27 | end
28 |
29 | def advert_selector_force_test_infos
30 | if defined?(@advert_selector_force_banner_infos) && @advert_selector_force_banner_infos
31 | content_tag :div, :id => "advert_selector_info", :class => 'alert alert-info', :style => "position: fixed; bottom: 5px;" do
32 | content_tag(:strong) { "AdvertSelectorInfos for HelperItems: ".html_safe } +
33 | content_tag(:ul) {
34 | @advert_selector_force_banner_infos.to_a.collect{|k, v| content_tag(:li){"#{k} : #{h(v)}".html_safe} }.join("\n").html_safe
35 | }
36 | end
37 | end
38 |
39 | end
40 |
41 |
42 | def advert_selector_banner_try(banner)
43 |
44 | if banner.show_now_basics? &&
45 | advert_selector_placement_free?(banner.placement) &&
46 | advert_selector_placement_once_per_session_ok?(banner.placement) &&
47 | advert_selector_banner_frequency_ok?(banner)
48 |
49 | banner.helper_items.each do |hi|
50 | if hi.content_for?
51 | content_for hi.name_sym, hi.content.html_safe
52 | else
53 | return unless send("advert_selector_#{hi.name}", hi)
54 | end
55 | end
56 |
57 | advert_selector_placement_once_per_session_shown(banner.placement)
58 | advert_selector_banner_frequency_shown(banner)
59 |
60 | banner.add_one_viewcount unless request.user_agent =~ /bot/i
61 |
62 | @advert_selector_banners_selected.push(banner)
63 |
64 | Rails.logger.info("Showing banner (#{banner.id}) #{banner.name} in placement #{banner.placement.name}")
65 | end
66 |
67 | rescue => e
68 | begin
69 | str = "Error with banner #{banner.name} in placement #{banner.placement.name}.\n#{Time.now.iso8601} - #{request.url} - #{params.inspect}\n#{e.inspect}\n\n#{e.backtrace.first(10).join("\n")}"
70 |
71 | AdvertSelector::ErrorsCache.add(str)
72 | Rails.logger.error(str)
73 | rescue => e
74 | Rails.logger.error("ERROR INSIDE ERROR with #{banner.name} in placement #{banner.placement.name} : #{e.inspect}")
75 | end
76 | end
77 |
78 | def advert_selector_banner_force(banner)
79 | @advert_selector_force_banner_infos = []
80 | @advert_selector_force_banner_infos.push [:show_now_basics_times_not_used, banner.show_now_basics?(false)]
81 | @advert_selector_force_banner_infos.push [:show_now_basics_with_times, banner.show_now_basics?]
82 | @advert_selector_force_banner_infos.push [:placement_free, advert_selector_placement_free?(banner.placement)]
83 | @advert_selector_force_banner_infos.push [:placement_once_per_session, advert_selector_placement_once_per_session_ok?(banner.placement)]
84 | @advert_selector_force_banner_infos.push [:frequency, advert_selector_banner_frequency_ok?(banner)]
85 | banner.helper_items.each do |hi|
86 | @advert_selector_force_banner_infos.push [hi.name_sym,
87 | if hi.content_for?
88 | content_for hi.name_sym, hi.content.html_safe
89 | content_for(hi.name_sym).first(20)
90 | else
91 | send("advert_selector_#{hi.name}", hi)
92 | end]
93 | end
94 |
95 | @advert_selector_banners_selected.push(banner)
96 | Rails.logger.info("ForceShowing banner #{banner.name} in placement #{banner.placement.name}")
97 |
98 | end
99 |
100 | def advert_selector_placement_free?(placement)
101 | !placement.conflicting_with?(@advert_selector_banners_selected.collect{|b| b.placement.name_sym})
102 | end
103 |
104 | def advert_selector_placement_once_per_session_ok?(placement)
105 | !( placement.only_once_per_session? && session[:advert_selector_session_shown] &&
106 | session[:advert_selector_session_shown].include?(placement.name) )
107 | end
108 | def advert_selector_placement_once_per_session_shown(placement)
109 | if placement.only_once_per_session?
110 | session[:advert_selector_session_shown] = [] if session[:advert_selector_session_shown].nil?
111 | session[:advert_selector_session_shown].push(placement.name)
112 | end
113 | end
114 |
115 | def advert_selector_banner_frequency_cookie(banner)
116 | val, time = cookies["ad_#{banner.id}"].to_s.split(",")
117 | [val.to_i, time]
118 | end
119 | def advert_selector_banner_frequency_ok?(banner)
120 | !banner.has_frequency? || advert_selector_banner_frequency_cookie(banner).first < banner.frequency
121 | end
122 | def advert_selector_banner_frequency_shown(banner)
123 | return true unless banner.has_frequency?
124 |
125 | val, time = advert_selector_banner_frequency_cookie(banner)
126 | time = time.blank? ? 1.week.from_now : Time.parse(time)
127 | val += 1
128 | cookies["ad_#{banner.id}"] = {:domain => :all, :expires => time, :value => [val, time.iso8601].join(",") }
129 | end
130 |
131 | ##########################################################
132 |
133 | def advert_selector_request_params_include?(placement)
134 | key, val = placement.content.to_s.split("=")
135 | return params[key] == val
136 | end
137 |
138 |
139 | ##########################################################
140 |
141 | $advert_selector_banners = []
142 | $advert_selector_banners_load_time = nil
143 | def advert_selector_banners
144 | if $advert_selector_banners_load_time.nil? || $advert_selector_banners_load_time < 10.minutes.ago || Rails.env.development?
145 | Rails.logger.info("AdvertSelection fetching banners and placements")
146 | $advert_selector_banners_load_time = Time.now
147 |
148 | $advert_selector_banners = Banner.find_current
149 | end
150 |
151 | $advert_selector_banners
152 | end
153 |
154 | end
155 | end
156 |
--------------------------------------------------------------------------------
/test/dummy/config/initializers/simple_form.rb:
--------------------------------------------------------------------------------
1 | # Use this setup block to configure all options available in SimpleForm.
2 | SimpleForm.setup do |config|
3 | # Wrappers are used by the form builder to generate a
4 | # complete input. You can remove any component from the
5 | # wrapper, change the order or even add your own to the
6 | # stack. The options given below are used to wrap the
7 | # whole input.
8 | config.wrappers :default, :class => :input,
9 | :hint_class => :field_with_hint, :error_class => :field_with_errors do |b|
10 | ## Extensions enabled by default
11 | # Any of these extensions can be disabled for a
12 | # given input by passing: `f.input EXTENSION_NAME => false`.
13 | # You can make any of these extensions optional by
14 | # renaming `b.use` to `b.optional`.
15 |
16 | # Determines whether to use HTML5 (:email, :url, ...)
17 | # and required attributes
18 | b.use :html5
19 |
20 | # Calculates placeholders automatically from I18n
21 | # You can also pass a string as f.input :placeholder => "Placeholder"
22 | b.use :placeholder
23 |
24 | ## Optional extensions
25 | # They are disabled unless you pass `f.input EXTENSION_NAME => :lookup`
26 | # to the input. If so, they will retrieve the values from the model
27 | # if any exists. If you want to enable the lookup for any of those
28 | # extensions by default, you can change `b.optional` to `b.use`.
29 |
30 | # Calculates maxlength from length validations for string inputs
31 | b.optional :maxlength
32 |
33 | # Calculates pattern from format validations for string inputs
34 | b.optional :pattern
35 |
36 | # Calculates min and max from length validations for numeric inputs
37 | b.optional :min_max
38 |
39 | # Calculates readonly automatically from readonly attributes
40 | b.optional :readonly
41 |
42 | ## Inputs
43 | b.use :label_input
44 | b.use :hint, :wrap_with => { :tag => :span, :class => :hint }
45 | b.use :error, :wrap_with => { :tag => :span, :class => :error }
46 | end
47 |
48 | config.wrappers :bootstrap, :tag => 'div', :class => 'control-group', :error_class => 'error' do |b|
49 | b.use :html5
50 | b.use :placeholder
51 | b.use :label
52 | b.wrapper :tag => 'div', :class => 'controls' do |ba|
53 | ba.use :input
54 | ba.use :error, :wrap_with => { :tag => 'span', :class => 'help-inline' }
55 | ba.use :hint, :wrap_with => { :tag => 'p', :class => 'help-block' }
56 | end
57 | end
58 |
59 | config.wrappers :prepend, :tag => 'div', :class => "control-group", :error_class => 'error' do |b|
60 | b.use :html5
61 | b.use :placeholder
62 | b.use :label
63 | b.wrapper :tag => 'div', :class => 'controls' do |input|
64 | input.wrapper :tag => 'div', :class => 'input-prepend' do |prepend|
65 | prepend.use :input
66 | end
67 | input.use :hint, :wrap_with => { :tag => 'span', :class => 'help-block' }
68 | input.use :error, :wrap_with => { :tag => 'span', :class => 'help-inline' }
69 | end
70 | end
71 |
72 | config.wrappers :append, :tag => 'div', :class => "control-group", :error_class => 'error' do |b|
73 | b.use :html5
74 | b.use :placeholder
75 | b.use :label
76 | b.wrapper :tag => 'div', :class => 'controls' do |input|
77 | input.wrapper :tag => 'div', :class => 'input-append' do |append|
78 | append.use :input
79 | end
80 | input.use :hint, :wrap_with => { :tag => 'span', :class => 'help-block' }
81 | input.use :error, :wrap_with => { :tag => 'span', :class => 'help-inline' }
82 | end
83 | end
84 |
85 | # Wrappers for forms and inputs using the Twitter Bootstrap toolkit.
86 | # Check the Bootstrap docs (http://twitter.github.com/bootstrap)
87 | # to learn about the different styles for forms and inputs,
88 | # buttons and other elements.
89 | config.default_wrapper = :bootstrap
90 |
91 | # Define the way to render check boxes / radio buttons with labels.
92 | # Defaults to :nested for bootstrap config.
93 | # :inline => input + label
94 | # :nested => label > input
95 | config.boolean_style = :nested
96 |
97 | # Default class for buttons
98 | config.button_class = 'btn'
99 |
100 | # Method used to tidy up errors. Specify any Rails Array method.
101 | # :first lists the first message for each field.
102 | # Use :to_sentence to list all errors for each field.
103 | # config.error_method = :first
104 |
105 | # Default tag used for error notification helper.
106 | config.error_notification_tag = :div
107 |
108 | # CSS class to add for error notification helper.
109 | config.error_notification_class = 'alert alert-error'
110 |
111 | # ID to add for error notification helper.
112 | # config.error_notification_id = nil
113 |
114 | # Series of attempts to detect a default label method for collection.
115 | # config.collection_label_methods = [ :to_label, :name, :title, :to_s ]
116 |
117 | # Series of attempts to detect a default value method for collection.
118 | # config.collection_value_methods = [ :id, :to_s ]
119 |
120 | # You can wrap a collection of radio/check boxes in a pre-defined tag, defaulting to none.
121 | # config.collection_wrapper_tag = nil
122 |
123 | # You can define the class to use on all collection wrappers. Defaulting to none.
124 | # config.collection_wrapper_class = nil
125 |
126 | # You can wrap each item in a collection of radio/check boxes with a tag,
127 | # defaulting to :span. Please note that when using :boolean_style = :nested,
128 | # SimpleForm will force this option to be a label.
129 | # config.item_wrapper_tag = :span
130 |
131 | # You can define a class to use in all item wrappers. Defaulting to none.
132 | # config.item_wrapper_class = nil
133 |
134 | # How the label text should be generated altogether with the required text.
135 | # config.label_text = lambda { |label, required| "#{required} #{label}" }
136 |
137 | # You can define the class to use on all labels. Default is nil.
138 | config.label_class = 'control-label'
139 |
140 | # You can define the class to use on all forms. Default is simple_form.
141 | # config.form_class = :simple_form
142 |
143 | # You can define which elements should obtain additional classes
144 | # config.generate_additional_classes_for = [:wrapper, :label, :input]
145 |
146 | # Whether attributes are required by default (or not). Default is true.
147 | # config.required_by_default = true
148 |
149 | # Tell browsers whether to use default HTML5 validations (novalidate option).
150 | # Default is enabled.
151 | config.browser_validations = false
152 |
153 | # Collection of methods to detect if a file type was given.
154 | # config.file_methods = [ :mounted_as, :file?, :public_filename ]
155 |
156 | # Custom mappings for input types. This should be a hash containing a regexp
157 | # to match as key, and the input type that will be used when the field name
158 | # matches the regexp as value.
159 | # config.input_mappings = { /count/ => :integer }
160 |
161 | # Default priority for time_zone inputs.
162 | # config.time_zone_priority = nil
163 |
164 | # Default priority for country inputs.
165 | # config.country_priority = nil
166 |
167 | # Default size for text inputs.
168 | # config.default_input_size = 50
169 |
170 | # When false, do not use translations for labels.
171 | # config.translate_labels = true
172 |
173 | # Automatically discover new inputs in Rails' autoload path.
174 | # config.inputs_discovery = true
175 |
176 | # Cache SimpleForm inputs discovery
177 | # config.cache_discovery = !Rails.env.development?
178 | end
179 |
--------------------------------------------------------------------------------
/test/unit/advert_selector/banner_test.rb:
--------------------------------------------------------------------------------
1 | require File.dirname(__FILE__) + '/../../test_helper'
2 |
3 | module AdvertSelector
4 | class BannerTest < ActiveSupport::TestCase
5 | # fixtures :all
6 |
7 | setup do
8 | $advert_selector_avoid_cache = true
9 | end
10 |
11 | test 'find_future && find_current scopes' do
12 | #binding.pry
13 | assert_equal 3, Banner.find_future.size
14 | assert_equal 3, Banner.find_current.size
15 |
16 | Timecop.travel( 1.year.ago ) do
17 | assert_equal 3, Banner.find_future.size
18 | assert_equal 0, Banner.find_current.size
19 | end
20 |
21 | end
22 |
23 | test "name_sym" do
24 | assert_equal :coke, @coke.name_sym
25 | end
26 |
27 | test "running_viewcount & add_one_viewcount" do
28 | $advert_selector_avoid_cache = false
29 | @coke.reset_cache
30 | @coke[:running_view_count] = 0
31 | @coke.save
32 |
33 | assert_equal 0, @coke.running_view_count
34 | @coke.add_one_viewcount
35 | @coke.add_one_viewcount
36 | assert_equal 2, @coke.running_view_count
37 |
38 | coke_second = Banner.find(@coke.id)
39 | assert_equal 0, coke_second[:running_view_count], "should not save value to db after every reload"
40 | assert_equal 2, coke_second.running_view_count, "should fetch value from rails cache in every view"
41 |
42 | Rails.cache.write(@coke.cache_key, 550, :expires_in => 2.weeks)
43 | @coke.add_one_viewcount
44 |
45 | coke_third = Banner.find(@coke.id)
46 | assert_equal 551, coke_third[:running_view_count], "should have saved value to db after so many views"
47 | end
48 |
49 | test "running_viewcount & add_one_viewcount reaching target" do
50 | $advert_selector_avoid_cache = false
51 | @coke.reset_cache
52 | @coke[:running_view_count] = 0
53 | @coke.target_view_count = 10
54 | @coke.save
55 |
56 | assert_equal 0, @coke.running_view_count
57 | Rails.cache.write(@coke.cache_key, @coke.target_view_count-1, :expires_in => 2.weeks)
58 | @coke.add_one_viewcount
59 | assert_equal 10, @coke.running_view_count
60 |
61 | coke_second = Banner.find(@coke.id)
62 | assert_equal 10, coke_second[:running_view_count], "should save if reaching target"
63 | end
64 |
65 | test "view_count per_hour" do
66 |
67 | start_time = Time.now.at_beginning_of_day
68 |
69 | # 10 views per hour
70 | @coke.update(:start_time => start_time, :end_time => start_time + 112.hours,
71 | :target_view_count => 1000, :fast_mode => false)
72 | @coke.running_view_count = 0
73 | @coke.save!
74 |
75 | Timecop.travel( start_time + 4.5.hours )
76 | assert @coke.show_today_has_viewcounts?
77 |
78 | @coke.reload
79 | @coke.running_view_count = 39
80 | assert @coke.show_today_has_viewcounts?
81 |
82 | @coke.reload
83 | @coke.running_view_count = 49
84 | assert @coke.show_today_has_viewcounts?
85 |
86 | @coke.reload
87 | @coke.running_view_count = 51
88 | assert !@coke.show_today_has_viewcounts?
89 |
90 | Timecop.travel( start_time + 90.hours )
91 | @coke.reload
92 | @coke.running_view_count = 990
93 | assert @coke.show_today_has_viewcounts?, "should let last 24h to display everything straight away"
94 |
95 | end
96 |
97 | test "view_count basics compare_value and per_fast_mode" do
98 | start_time = Time.now.at_beginning_of_day
99 |
100 | # 10 views per hour
101 | @coke.update(:start_time => start_time, :end_time => start_time + 112.hours,
102 | :target_view_count => 1000, :fast_mode => false)
103 | @coke.running_view_count = 0
104 | @coke.save!
105 |
106 | Timecop.travel( start_time + 4.5.hours )
107 |
108 | assert @coke.show_today_has_viewcounts?
109 | @coke.reload
110 | assert !@coke.show_today_has_viewcounts?(900), "use given value in has_viewcounts"
111 |
112 | @coke.reload
113 | @coke.fast_mode = true
114 | assert @coke.show_today_has_viewcounts?(900), "true always with fast_mode on"
115 |
116 | @coke.reload
117 | @coke.fast_mode = true
118 | assert !@coke.show_today_has_viewcounts?(1010), "should be false if viewcount has been achieved even though in fast mode"
119 |
120 | @coke.reload
121 | @coke.target_view_count = nil
122 | assert @coke.show_today_has_viewcounts?(2000), "should be true if no target viewcount"
123 | end
124 |
125 | test "view_count daily tests" do
126 | start_time = Time.now.at_beginning_of_day
127 | @coke.update(:start_time => start_time, :end_time => 10.days.from_now.at_beginning_of_day,
128 | :target_view_count => 1000, :fast_mode => false)
129 | @coke.running_view_count = 0
130 | @coke.save!
131 |
132 | Timecop.travel( start_time + 11.hours )
133 |
134 | assert @coke.show_today_has_viewcounts?
135 |
136 | @coke.reload
137 | @coke.running_view_count = 130
138 | assert !@coke.show_today_has_viewcounts?, "daily limit should be full"
139 |
140 | @coke.fast_mode = true
141 | assert @coke.show_today_has_viewcounts?, "fast_mode true, always true"
142 | @coke.fast_mode = false
143 |
144 |
145 | Timecop.travel( 5.days.from_now.at_midnight + 12.hours ) do
146 | @coke.reload
147 | @coke.running_view_count = 300
148 | assert @coke.show_today_has_viewcounts?
149 | assert @coke.show_today_has_viewcounts?(300)
150 | assert !@coke.show_today_has_viewcounts?(600), "should compare with given value"
151 |
152 | @coke.reload
153 | @coke.running_view_count = 600
154 | assert !@coke.show_today_has_viewcounts?
155 | end
156 |
157 | Timecop.travel( 9.days.from_now.at_midnight + 12.hours ) do
158 | @coke.reload
159 | assert @coke.show_today_has_viewcounts?(990), "last day should always be true"
160 | end
161 |
162 | @coke.reload
163 | assert !@coke.show_today_has_viewcounts?(1010), "should be false if viewcount has been achieved"
164 |
165 | @coke.reload
166 | @coke.target_view_count = nil
167 | assert @coke.show_today_has_viewcounts?(1000), "should be true if no target viewcount"
168 |
169 | Timecop.travel( start_time + 19.hours ) do
170 | @coke.reload
171 | @coke.update(:start_time => start_time + 18.hours, :end_time => 10.days.from_now.at_beginning_of_day,
172 | :target_view_count => 1000)
173 | @coke.running_view_count = 0
174 | assert @coke.show_today_has_viewcounts?(), "should be true for the first day even if start is later in the evening"
175 | end
176 | end
177 |
178 | test "show_now_basic? for default banners" do
179 | @coke.confirmed = false
180 | @coke.save!
181 | assert !@coke.show_now_basics?, "not confirmed"
182 |
183 | @coke.confirmed = true
184 | @coke.save!
185 | assert @coke.show_now_basics?
186 |
187 | @coke.reload
188 | @coke.running_view_count = 100000000
189 | assert !@coke.show_now_basics?, "target reached"
190 | end
191 |
192 | test "show_now_basic? time usages" do
193 | assert @coke.show_now_basics?, "setup is wrong"
194 |
195 | @coke.start_time = 1.hour.from_now
196 | @coke.save!
197 | assert !@coke.show_now_basics?, "should not display if advert in future"
198 |
199 | @coke.start_time = 2.hour.ago
200 | @coke.end_time = 1.hour.ago
201 | @coke.save!
202 | assert !@coke.show_now_basics?, "should not display if advert in past"
203 |
204 | @coke.start_time = nil
205 | @coke.end_time = 1.hour.from_now
206 | @coke.save!
207 | assert @coke.show_now_basics?, "should display if advert has only end_time in the future"
208 |
209 | end
210 |
211 | test "HelperItems" do
212 | helper_items_count = HelperItem.count
213 | @coke.helper_items.build
214 | @coke.helper_items.build(:name => 'content_for_invalid', :content_for => true, :position => 10)
215 | @coke.helper_items.build(:name => 'some_limit_helper_here', :position => 0)
216 | assert @coke.save
217 |
218 | banner = Banner.find(@coke.id)
219 |
220 | assert_equal helper_items_count+2, HelperItem.count
221 |
222 | assert 'some_limit_helper_here', banner.helper_items.first.name
223 |
224 | assert 'content_for_invalid', banner.helper_items.last.name
225 | end
226 |
227 |
228 | # test "the truth" do
229 | # assert true
230 | # end
231 | end
232 | end
233 |
--------------------------------------------------------------------------------
/test/dummy/README.rdoc:
--------------------------------------------------------------------------------
1 | == Welcome to Rails
2 |
3 | Rails is a web-application framework that includes everything needed to create
4 | database-backed web applications according to the Model-View-Control pattern.
5 |
6 | This pattern splits the view (also called the presentation) into "dumb"
7 | templates that are primarily responsible for inserting pre-built data in between
8 | HTML tags. The model contains the "smart" domain objects (such as Account,
9 | Product, Person, Post) that holds all the business logic and knows how to
10 | persist themselves to a database. The controller handles the incoming requests
11 | (such as Save New Account, Update Product, Show Post) by manipulating the model
12 | and directing data to the view.
13 |
14 | In Rails, the model is handled by what's called an object-relational mapping
15 | layer entitled Active Record. This layer allows you to present the data from
16 | database rows as objects and embellish these data objects with business logic
17 | methods. You can read more about Active Record in
18 | link:files/vendor/rails/activerecord/README.html.
19 |
20 | The controller and view are handled by the Action Pack, which handles both
21 | layers by its two parts: Action View and Action Controller. These two layers
22 | are bundled in a single package due to their heavy interdependence. This is
23 | unlike the relationship between the Active Record and Action Pack that is much
24 | more separate. Each of these packages can be used independently outside of
25 | Rails. You can read more about Action Pack in
26 | link:files/vendor/rails/actionpack/README.html.
27 |
28 |
29 | == Getting Started
30 |
31 | 1. At the command prompt, create a new Rails application:
32 | rails new myapp (where myapp is the application name)
33 |
34 | 2. Change directory to myapp and start the web server:
35 | cd myapp; rails server (run with --help for options)
36 |
37 | 3. Go to http://localhost:3000/ and you'll see:
38 | "Welcome aboard: You're riding Ruby on Rails!"
39 |
40 | 4. Follow the guidelines to start developing your application. You can find
41 | the following resources handy:
42 |
43 | * The Getting Started Guide: http://guides.rubyonrails.org/getting_started.html
44 | * Ruby on Rails Tutorial Book: http://www.railstutorial.org/
45 |
46 |
47 | == Debugging Rails
48 |
49 | Sometimes your application goes wrong. Fortunately there are a lot of tools that
50 | will help you debug it and get it back on the rails.
51 |
52 | First area to check is the application log files. Have "tail -f" commands
53 | running on the server.log and development.log. Rails will automatically display
54 | debugging and runtime information to these files. Debugging info will also be
55 | shown in the browser on requests from 127.0.0.1.
56 |
57 | You can also log your own messages directly into the log file from your code
58 | using the Ruby logger class from inside your controllers. Example:
59 |
60 | class WeblogController < ActionController::Base
61 | def destroy
62 | @weblog = Weblog.find(params[:id])
63 | @weblog.destroy
64 | logger.info("#{Time.now} Destroyed Weblog ID ##{@weblog.id}!")
65 | end
66 | end
67 |
68 | The result will be a message in your log file along the lines of:
69 |
70 | Mon Oct 08 14:22:29 +1000 2007 Destroyed Weblog ID #1!
71 |
72 | More information on how to use the logger is at http://www.ruby-doc.org/core/
73 |
74 | Also, Ruby documentation can be found at http://www.ruby-lang.org/. There are
75 | several books available online as well:
76 |
77 | * Programming Ruby: http://www.ruby-doc.org/docs/ProgrammingRuby/ (Pickaxe)
78 | * Learn to Program: http://pine.fm/LearnToProgram/ (a beginners guide)
79 |
80 | These two books will bring you up to speed on the Ruby language and also on
81 | programming in general.
82 |
83 |
84 | == Debugger
85 |
86 | Debugger support is available through the debugger command when you start your
87 | Mongrel or WEBrick server with --debugger. This means that you can break out of
88 | execution at any point in the code, investigate and change the model, and then,
89 | resume execution! You need to install ruby-debug to run the server in debugging
90 | mode. With gems, use sudo gem install ruby-debug . Example:
91 |
92 | class WeblogController < ActionController::Base
93 | def index
94 | @posts = Post.all
95 | debugger
96 | end
97 | end
98 |
99 | So the controller will accept the action, run the first line, then present you
100 | with a IRB prompt in the server window. Here you can do things like:
101 |
102 | >> @posts.inspect
103 | => "[#nil, "body"=>nil, "id"=>"1"}>,
105 | #"Rails", "body"=>"Only ten..", "id"=>"2"}>]"
107 | >> @posts.first.title = "hello from a debugger"
108 | => "hello from a debugger"
109 |
110 | ...and even better, you can examine how your runtime objects actually work:
111 |
112 | >> f = @posts.first
113 | => #nil, "body"=>nil, "id"=>"1"}>
114 | >> f.
115 | Display all 152 possibilities? (y or n)
116 |
117 | Finally, when you're ready to resume execution, you can enter "cont".
118 |
119 |
120 | == Console
121 |
122 | The console is a Ruby shell, which allows you to interact with your
123 | application's domain model. Here you'll have all parts of the application
124 | configured, just like it is when the application is running. You can inspect
125 | domain models, change values, and save to the database. Starting the script
126 | without arguments will launch it in the development environment.
127 |
128 | To start the console, run rails console from the application
129 | directory.
130 |
131 | Options:
132 |
133 | * Passing the -s, --sandbox argument will rollback any modifications
134 | made to the database.
135 | * Passing an environment name as an argument will load the corresponding
136 | environment. Example: rails console production .
137 |
138 | To reload your controllers and models after launching the console run
139 | reload!
140 |
141 | More information about irb can be found at:
142 | link:http://www.rubycentral.org/pickaxe/irb.html
143 |
144 |
145 | == dbconsole
146 |
147 | You can go to the command line of your database directly through rails
148 | dbconsole . You would be connected to the database with the credentials
149 | defined in database.yml. Starting the script without arguments will connect you
150 | to the development database. Passing an argument will connect you to a different
151 | database, like rails dbconsole production . Currently works for MySQL,
152 | PostgreSQL and SQLite 3.
153 |
154 | == Description of Contents
155 |
156 | The default directory structure of a generated Ruby on Rails application:
157 |
158 | |-- app
159 | | |-- assets
160 | | |-- images
161 | | |-- javascripts
162 | | `-- stylesheets
163 | | |-- controllers
164 | | |-- helpers
165 | | |-- mailers
166 | | |-- models
167 | | `-- views
168 | | `-- layouts
169 | |-- config
170 | | |-- environments
171 | | |-- initializers
172 | | `-- locales
173 | |-- db
174 | |-- doc
175 | |-- lib
176 | | `-- tasks
177 | |-- log
178 | |-- public
179 | |-- script
180 | |-- test
181 | | |-- fixtures
182 | | |-- functional
183 | | |-- integration
184 | | |-- performance
185 | | `-- unit
186 | |-- tmp
187 | | |-- cache
188 | | |-- pids
189 | | |-- sessions
190 | | `-- sockets
191 | `-- vendor
192 | |-- assets
193 | `-- stylesheets
194 | `-- plugins
195 |
196 | app
197 | Holds all the code that's specific to this particular application.
198 |
199 | app/assets
200 | Contains subdirectories for images, stylesheets, and JavaScript files.
201 |
202 | app/controllers
203 | Holds controllers that should be named like weblogs_controller.rb for
204 | automated URL mapping. All controllers should descend from
205 | ApplicationController which itself descends from ActionController::Base.
206 |
207 | app/models
208 | Holds models that should be named like post.rb. Models descend from
209 | ActiveRecord::Base by default.
210 |
211 | app/views
212 | Holds the template files for the view that should be named like
213 | weblogs/index.html.erb for the WeblogsController#index action. All views use
214 | eRuby syntax by default.
215 |
216 | app/views/layouts
217 | Holds the template files for layouts to be used with views. This models the
218 | common header/footer method of wrapping views. In your views, define a layout
219 | using the layout :default and create a file named default.html.erb.
220 | Inside default.html.erb, call <% yield %> to render the view using this
221 | layout.
222 |
223 | app/helpers
224 | Holds view helpers that should be named like weblogs_helper.rb. These are
225 | generated for you automatically when using generators for controllers.
226 | Helpers can be used to wrap functionality for your views into methods.
227 |
228 | config
229 | Configuration files for the Rails environment, the routing map, the database,
230 | and other dependencies.
231 |
232 | db
233 | Contains the database schema in schema.rb. db/migrate contains all the
234 | sequence of Migrations for your schema.
235 |
236 | doc
237 | This directory is where your application documentation will be stored when
238 | generated using rake doc:app
239 |
240 | lib
241 | Application specific libraries. Basically, any kind of custom code that
242 | doesn't belong under controllers, models, or helpers. This directory is in
243 | the load path.
244 |
245 | public
246 | The directory available for the web server. Also contains the dispatchers and the
247 | default HTML files. This should be set as the DOCUMENT_ROOT of your web
248 | server.
249 |
250 | script
251 | Helper scripts for automation and generation.
252 |
253 | test
254 | Unit and functional tests along with fixtures. When using the rails generate
255 | command, template test files will be generated for you and placed in this
256 | directory.
257 |
258 | vendor
259 | External libraries that the application depends on. Also includes the plugins
260 | subdirectory. If the app has frozen rails, those gems also go here, under
261 | vendor/rails/. This directory is in the load path.
262 |
--------------------------------------------------------------------------------