ez-settings 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +241 -0
- data/Rakefile +28 -0
- data/app/assets/config/ez_settings_manifest.js +2 -0
- data/app/assets/javascripts/ez/settings/application.js +13 -0
- data/app/assets/stylesheets/ez/settings/application.css +15 -0
- data/app/cells/ez/settings/base_cell.rb +95 -0
- data/app/cells/ez/settings/group/show.slim +17 -0
- data/app/cells/ez/settings/group_cell.rb +26 -0
- data/app/cells/ez/settings/index/show.slim +18 -0
- data/app/cells/ez/settings/index_cell.rb +4 -0
- data/app/cells/ez/settings/key/show.slim +4 -0
- data/app/cells/ez/settings/key_cell.rb +21 -0
- data/app/cells/ez/settings/nav/show.slim +9 -0
- data/app/cells/ez/settings/nav_cell.rb +13 -0
- data/app/controllers/ez/settings/application_controller.rb +11 -0
- data/app/controllers/ez/settings/settings_controller.rb +35 -0
- data/app/helpers/ez/settings/application_helper.rb +6 -0
- data/app/models/ez/settings/application_record.rb +7 -0
- data/config/routes.rb +8 -0
- data/lib/ez/settings.rb +10 -0
- data/lib/ez/settings/accessors.rb +31 -0
- data/lib/ez/settings/backend/file_system.rb +24 -0
- data/lib/ez/settings/config.rb +9 -0
- data/lib/ez/settings/engine.rb +13 -0
- data/lib/ez/settings/interface.rb +65 -0
- data/lib/ez/settings/interface/group.rb +44 -0
- data/lib/ez/settings/interface/key.rb +24 -0
- data/lib/ez/settings/request_dispatcher.rb +19 -0
- data/lib/ez/settings/routes.rb +7 -0
- data/lib/ez/settings/store.rb +80 -0
- data/lib/ez/settings/version.rb +5 -0
- data/lib/generators/ez/settings/install_generator.rb +1 -0
- metadata +259 -0
    
        checksums.yaml
    ADDED
    
    | @@ -0,0 +1,7 @@ | |
| 1 | 
            +
            ---
         | 
| 2 | 
            +
            SHA1:
         | 
| 3 | 
            +
              metadata.gz: ab92996758b0720c782a270a27937d4e62d2f84a
         | 
| 4 | 
            +
              data.tar.gz: ccad41c32652e4f590a428b7e64268e0605b1ee3
         | 
| 5 | 
            +
            SHA512:
         | 
| 6 | 
            +
              metadata.gz: 1a07335a8448a2baf82d96f22c67855aa02c24cb8b2e8a2f9f14fa3a6304cf2b53448aa717670238253efef4aaa2515975787d6dba3cd31db8cfabab5940673d
         | 
| 7 | 
            +
              data.tar.gz: a61b3c69ff16ebe41297861b902add48fe025da4e95e748610c0e89570244293a6aeb238a7fca05b25df37ff151f3f8eb50e8fbcbdae85c682df42049816e0c2
         | 
    
        data/MIT-LICENSE
    ADDED
    
    | @@ -0,0 +1,20 @@ | |
| 1 | 
            +
            Copyright 2017 Volodya Sveredyuk
         | 
| 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.
         | 
    
        data/README.md
    ADDED
    
    | @@ -0,0 +1,241 @@ | |
| 1 | 
            +
            # Ez::Settings
         | 
| 2 | 
            +
            **Ez Settings** (read as "easy settings") - one of the [ez-engines](https://github.com/ez-engines) collection that helps easily add settings interface to your [Rails](http://rubyonrails.org/) application.
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            - Flexible tool with simple DSL
         | 
| 5 | 
            +
            - Convetion over configuration principles.
         | 
| 6 | 
            +
            - Depends on [ez-core](https://github.com/ez-engines/ez-core)
         | 
| 7 | 
            +
             | 
| 8 | 
            +
            ## Installation
         | 
| 9 | 
            +
            Add this line to your application's Gemfile:
         | 
| 10 | 
            +
             | 
| 11 | 
            +
            ```ruby
         | 
| 12 | 
            +
            gem 'ez-settings'
         | 
| 13 | 
            +
            ```
         | 
| 14 | 
            +
             | 
| 15 | 
            +
            And then execute:
         | 
| 16 | 
            +
            ```bash
         | 
| 17 | 
            +
            $ bundle
         | 
| 18 | 
            +
            ```
         | 
| 19 | 
            +
             | 
| 20 | 
            +
            Or install it yourself as:
         | 
| 21 | 
            +
            ```bash
         | 
| 22 | 
            +
            $ gem install ez-settings
         | 
| 23 | 
            +
            ```
         | 
| 24 | 
            +
             | 
| 25 | 
            +
            ## Setup
         | 
| 26 | 
            +
             | 
| 27 | 
            +
            ### Initializer
         | 
| 28 | 
            +
             | 
| 29 | 
            +
            Create initializer: `config/initializers/ez_settings.rb` (rails generator would be added in 0.2 version)
         | 
| 30 | 
            +
             | 
| 31 | 
            +
            If you are boring to read all this stuff maybe this video motivates you:
         | 
| 32 | 
            +
             | 
| 33 | 
            +
            [YouTube demonstrates how easy we integrated ez-settings for our pivorak-web-app only by ±50 lines of code](https://www.youtube.com/watch?v=TiX0QDHEaKA&feature=youtu.be)
         | 
| 34 | 
            +
             | 
| 35 | 
            +
            ```ruby
         | 
| 36 | 
            +
            # By default engine try to inherit from ApplicationController, but you could change this:
         | 
| 37 | 
            +
            # Ez::Settings.config.base_controller = 'Admin::BaseController'
         | 
| 38 | 
            +
            #
         | 
| 39 | 
            +
            # Then you should define settings interfaces (you can create as much as you need)
         | 
| 40 | 
            +
            # Ierachy is pretty simple: Interface -> Group -> Key
         | 
| 41 | 
            +
            #
         | 
| 42 | 
            +
            # Interface DSL allows you to do this very declaratively
         | 
| 43 | 
            +
            #
         | 
| 44 | 
            +
            #
         | 
| 45 | 
            +
            app = Ez::Settings::Interface.define :app do         # :app - interface name
         | 
| 46 | 
            +
              group :general do                                  # :general will be name of the group
         | 
| 47 | 
            +
                key :app_title, default: -> { 'Main app title' } # :app_title will be as settings key name for store value in :general group for :app interface
         | 
| 48 | 
            +
              end
         | 
| 49 | 
            +
             | 
| 50 | 
            +
              # And so on...
         | 
| 51 | 
            +
              group :admin do
         | 
| 52 | 
            +
                key :app_title, default: -> { 'Admin app title' }
         | 
| 53 | 
            +
              end
         | 
| 54 | 
            +
             | 
| 55 | 
            +
              # If you want to see all power of the engine, add this showcase:
         | 
| 56 | 
            +
              group :showcase do
         | 
| 57 | 
            +
                key :string,                        default: -> { 'simple string' }
         | 
| 58 | 
            +
                key :bool,         type: :boolean,  default: -> { true }
         | 
| 59 | 
            +
                key :integer,      type: :integer,  default: -> { 777 }
         | 
| 60 | 
            +
                key :select,       type: :select,   default: -> { 'foo' }, collection: %w(foo bar baz)
         | 
| 61 | 
            +
                key :not_validate, required: false, presence: false
         | 
| 62 | 
            +
                key :not_for_ui,   required: false, ui:       false
         | 
| 63 | 
            +
              end
         | 
| 64 | 
            +
              # Keys could have:
         | 
| 65 | 
            +
              # :type (string by default), now ez-settings supports only: string, boolean, integer and select
         | 
| 66 | 
            +
              # :default value (as callable objects)
         | 
| 67 | 
            +
              # :required - be or not (all keys are required by default)
         | 
| 68 | 
            +
              # :ui visible or not (all keys are UI visible by default)
         | 
| 69 | 
            +
            end
         | 
| 70 | 
            +
             | 
| 71 | 
            +
            # After defining settings interface groups/keys you need to configure it:
         | 
| 72 | 
            +
            app.configure do |config|
         | 
| 73 | 
            +
              # Backend adapter to store all settings (in current version only file system)
         | 
| 74 | 
            +
              config.backend = Ez::Settings::Backend::FileSystem.new(Rails.root.join('config', 'settings.yml'))
         | 
| 75 | 
            +
             | 
| 76 | 
            +
              # Default path for redirect in controller
         | 
| 77 | 
            +
              config.default_path = '/admin/settings'
         | 
| 78 | 
            +
             | 
| 79 | 
            +
              # Pass your custom css classes through css_map config
         | 
| 80 | 
            +
              # Defaults would be merged with yours:
         | 
| 81 | 
            +
              # config.custom_css_map  = {
         | 
| 82 | 
            +
              #   nav_label:                           'ez-settings-nav-label',
         | 
| 83 | 
            +
              #   nav_menu:                            'ez-settings-nav-menu',
         | 
| 84 | 
            +
              #   nav_menu_item:                       'ez-settings-nav-menu-item',
         | 
| 85 | 
            +
              #   overview_page_wrapper:               'ez-settings-overview',
         | 
| 86 | 
            +
              #   overview_page_section:               'ez-settings-overview-section',
         | 
| 87 | 
            +
              #   overview_page_section_header:        'ez-settings-overview-section-header',
         | 
| 88 | 
            +
              #   overview_page_section_content:       'ez-settings-overview-section-content',
         | 
| 89 | 
            +
              #   overview_page_section_content_key:   'ez-settings-overview-section-content-key',
         | 
| 90 | 
            +
              #   overview_page_section_content_value: 'ez-settings-overview-section-content-value',
         | 
| 91 | 
            +
              #   group_page_wrapper:                  'ez-settings-group-wrapper',
         | 
| 92 | 
            +
              #   group_page_inner_wrapper:            'ez-settings-group-inner-wrapper',
         | 
| 93 | 
            +
              #   group_page_header:                   'ez-settings-group-header',
         | 
| 94 | 
            +
              #   group_page_form_wrapper:             'ez-settings-group-form-wrapper',
         | 
| 95 | 
            +
              #   group_page_form_inner:               'ez-settings-group-form-inner',
         | 
| 96 | 
            +
              #   group_page_form_field_row:           'ez-settings-group-form-field-row',
         | 
| 97 | 
            +
              #   group_page_form_string_wrapper:      'ez-settings-group-form-string-wrapper',
         | 
| 98 | 
            +
              #   group_page_form_boolean_wrapper:     'ez-settings-group-form-boolean-wrapper',
         | 
| 99 | 
            +
              #   group_page_form_select_wrapper:      'ez-settings-group-form-select-wrapper',
         | 
| 100 | 
            +
              #   group_page_actions_wrapper:          'ez-settings-group-actions-wrapper',
         | 
| 101 | 
            +
              #   group_page_actions_wrapper:          'ez-settings-group-actions-wrapper',
         | 
| 102 | 
            +
              #   group_page_actions_save_button:      'ez-settings-group-actions-save-btn',
         | 
| 103 | 
            +
              #   group_page_actions_cancel_link:      'ez-settings-group-actions-cancel-link'
         | 
| 104 | 
            +
              # }
         | 
| 105 | 
            +
              #
         | 
| 106 | 
            +
              # Highly recommend inspecting settings page DOM.
         | 
| 107 | 
            +
              # You can find there a lot of interesting id/class stuff
         | 
| 108 | 
            +
              #
         | 
| 109 | 
            +
              # You even can define dynamic map for allows to decide which CSS class could be added
         | 
| 110 | 
            +
              # `if` must contain callable object that receives controller as a first argument and dynamic element as second one:
         | 
| 111 | 
            +
              #
         | 
| 112 | 
            +
              # In this example, you easily could add 'active' CSS class if route end with some fragment:
         | 
| 113 | 
            +
              # config.dynamic_css_map = {
         | 
| 114 | 
            +
              #   nav_menu_item: {
         | 
| 115 | 
            +
              #     css_class: 'active',
         | 
| 116 | 
            +
              #     if: ->(controller, path_fragment) { controller.request.path.end_with?(path_fragment.to_s) }
         | 
| 117 | 
            +
              #   }
         | 
| 118 | 
            +
              # }
         | 
| 119 | 
            +
            end
         | 
| 120 | 
            +
             | 
| 121 | 
            +
            # EzSettings uses Ez::Registry from ez-core lib for storing all knowledges in one place.
         | 
| 122 | 
            +
            # This place is registry records for :settings_interfaces registry
         | 
| 123 | 
            +
            #
         | 
| 124 | 
            +
            # Register `app` variable as settings interface
         | 
| 125 | 
            +
            Ez::Registry.in(:settings_interfaces, by: PivorakWebApp) do |registry|
         | 
| 126 | 
            +
              registry.add app
         | 
| 127 | 
            +
            end
         | 
| 128 | 
            +
            ```
         | 
| 129 | 
            +
            ### Routes
         | 
| 130 | 
            +
            `config/routes.rb`
         | 
| 131 | 
            +
            ```ruby
         | 
| 132 | 
            +
            Rails.application.routes.draw do
         | 
| 133 | 
            +
              # your routes code before
         | 
| 134 | 
            +
             | 
| 135 | 
            +
              # We recommend to hide settings into admin area
         | 
| 136 | 
            +
              authenticate :user, ->(u) { u.admin? } do
         | 
| 137 | 
            +
                namespace :admin do
         | 
| 138 | 
            +
                  # :app just interface name as you registred it in Ez::Registry
         | 
| 139 | 
            +
                  ez_settings_for :app
         | 
| 140 | 
            +
                end
         | 
| 141 | 
            +
              end
         | 
| 142 | 
            +
             | 
| 143 | 
            +
              # more your routes after
         | 
| 144 | 
            +
            end
         | 
| 145 | 
            +
            ```
         | 
| 146 | 
            +
             | 
| 147 | 
            +
            This routes setup allows you to have routes like:
         | 
| 148 | 
            +
             | 
| 149 | 
            +
            `rake routes | grep settings`
         | 
| 150 | 
            +
            ```
         | 
| 151 | 
            +
             admin_ez_settings          /admin/settings  Ez::Settings::Engine {:interface=>:app}
         | 
| 152 | 
            +
             | 
| 153 | 
            +
              root GET  /                 ez/settings/settings#index
         | 
| 154 | 
            +
                   GET  /:group(.:format) ez/settings/settings#show
         | 
| 155 | 
            +
                   PUT  /:group(.:format) ez/settings/settings#update
         | 
| 156 | 
            +
            ```
         | 
| 157 | 
            +
             | 
| 158 | 
            +
            In case of example above you could route:
         | 
| 159 | 
            +
            ```
         | 
| 160 | 
            +
            GET      /admin/settings/         - overview page for all :app interface settings
         | 
| 161 | 
            +
            GET/PUT: /admin/settings/general  - general group settings
         | 
| 162 | 
            +
            GET/PUT: /admin/settings/admin    - admin group settings
         | 
| 163 | 
            +
            GET/PUT: /admin/settings/showcase - showcase group settings
         | 
| 164 | 
            +
            ```
         | 
| 165 | 
            +
             | 
| 166 | 
            +
            ### Settings accessors
         | 
| 167 | 
            +
            In your app you could access to settings values through:
         | 
| 168 | 
            +
            ```ruby
         | 
| 169 | 
            +
            Ez::Settings[:all, :general, :app_title]
         | 
| 170 | 
            +
            Ez::Settings[:all, :admin, :app_title]
         | 
| 171 | 
            +
            Ez::Settings[:all, :showcase, :select]
         | 
| 172 | 
            +
            ```
         | 
| 173 | 
            +
            Just use template: `Ez::Settings['interface', 'group', 'key']`
         | 
| 174 | 
            +
             | 
| 175 | 
            +
            In the case of missing interface/group/key you will receive one of exceptions:
         | 
| 176 | 
            +
            ```
         | 
| 177 | 
            +
            NotRegistredInterfaceError
         | 
| 178 | 
            +
            NotRegistredGroupError
         | 
| 179 | 
            +
            NotRegistredKeyError
         | 
| 180 | 
            +
            ```
         | 
| 181 | 
            +
            Be careful ;)
         | 
| 182 | 
            +
             | 
| 183 | 
            +
            ### I18n
         | 
| 184 | 
            +
             | 
| 185 | 
            +
            By default, all interfaces/groups/keys name would be humanized, but in fact, it tries to get translations from YAML first.
         | 
| 186 | 
            +
             | 
| 187 | 
            +
            If you need, create locale file with this structure:
         | 
| 188 | 
            +
             | 
| 189 | 
            +
            `config/locales/ez-settings.en.yml`
         | 
| 190 | 
            +
            ```yaml
         | 
| 191 | 
            +
              en:
         | 
| 192 | 
            +
                ez_settings:
         | 
| 193 | 
            +
                  label: Ez Settings
         | 
| 194 | 
            +
                  interfaces:
         | 
| 195 | 
            +
                    app:
         | 
| 196 | 
            +
                      label: App Settings
         | 
| 197 | 
            +
                      actions:
         | 
| 198 | 
            +
                        save:
         | 
| 199 | 
            +
                          label: Save Settings
         | 
| 200 | 
            +
                        cancel:
         | 
| 201 | 
            +
                          label: Cancel Settings
         | 
| 202 | 
            +
                      groups:
         | 
| 203 | 
            +
                        general:
         | 
| 204 | 
            +
                          label: General
         | 
| 205 | 
            +
                        admin:
         | 
| 206 | 
            +
                          label: Admin
         | 
| 207 | 
            +
                        showcase:
         | 
| 208 | 
            +
                          label: Showcase
         | 
| 209 | 
            +
                          keys:
         | 
| 210 | 
            +
                            string:
         | 
| 211 | 
            +
                              label: String
         | 
| 212 | 
            +
                            bool:
         | 
| 213 | 
            +
                              label: Bool
         | 
| 214 | 
            +
                            integer:
         | 
| 215 | 
            +
                              label: Integer
         | 
| 216 | 
            +
                            select:
         | 
| 217 | 
            +
                              label: Select
         | 
| 218 | 
            +
                            not_validate:
         | 
| 219 | 
            +
                              label: Not Validate
         | 
| 220 | 
            +
                            not_for_ui:
         | 
| 221 | 
            +
                              label: Not For UI
         | 
| 222 | 
            +
            ```
         | 
| 223 | 
            +
             | 
| 224 | 
            +
            ## TODO
         | 
| 225 | 
            +
            This features will be implemented in upcoming 0.2 and 0.3 releases:
         | 
| 226 | 
            +
            - `rails g ez:settings:install` it creates `config/ez_settings.rb` and `config/locales/ez-settings.en.yml`
         | 
| 227 | 
            +
            - Scoped settings (`:scope_id`, `:scope_type`)
         | 
| 228 | 
            +
            - groups as defined methods for interface
         | 
| 229 | 
            +
            - controller before actions as configured callbacks
         | 
| 230 | 
            +
            - JSON API endpoints
         | 
| 231 | 
            +
            - Allow to config endpoints
         | 
| 232 | 
            +
            - Interface description (and show at UI)
         | 
| 233 | 
            +
            - Groups description (and show at UI)
         | 
| 234 | 
            +
            - Database storage as backend
         | 
| 235 | 
            +
            - Redis storage as backend
         | 
| 236 | 
            +
             | 
| 237 | 
            +
            ## Contributing
         | 
| 238 | 
            +
            Fork => Fix => MR warmly welcomed!
         | 
| 239 | 
            +
             | 
| 240 | 
            +
            ## License
         | 
| 241 | 
            +
            The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
         | 
    
        data/Rakefile
    ADDED
    
    | @@ -0,0 +1,28 @@ | |
| 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    = 'Ez::Settings'
         | 
| 12 | 
            +
              rdoc.options << '--line-numbers'
         | 
| 13 | 
            +
              rdoc.rdoc_files.include('README.md')
         | 
| 14 | 
            +
              rdoc.rdoc_files.include('lib/**/*.rb')
         | 
| 15 | 
            +
            end
         | 
| 16 | 
            +
             | 
| 17 | 
            +
            APP_RAKEFILE = File.expand_path("../spec/dummy/Rakefile", __FILE__)
         | 
| 18 | 
            +
            load 'rails/tasks/engine.rake'
         | 
| 19 | 
            +
             | 
| 20 | 
            +
            load 'rails/tasks/statistics.rake'
         | 
| 21 | 
            +
             | 
| 22 | 
            +
            require 'bundler/gem_tasks'
         | 
| 23 | 
            +
             | 
| 24 | 
            +
            require "rspec/core/rake_task"
         | 
| 25 | 
            +
             | 
| 26 | 
            +
            RSpec::Core::RakeTask.new(:spec)
         | 
| 27 | 
            +
             | 
| 28 | 
            +
            task :default => :spec
         | 
| @@ -0,0 +1,13 @@ | |
| 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 any plugin's vendor/assets/javascripts directory 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 | 
            +
            // compiled file. JavaScript code in this file should be added after the last require_* statement.
         | 
| 9 | 
            +
            //
         | 
| 10 | 
            +
            // Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
         | 
| 11 | 
            +
            // about supported directives.
         | 
| 12 | 
            +
            //
         | 
| 13 | 
            +
            //= require_tree .
         | 
| @@ -0,0 +1,15 @@ | |
| 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 any plugin's vendor/assets/stylesheets directory 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 bottom of the
         | 
| 9 | 
            +
             * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
         | 
| 10 | 
            +
             * files in this directory. Styles in this file should be added after the last require_* statement.
         | 
| 11 | 
            +
             * It is generally better to create a new file per style scope.
         | 
| 12 | 
            +
             *
         | 
| 13 | 
            +
             *= require_tree .
         | 
| 14 | 
            +
             *= require_self
         | 
| 15 | 
            +
             */
         | 
| @@ -0,0 +1,95 @@ | |
| 1 | 
            +
            module Ez::Settings
         | 
| 2 | 
            +
              class BaseCell < Cell::ViewModel
         | 
| 3 | 
            +
                include RequestDispatcher
         | 
| 4 | 
            +
             | 
| 5 | 
            +
                self.view_paths = ["#{Ez::Settings::Engine.root}/app/cells"]
         | 
| 6 | 
            +
             | 
| 7 | 
            +
                delegate :t, to: I18n
         | 
| 8 | 
            +
                delegate :params, :request, to: :controller
         | 
| 9 | 
            +
                delegate :dynamic_css_map,  to: :'interface.config'
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                SCOPE      = 'ez_settings'
         | 
| 12 | 
            +
                LABEL      = 'label'
         | 
| 13 | 
            +
                INTERFACES = 'interfaces'
         | 
| 14 | 
            +
                GROUPS     = 'groups'
         | 
| 15 | 
            +
                KEYS       = 'keys'
         | 
| 16 | 
            +
                ACTIONS    = 'actions'
         | 
| 17 | 
            +
                SAVE       = 'save'
         | 
| 18 | 
            +
                CANCEL     = 'cancel'
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                def self.form
         | 
| 21 | 
            +
                  include ActionView::Helpers::FormHelper
         | 
| 22 | 
            +
                  include SimpleForm::ActionViewExtensions::FormHelper
         | 
| 23 | 
            +
                  include ActionView::RecordIdentifier
         | 
| 24 | 
            +
                  include ActionView::Helpers::FormOptionsHelper
         | 
| 25 | 
            +
                end
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                def self.option(name, default: nil)
         | 
| 28 | 
            +
                  define_method name do
         | 
| 29 | 
            +
                    options[name]
         | 
| 30 | 
            +
                  end
         | 
| 31 | 
            +
                end
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                def css_for(item, dynamic: nil)
         | 
| 34 | 
            +
                  return "ez-settings-defined-#{item}" unless css_map[item]
         | 
| 35 | 
            +
                  return css_map[item] unless dynamic
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                  if dynamic_css_map.dig(item, :if)&.call(controller, dynamic)
         | 
| 38 | 
            +
                    dynamic_css_map.dig(item, :css_class) + ' ' + css_map[item]
         | 
| 39 | 
            +
                  else
         | 
| 40 | 
            +
                    css_map[item]
         | 
| 41 | 
            +
                  end
         | 
| 42 | 
            +
                end
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                def css_map
         | 
| 45 | 
            +
                  {
         | 
| 46 | 
            +
                    nav_label:                           'ez-settings-nav-label',
         | 
| 47 | 
            +
                    nav_menu:                            'ez-settings-nav-menu',
         | 
| 48 | 
            +
                    nav_menu_item:                       'ez-settings-nav-menu-item',
         | 
| 49 | 
            +
                    overview_page_wrapper:               'ez-settings-overview',
         | 
| 50 | 
            +
                    overview_page_section:               'ez-settings-overview-section',
         | 
| 51 | 
            +
                    overview_page_section_header:        'ez-settings-overview-section-header',
         | 
| 52 | 
            +
                    overview_page_section_content:       'ez-settings-overview-section-content',
         | 
| 53 | 
            +
                    overview_page_section_content_key:   'ez-settings-overview-section-content-key',
         | 
| 54 | 
            +
                    overview_page_section_content_value: 'ez-settings-overview-section-content-value',
         | 
| 55 | 
            +
                    group_page_wrapper:                  'ez-settings-group-wrapper',
         | 
| 56 | 
            +
                    group_page_inner_wrapper:            'ez-settings-group-inner-wrapper',
         | 
| 57 | 
            +
                    group_page_header:                   'ez-settings-group-header',
         | 
| 58 | 
            +
                    group_page_form_wrapper:             'ez-settings-group-form-wrapper',
         | 
| 59 | 
            +
                    group_page_form_inner:               'ez-settings-group-form-inner',
         | 
| 60 | 
            +
                    group_page_form_field_row:           'ez-settings-group-form-field-row',
         | 
| 61 | 
            +
                    group_page_form_string_wrapper:      'ez-settings-group-form-string-wrapper',
         | 
| 62 | 
            +
                    group_page_form_boolean_wrapper:     'ez-settings-group-form-boolean-wrapper',
         | 
| 63 | 
            +
                    group_page_form_select_wrapper:      'ez-settings-group-form-select-wrapper',
         | 
| 64 | 
            +
                    group_page_actions_wrapper:          'ez-settings-group-actions-wrapper',
         | 
| 65 | 
            +
                    group_page_actions_wrapper:          'ez-settings-group-actions-wrapper',
         | 
| 66 | 
            +
                    group_page_actions_save_button:      'ez-settings-group-actions-save-btn',
         | 
| 67 | 
            +
                    group_page_actions_cancel_link:      'ez-settings-group-actions-cancel-link',
         | 
| 68 | 
            +
                  }.merge(interface.config.custom_css_map)
         | 
| 69 | 
            +
                end
         | 
| 70 | 
            +
             | 
| 71 | 
            +
                def controller
         | 
| 72 | 
            +
                  context[:controller]
         | 
| 73 | 
            +
                end
         | 
| 74 | 
            +
             | 
| 75 | 
            +
                def group_link(group, options = {})
         | 
| 76 | 
            +
                  link_to i18n_group_label(group),
         | 
| 77 | 
            +
                    group_path(group),
         | 
| 78 | 
            +
                    class: css_for(:nav_menu_item, dynamic: "settings/#{group.name}")
         | 
| 79 | 
            +
                end
         | 
| 80 | 
            +
             | 
| 81 | 
            +
                def group_path(group)
         | 
| 82 | 
            +
                  "#{interface.config.default_path}/#{group.name}"
         | 
| 83 | 
            +
                end
         | 
| 84 | 
            +
             | 
| 85 | 
            +
                def i18n_group_label(group)
         | 
| 86 | 
            +
                  t(LABEL, scope:   [SCOPE, INTERFACES, group.interface, GROUPS, group.name],
         | 
| 87 | 
            +
                           default: group.name.to_s.humanize)
         | 
| 88 | 
            +
                end
         | 
| 89 | 
            +
             | 
| 90 | 
            +
                def i18n_key_label(key)
         | 
| 91 | 
            +
                  t(LABEL, scope:   [SCOPE, INTERFACES, key.interface, GROUPS, key.group, KEYS, key.name],
         | 
| 92 | 
            +
                           default: key.name.to_s.humanize)
         | 
| 93 | 
            +
                end
         | 
| 94 | 
            +
              end
         | 
| 95 | 
            +
            end
         | 
| @@ -0,0 +1,17 @@ | |
| 1 | 
            +
            #ez-settings
         | 
| 2 | 
            +
              = cell 'ez/settings/nav'
         | 
| 3 | 
            +
             | 
| 4 | 
            +
              div class="#{css_for :group_page_wrapper} ez-settings-group-#{group.name}"
         | 
| 5 | 
            +
                div class=(css_for :group_page_inner_wrapper)
         | 
| 6 | 
            +
                  div class=(css_for :group_page_header)
         | 
| 7 | 
            +
                    = i18n_group_label(group)
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                  div class=(css_for :group_page_form_wrapper)
         | 
| 10 | 
            +
                    = simple_form_for store, as: :settings, url: form_url, method: :put do |f|
         | 
| 11 | 
            +
                      div class=(css_for :group_page_form_inner)
         | 
| 12 | 
            +
                        - ui_keys.each do |key|
         | 
| 13 | 
            +
                          = cell 'ez/settings/key', key, form: f
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                        div class=(css_for :group_page_actions_wrapper)
         | 
| 16 | 
            +
                          = save_button(f)
         | 
| 17 | 
            +
                          = cancel_link
         | 
| @@ -0,0 +1,26 @@ | |
| 1 | 
            +
            module Ez::Settings
         | 
| 2 | 
            +
              class GroupCell < BaseCell
         | 
| 3 | 
            +
                form
         | 
| 4 | 
            +
             | 
| 5 | 
            +
                property :ui_keys
         | 
| 6 | 
            +
                option :store
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                def form_url
         | 
| 9 | 
            +
                  "#{interface.config.default_path}/#{model.name}"
         | 
| 10 | 
            +
                end
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                def save_button(form)
         | 
| 13 | 
            +
                  form.button :submit,
         | 
| 14 | 
            +
                    t(LABEL, scope:   [SCOPE, INTERFACES, model.interface, ACTIONS, SAVE],
         | 
| 15 | 
            +
                             default: 'Save'),
         | 
| 16 | 
            +
                    class: css_for(:group_page_actions_save_button)
         | 
| 17 | 
            +
                end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                def cancel_link
         | 
| 20 | 
            +
                  link_to t(LABEL, scope:   [SCOPE, INTERFACES, model.interface, ACTIONS,CANCEL],
         | 
| 21 | 
            +
                                   default: 'Cancel'),
         | 
| 22 | 
            +
                    interface.config.default_path,
         | 
| 23 | 
            +
                    class: css_for(:group_page_actions_cancel_link)
         | 
| 24 | 
            +
                end
         | 
| 25 | 
            +
              end
         | 
| 26 | 
            +
            end
         | 
| @@ -0,0 +1,18 @@ | |
| 1 | 
            +
            #ez-settings
         | 
| 2 | 
            +
              = cell 'ez/settings/nav'
         | 
| 3 | 
            +
             | 
| 4 | 
            +
              div class="#{css_for :overview_page_wrapper}"
         | 
| 5 | 
            +
                - interface.groups.each do |group|
         | 
| 6 | 
            +
             | 
| 7 | 
            +
                  div class="#{css_for :overview_page_section}"
         | 
| 8 | 
            +
                    div class="#{css_for :overview_page_section_header}"
         | 
| 9 | 
            +
                      = group_link(group)
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                    - group.ui_keys.each do |key|
         | 
| 12 | 
            +
                      div class="#{css_for :overview_page_section_content}"
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                        div class="#{css_for :overview_page_section_content_key}"
         | 
| 15 | 
            +
                          = i18n_key_label(key)
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                        div class="#{css_for :overview_page_section_content_value}"
         | 
| 18 | 
            +
                          = group.store(interface.config.backend).send(key.name)
         | 
| @@ -0,0 +1,21 @@ | |
| 1 | 
            +
            module Ez::Settings
         | 
| 2 | 
            +
              class KeyCell < BaseCell
         | 
| 3 | 
            +
                form
         | 
| 4 | 
            +
             | 
| 5 | 
            +
                option :form
         | 
| 6 | 
            +
             | 
| 7 | 
            +
                private
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                def html_options
         | 
| 10 | 
            +
                  {
         | 
| 11 | 
            +
                    label:           i18n_key_label(model),
         | 
| 12 | 
            +
                    as:              model.type,
         | 
| 13 | 
            +
                    collection:      model.collection,
         | 
| 14 | 
            +
                    include_blank:   model.default.present?,
         | 
| 15 | 
            +
                    required:        model.required?,
         | 
| 16 | 
            +
                    checked_value:   TRUE.to_s,
         | 
| 17 | 
            +
                    unchecked_value: FALSE.to_s
         | 
| 18 | 
            +
                  }.merge(model.options)
         | 
| 19 | 
            +
                end
         | 
| 20 | 
            +
              end
         | 
| 21 | 
            +
            end
         | 
| @@ -0,0 +1,13 @@ | |
| 1 | 
            +
            module Ez::Settings
         | 
| 2 | 
            +
              class NavCell < BaseCell
         | 
| 3 | 
            +
                def nav_label
         | 
| 4 | 
            +
                  t(LABEL, scope: SCOPE, default: 'Settings')
         | 
| 5 | 
            +
                end
         | 
| 6 | 
            +
             | 
| 7 | 
            +
                def settings_link
         | 
| 8 | 
            +
                  link_to nav_label,
         | 
| 9 | 
            +
                    interface.config.default_path,
         | 
| 10 | 
            +
                    class: css_for(:nav_menu_item, dynamic: 'settings/')
         | 
| 11 | 
            +
                end
         | 
| 12 | 
            +
              end
         | 
| 13 | 
            +
            end
         | 
| @@ -0,0 +1,11 @@ | |
| 1 | 
            +
            module Ez
         | 
| 2 | 
            +
              module Settings
         | 
| 3 | 
            +
                class ApplicationController < Ez::Settings.config.base_controller.constantize
         | 
| 4 | 
            +
                  protect_from_forgery with: :exception
         | 
| 5 | 
            +
             | 
| 6 | 
            +
                  def view(cell_name, *args)
         | 
| 7 | 
            +
                    render html: cell("ez/settings/#{cell_name}", *args), layout: true
         | 
| 8 | 
            +
                  end
         | 
| 9 | 
            +
                end
         | 
| 10 | 
            +
              end
         | 
| 11 | 
            +
            end
         | 
| @@ -0,0 +1,35 @@ | |
| 1 | 
            +
            module Ez
         | 
| 2 | 
            +
              module Settings
         | 
| 3 | 
            +
                class SettingsController < Ez::Settings::ApplicationController
         | 
| 4 | 
            +
                  include Ez::Settings::RequestDispatcher
         | 
| 5 | 
            +
             | 
| 6 | 
            +
                  def index
         | 
| 7 | 
            +
                    view :index
         | 
| 8 | 
            +
                  end
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                  def show
         | 
| 11 | 
            +
                    render_group(group, group.store(backend))
         | 
| 12 | 
            +
                  end
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                  def update
         | 
| 15 | 
            +
                    store = group.store(backend).update(params[:settings])
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                    if store.valid?
         | 
| 18 | 
            +
                      redirect_to interface.config.default_path
         | 
| 19 | 
            +
                    else
         | 
| 20 | 
            +
                      render_group(group, store)
         | 
| 21 | 
            +
                    end
         | 
| 22 | 
            +
                  end
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                  private
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                  def render_group(group, store)
         | 
| 27 | 
            +
                    view :group, group, store: store
         | 
| 28 | 
            +
                  end
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                  def backend
         | 
| 31 | 
            +
                    interface.config.backend
         | 
| 32 | 
            +
                  end
         | 
| 33 | 
            +
                end
         | 
| 34 | 
            +
              end
         | 
| 35 | 
            +
            end
         | 
    
        data/config/routes.rb
    ADDED
    
    
    
        data/lib/ez/settings.rb
    ADDED
    
    
| @@ -0,0 +1,31 @@ | |
| 1 | 
            +
            module Ez::Settings
         | 
| 2 | 
            +
              NotRegistredInterfaceError = Class.new(StandardError)
         | 
| 3 | 
            +
              NotRegistredGroupError     = Class.new(StandardError)
         | 
| 4 | 
            +
              NotRegistredKeyError       = Class.new(StandardError)
         | 
| 5 | 
            +
             | 
| 6 | 
            +
              module Accessors
         | 
| 7 | 
            +
                def [](interface_name, group_name, key_name)
         | 
| 8 | 
            +
                  interface = Ez::Registry.data(:settings_interfaces).find do |interface|
         | 
| 9 | 
            +
                    interface.name == interface_name.to_sym
         | 
| 10 | 
            +
                  end
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                  unless interface
         | 
| 13 | 
            +
                    raise NotRegistredInterfaceError, "Interface #{interface_name} is not registred!"
         | 
| 14 | 
            +
                  end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                  group = interface.groups.find { |g| g.name == group_name.to_sym }
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                  unless group
         | 
| 19 | 
            +
                    raise NotRegistredGroupError, "Group #{group_name} is not registred for #{interface_name} interface"
         | 
| 20 | 
            +
                  end
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                  store = Ez::Settings::Store.new(group, interface.config.backend)
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                  begin
         | 
| 25 | 
            +
                    store.send(key_name.to_sym)
         | 
| 26 | 
            +
                  rescue NoMethodError
         | 
| 27 | 
            +
                    raise NotRegistredKeyError, "Key #{key_name} is not registred for #{interface_name} interface, #{group_name} group"
         | 
| 28 | 
            +
                  end
         | 
| 29 | 
            +
                end
         | 
| 30 | 
            +
              end
         | 
| 31 | 
            +
            end
         | 
| @@ -0,0 +1,24 @@ | |
| 1 | 
            +
            require 'yaml'
         | 
| 2 | 
            +
            require 'active_support/hash_with_indifferent_access'
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            module Ez::Settings
         | 
| 5 | 
            +
              module Backend
         | 
| 6 | 
            +
                class FileSystem
         | 
| 7 | 
            +
                  attr_reader :file
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                  def initialize(file)
         | 
| 10 | 
            +
                    @file = file
         | 
| 11 | 
            +
                  end
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                  def read
         | 
| 14 | 
            +
                    return {} unless File.exist?(file)
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                    YAML.load_file(file).deep_symbolize_keys
         | 
| 17 | 
            +
                  end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                  def write(data)
         | 
| 20 | 
            +
                    File.write(file, read.merge(data).deep_stringify_keys.to_yaml)
         | 
| 21 | 
            +
                  end
         | 
| 22 | 
            +
                end
         | 
| 23 | 
            +
              end
         | 
| 24 | 
            +
            end
         | 
| @@ -0,0 +1,65 @@ | |
| 1 | 
            +
            require_relative 'interface/group'
         | 
| 2 | 
            +
            require_relative 'interface/key'
         | 
| 3 | 
            +
            require_relative 'store'
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            require 'ez/configurator'
         | 
| 6 | 
            +
            require 'ez/settings/backend/file_system'
         | 
| 7 | 
            +
             | 
| 8 | 
            +
            module Ez::Settings
         | 
| 9 | 
            +
              class Interface
         | 
| 10 | 
            +
                include Ez::Configurator
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                configure do |config|
         | 
| 13 | 
            +
                  config.base_controller = 'ApplicationController'
         | 
| 14 | 
            +
                  config.default_path    = '/settings'
         | 
| 15 | 
            +
                  config.backend         = Backend::FileSystem.new('settings.yml')
         | 
| 16 | 
            +
                  config.custom_css_map  = {}
         | 
| 17 | 
            +
                  config.dynamic_css_map = {}
         | 
| 18 | 
            +
                end
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                def self.define(name, &block)
         | 
| 21 | 
            +
                  interface = new(name)
         | 
| 22 | 
            +
                  interface.instance_eval(&block)
         | 
| 23 | 
            +
                  interface
         | 
| 24 | 
            +
                end
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                delegate :config, :configure, to: :class
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                attr_reader :name, :groups, :store
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                def initialize(name)
         | 
| 31 | 
            +
                  @name = name
         | 
| 32 | 
            +
                  @keys, @groups = [], []
         | 
| 33 | 
            +
                end
         | 
| 34 | 
            +
             | 
| 35 | 
            +
                def define(&block)
         | 
| 36 | 
            +
                  self.instance_eval(&block)
         | 
| 37 | 
            +
                end
         | 
| 38 | 
            +
             | 
| 39 | 
            +
                def group(name, &block)
         | 
| 40 | 
            +
                  find_or_initialize_group(name, &block)
         | 
| 41 | 
            +
                end
         | 
| 42 | 
            +
             | 
| 43 | 
            +
                def keys
         | 
| 44 | 
            +
                  groups.map(&:keys).flatten
         | 
| 45 | 
            +
                end
         | 
| 46 | 
            +
             | 
| 47 | 
            +
                private
         | 
| 48 | 
            +
             | 
| 49 | 
            +
                def find_or_initialize_group(name, &block)
         | 
| 50 | 
            +
                  existing_group = groups.find { |g| g.name == name }
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                  if existing_group
         | 
| 53 | 
            +
                    existing_group.instance_eval(&block)
         | 
| 54 | 
            +
                  else
         | 
| 55 | 
            +
                    add_group(Group.new(name, self.name, &block))
         | 
| 56 | 
            +
                  end
         | 
| 57 | 
            +
                end
         | 
| 58 | 
            +
             | 
| 59 | 
            +
                def add_group(group)
         | 
| 60 | 
            +
                  @groups << group
         | 
| 61 | 
            +
             | 
| 62 | 
            +
                  group
         | 
| 63 | 
            +
                end
         | 
| 64 | 
            +
              end
         | 
| 65 | 
            +
            end
         | 
| @@ -0,0 +1,44 @@ | |
| 1 | 
            +
            require_relative 'key'
         | 
| 2 | 
            +
            require_relative '../store'
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            module Ez::Settings
         | 
| 5 | 
            +
              class Interface
         | 
| 6 | 
            +
                class Group
         | 
| 7 | 
            +
                  OverwriteKeyError = Class.new(StandardError)
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                  attr_reader :name, :options, :keys, :store, :interface
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                  def initialize(name, interface, options = {}, &block)
         | 
| 12 | 
            +
                    @name      = name
         | 
| 13 | 
            +
                    @interface = interface
         | 
| 14 | 
            +
                    @options   = options
         | 
| 15 | 
            +
                    @keys      = []
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                    instance_eval(&block)
         | 
| 18 | 
            +
                  end
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                  def key(key_name, params = {})
         | 
| 21 | 
            +
                    prevent_key_rewrite!(key_name)
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                    keys << Interface::Key.new(key_name, params.merge(group: name, interface: interface))
         | 
| 24 | 
            +
                    keys
         | 
| 25 | 
            +
                  end
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                  def ui_keys
         | 
| 28 | 
            +
                    keys.select(&:ui?)
         | 
| 29 | 
            +
                  end
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                  def store(backend)
         | 
| 32 | 
            +
                    Ez::Settings::Store.new(self, backend)
         | 
| 33 | 
            +
                  end
         | 
| 34 | 
            +
             | 
| 35 | 
            +
                  private
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                  def prevent_key_rewrite!(key_name)
         | 
| 38 | 
            +
                    return unless keys.map(&:name).include?(key_name)
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                    raise OverwriteKeyError, "Key #{key_name} already registred in #{name} group"
         | 
| 41 | 
            +
                  end
         | 
| 42 | 
            +
                end
         | 
| 43 | 
            +
              end
         | 
| 44 | 
            +
            end
         | 
| @@ -0,0 +1,24 @@ | |
| 1 | 
            +
            module Ez::Settings
         | 
| 2 | 
            +
              class Interface
         | 
| 3 | 
            +
                class Key
         | 
| 4 | 
            +
                  attr_reader :name, :group, :interface, :type, :default,
         | 
| 5 | 
            +
                              :ui, :required, :collection, :options
         | 
| 6 | 
            +
             | 
| 7 | 
            +
                  def initialize(name, params)
         | 
| 8 | 
            +
                    @name       = name
         | 
| 9 | 
            +
                    @group      = params.fetch(:group)
         | 
| 10 | 
            +
                    @interface  = params.fetch(:interface)
         | 
| 11 | 
            +
                    @type       = params.fetch(:type, :string)
         | 
| 12 | 
            +
                    @default    = params.fetch(:default, -> {}).call
         | 
| 13 | 
            +
                    @ui         = params.fetch(:ui, true)
         | 
| 14 | 
            +
                    @required   = params.fetch(:required, true)
         | 
| 15 | 
            +
                    @collection = params.fetch(:collection, [])
         | 
| 16 | 
            +
                    @options    = params.fetch(:options, {})
         | 
| 17 | 
            +
                  end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                  # Alias all boolean-like options to predicates methods, please
         | 
| 20 | 
            +
                  alias_method :ui?,       :ui
         | 
| 21 | 
            +
                  alias_method :required?, :required
         | 
| 22 | 
            +
                end
         | 
| 23 | 
            +
              end
         | 
| 24 | 
            +
            end
         | 
| @@ -0,0 +1,19 @@ | |
| 1 | 
            +
            module Ez
         | 
| 2 | 
            +
              module Settings
         | 
| 3 | 
            +
                module RequestDispatcher
         | 
| 4 | 
            +
                  # should implement :params
         | 
| 5 | 
            +
             | 
| 6 | 
            +
                  def interface
         | 
| 7 | 
            +
                    # TODO: add raise exception in nil
         | 
| 8 | 
            +
                    Ez::Registry.data(:settings_interfaces).find do |interface|
         | 
| 9 | 
            +
                      interface.name ==  params[:interface].to_sym
         | 
| 10 | 
            +
                    end
         | 
| 11 | 
            +
                  end
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                  def group
         | 
| 14 | 
            +
                    # TODO: add raise exception if nil
         | 
| 15 | 
            +
                    interface.groups.find { |g| g.name == params[:group].to_sym }
         | 
| 16 | 
            +
                  end
         | 
| 17 | 
            +
                end
         | 
| 18 | 
            +
              end
         | 
| 19 | 
            +
            end
         | 
| @@ -0,0 +1,80 @@ | |
| 1 | 
            +
            require 'active_model'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require 'ez/settings/config'
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            module Ez::Settings
         | 
| 6 | 
            +
              class Store
         | 
| 7 | 
            +
                delegate :keys, to: :group
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                attr_reader :group, :errors, :backend
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                def initialize(group, backend)
         | 
| 12 | 
            +
                  @group   = group
         | 
| 13 | 
            +
                  @errors  = ActiveModel::Errors.new(self)
         | 
| 14 | 
            +
                  @backend = backend
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                  define_accessors
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                  keys.each { |key| default_or_exists_value(data, key) }
         | 
| 19 | 
            +
                end
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                def validate
         | 
| 22 | 
            +
                  @errors = ActiveModel::Errors.new(self)
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                  group.keys.select(&:required?).each do |key|
         | 
| 25 | 
            +
                    errors.add(key.name, "can't be blank") if self.send(key.name).blank?
         | 
| 26 | 
            +
                  end
         | 
| 27 | 
            +
                end
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                def valid?
         | 
| 30 | 
            +
                  errors.empty?
         | 
| 31 | 
            +
                end
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                def invalid?
         | 
| 34 | 
            +
                  !valid?
         | 
| 35 | 
            +
                end
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                def update(params)
         | 
| 38 | 
            +
                  params.each { |key, value| self.public_send("#{key}=", value) }
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                  validate
         | 
| 41 | 
            +
                  return self unless errors.empty?
         | 
| 42 | 
            +
             | 
| 43 | 
            +
                  backend.write(schema)
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                  self
         | 
| 46 | 
            +
                end
         | 
| 47 | 
            +
             | 
| 48 | 
            +
                def schema
         | 
| 49 | 
            +
                  {
         | 
| 50 | 
            +
                    group.name => group.keys.map(&:name).each_with_object({}) do |key_name, schema|
         | 
| 51 | 
            +
                      schema[key_name] = send(key_name)
         | 
| 52 | 
            +
                    end
         | 
| 53 | 
            +
                  }
         | 
| 54 | 
            +
                end
         | 
| 55 | 
            +
             | 
| 56 | 
            +
                private
         | 
| 57 | 
            +
             | 
| 58 | 
            +
                def default_or_exists_value(from_data, key)
         | 
| 59 | 
            +
                  value = from_data[key.name].nil? ? key.default : from_data[key.name]
         | 
| 60 | 
            +
             | 
| 61 | 
            +
                  public_send("#{key.name}=", value)
         | 
| 62 | 
            +
                end
         | 
| 63 | 
            +
             | 
| 64 | 
            +
                def data
         | 
| 65 | 
            +
                  @data ||= backend.read[group.name] || {}
         | 
| 66 | 
            +
                end
         | 
| 67 | 
            +
             | 
| 68 | 
            +
                def define_accessors
         | 
| 69 | 
            +
                  group.keys.map(&:name).each do |name|
         | 
| 70 | 
            +
                    define_singleton_method(name) do
         | 
| 71 | 
            +
                      instance_variable_get("@#{name}")
         | 
| 72 | 
            +
                    end
         | 
| 73 | 
            +
             | 
| 74 | 
            +
                    define_singleton_method("#{name}=") do |value|
         | 
| 75 | 
            +
                      instance_variable_set("@#{name}", value)
         | 
| 76 | 
            +
                    end
         | 
| 77 | 
            +
                  end
         | 
| 78 | 
            +
                end
         | 
| 79 | 
            +
              end
         | 
| 80 | 
            +
            end
         | 
| @@ -0,0 +1 @@ | |
| 1 | 
            +
            # TODO @VS [0.2]
         | 
    
        metadata
    ADDED
    
    | @@ -0,0 +1,259 @@ | |
| 1 | 
            +
            --- !ruby/object:Gem::Specification
         | 
| 2 | 
            +
            name: ez-settings
         | 
| 3 | 
            +
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            +
              version: 0.1.0
         | 
| 5 | 
            +
            platform: ruby
         | 
| 6 | 
            +
            authors:
         | 
| 7 | 
            +
            - Volodya Sveredyuk
         | 
| 8 | 
            +
            autorequire: 
         | 
| 9 | 
            +
            bindir: bin
         | 
| 10 | 
            +
            cert_chain: []
         | 
| 11 | 
            +
            date: 2017-06-28 00:00:00.000000000 Z
         | 
| 12 | 
            +
            dependencies:
         | 
| 13 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 14 | 
            +
              name: ez-core
         | 
| 15 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 16 | 
            +
                requirements:
         | 
| 17 | 
            +
                - - "~>"
         | 
| 18 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 19 | 
            +
                    version: 0.1.1
         | 
| 20 | 
            +
              type: :runtime
         | 
| 21 | 
            +
              prerelease: false
         | 
| 22 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 23 | 
            +
                requirements:
         | 
| 24 | 
            +
                - - "~>"
         | 
| 25 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 26 | 
            +
                    version: 0.1.1
         | 
| 27 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 28 | 
            +
              name: rails
         | 
| 29 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 30 | 
            +
                requirements:
         | 
| 31 | 
            +
                - - "~>"
         | 
| 32 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 33 | 
            +
                    version: 5.0.0
         | 
| 34 | 
            +
              type: :runtime
         | 
| 35 | 
            +
              prerelease: false
         | 
| 36 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 37 | 
            +
                requirements:
         | 
| 38 | 
            +
                - - "~>"
         | 
| 39 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 40 | 
            +
                    version: 5.0.0
         | 
| 41 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 42 | 
            +
              name: cells-rails
         | 
| 43 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 44 | 
            +
                requirements:
         | 
| 45 | 
            +
                - - "~>"
         | 
| 46 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 47 | 
            +
                    version: 0.0.8
         | 
| 48 | 
            +
              type: :runtime
         | 
| 49 | 
            +
              prerelease: false
         | 
| 50 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 51 | 
            +
                requirements:
         | 
| 52 | 
            +
                - - "~>"
         | 
| 53 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 54 | 
            +
                    version: 0.0.8
         | 
| 55 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 56 | 
            +
              name: cells-slim
         | 
| 57 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 58 | 
            +
                requirements:
         | 
| 59 | 
            +
                - - "~>"
         | 
| 60 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 61 | 
            +
                    version: 0.0.5
         | 
| 62 | 
            +
              type: :runtime
         | 
| 63 | 
            +
              prerelease: false
         | 
| 64 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 65 | 
            +
                requirements:
         | 
| 66 | 
            +
                - - "~>"
         | 
| 67 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 68 | 
            +
                    version: 0.0.5
         | 
| 69 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 70 | 
            +
              name: simple_form
         | 
| 71 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 72 | 
            +
                requirements:
         | 
| 73 | 
            +
                - - "~>"
         | 
| 74 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 75 | 
            +
                    version: 3.5.0
         | 
| 76 | 
            +
              type: :runtime
         | 
| 77 | 
            +
              prerelease: false
         | 
| 78 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 79 | 
            +
                requirements:
         | 
| 80 | 
            +
                - - "~>"
         | 
| 81 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 82 | 
            +
                    version: 3.5.0
         | 
| 83 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 84 | 
            +
              name: sqlite3
         | 
| 85 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 86 | 
            +
                requirements:
         | 
| 87 | 
            +
                - - ">="
         | 
| 88 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 89 | 
            +
                    version: '0'
         | 
| 90 | 
            +
              type: :development
         | 
| 91 | 
            +
              prerelease: false
         | 
| 92 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 93 | 
            +
                requirements:
         | 
| 94 | 
            +
                - - ">="
         | 
| 95 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 96 | 
            +
                    version: '0'
         | 
| 97 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 98 | 
            +
              name: rspec-rails
         | 
| 99 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 100 | 
            +
                requirements:
         | 
| 101 | 
            +
                - - ">="
         | 
| 102 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 103 | 
            +
                    version: '0'
         | 
| 104 | 
            +
              type: :development
         | 
| 105 | 
            +
              prerelease: false
         | 
| 106 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 107 | 
            +
                requirements:
         | 
| 108 | 
            +
                - - ">="
         | 
| 109 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 110 | 
            +
                    version: '0'
         | 
| 111 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 112 | 
            +
              name: pry-rails
         | 
| 113 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 114 | 
            +
                requirements:
         | 
| 115 | 
            +
                - - ">="
         | 
| 116 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 117 | 
            +
                    version: '0'
         | 
| 118 | 
            +
              type: :development
         | 
| 119 | 
            +
              prerelease: false
         | 
| 120 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 121 | 
            +
                requirements:
         | 
| 122 | 
            +
                - - ">="
         | 
| 123 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 124 | 
            +
                    version: '0'
         | 
| 125 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 126 | 
            +
              name: simplecov
         | 
| 127 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 128 | 
            +
                requirements:
         | 
| 129 | 
            +
                - - ">="
         | 
| 130 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 131 | 
            +
                    version: '0'
         | 
| 132 | 
            +
              type: :development
         | 
| 133 | 
            +
              prerelease: false
         | 
| 134 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 135 | 
            +
                requirements:
         | 
| 136 | 
            +
                - - ">="
         | 
| 137 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 138 | 
            +
                    version: '0'
         | 
| 139 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 140 | 
            +
              name: capybara
         | 
| 141 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 142 | 
            +
                requirements:
         | 
| 143 | 
            +
                - - ">="
         | 
| 144 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 145 | 
            +
                    version: '0'
         | 
| 146 | 
            +
              type: :development
         | 
| 147 | 
            +
              prerelease: false
         | 
| 148 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 149 | 
            +
                requirements:
         | 
| 150 | 
            +
                - - ">="
         | 
| 151 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 152 | 
            +
                    version: '0'
         | 
| 153 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 154 | 
            +
              name: launchy
         | 
| 155 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 156 | 
            +
                requirements:
         | 
| 157 | 
            +
                - - ">="
         | 
| 158 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 159 | 
            +
                    version: '0'
         | 
| 160 | 
            +
              type: :development
         | 
| 161 | 
            +
              prerelease: false
         | 
| 162 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 163 | 
            +
                requirements:
         | 
| 164 | 
            +
                - - ">="
         | 
| 165 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 166 | 
            +
                    version: '0'
         | 
| 167 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 168 | 
            +
              name: faker
         | 
| 169 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 170 | 
            +
                requirements:
         | 
| 171 | 
            +
                - - ">="
         | 
| 172 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 173 | 
            +
                    version: '0'
         | 
| 174 | 
            +
              type: :development
         | 
| 175 | 
            +
              prerelease: false
         | 
| 176 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 177 | 
            +
                requirements:
         | 
| 178 | 
            +
                - - ">="
         | 
| 179 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 180 | 
            +
                    version: '0'
         | 
| 181 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 182 | 
            +
              name: guard-rspec
         | 
| 183 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 184 | 
            +
                requirements:
         | 
| 185 | 
            +
                - - ">="
         | 
| 186 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 187 | 
            +
                    version: '0'
         | 
| 188 | 
            +
              type: :development
         | 
| 189 | 
            +
              prerelease: false
         | 
| 190 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 191 | 
            +
                requirements:
         | 
| 192 | 
            +
                - - ">="
         | 
| 193 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 194 | 
            +
                    version: '0'
         | 
| 195 | 
            +
            description: Easy settings engine for Rails app.
         | 
| 196 | 
            +
            email:
         | 
| 197 | 
            +
            - sveredyuk@gmail.com
         | 
| 198 | 
            +
            executables: []
         | 
| 199 | 
            +
            extensions: []
         | 
| 200 | 
            +
            extra_rdoc_files: []
         | 
| 201 | 
            +
            files:
         | 
| 202 | 
            +
            - MIT-LICENSE
         | 
| 203 | 
            +
            - README.md
         | 
| 204 | 
            +
            - Rakefile
         | 
| 205 | 
            +
            - app/assets/config/ez_settings_manifest.js
         | 
| 206 | 
            +
            - app/assets/javascripts/ez/settings/application.js
         | 
| 207 | 
            +
            - app/assets/stylesheets/ez/settings/application.css
         | 
| 208 | 
            +
            - app/cells/ez/settings/base_cell.rb
         | 
| 209 | 
            +
            - app/cells/ez/settings/group/show.slim
         | 
| 210 | 
            +
            - app/cells/ez/settings/group_cell.rb
         | 
| 211 | 
            +
            - app/cells/ez/settings/index/show.slim
         | 
| 212 | 
            +
            - app/cells/ez/settings/index_cell.rb
         | 
| 213 | 
            +
            - app/cells/ez/settings/key/show.slim
         | 
| 214 | 
            +
            - app/cells/ez/settings/key_cell.rb
         | 
| 215 | 
            +
            - app/cells/ez/settings/nav/show.slim
         | 
| 216 | 
            +
            - app/cells/ez/settings/nav_cell.rb
         | 
| 217 | 
            +
            - app/controllers/ez/settings/application_controller.rb
         | 
| 218 | 
            +
            - app/controllers/ez/settings/settings_controller.rb
         | 
| 219 | 
            +
            - app/helpers/ez/settings/application_helper.rb
         | 
| 220 | 
            +
            - app/models/ez/settings/application_record.rb
         | 
| 221 | 
            +
            - config/routes.rb
         | 
| 222 | 
            +
            - lib/ez/settings.rb
         | 
| 223 | 
            +
            - lib/ez/settings/accessors.rb
         | 
| 224 | 
            +
            - lib/ez/settings/backend/file_system.rb
         | 
| 225 | 
            +
            - lib/ez/settings/config.rb
         | 
| 226 | 
            +
            - lib/ez/settings/engine.rb
         | 
| 227 | 
            +
            - lib/ez/settings/interface.rb
         | 
| 228 | 
            +
            - lib/ez/settings/interface/group.rb
         | 
| 229 | 
            +
            - lib/ez/settings/interface/key.rb
         | 
| 230 | 
            +
            - lib/ez/settings/request_dispatcher.rb
         | 
| 231 | 
            +
            - lib/ez/settings/routes.rb
         | 
| 232 | 
            +
            - lib/ez/settings/store.rb
         | 
| 233 | 
            +
            - lib/ez/settings/version.rb
         | 
| 234 | 
            +
            - lib/generators/ez/settings/install_generator.rb
         | 
| 235 | 
            +
            homepage: https://github.com/ez-engines
         | 
| 236 | 
            +
            licenses:
         | 
| 237 | 
            +
            - MIT
         | 
| 238 | 
            +
            metadata: {}
         | 
| 239 | 
            +
            post_install_message: 
         | 
| 240 | 
            +
            rdoc_options: []
         | 
| 241 | 
            +
            require_paths:
         | 
| 242 | 
            +
            - lib
         | 
| 243 | 
            +
            required_ruby_version: !ruby/object:Gem::Requirement
         | 
| 244 | 
            +
              requirements:
         | 
| 245 | 
            +
              - - ">="
         | 
| 246 | 
            +
                - !ruby/object:Gem::Version
         | 
| 247 | 
            +
                  version: '0'
         | 
| 248 | 
            +
            required_rubygems_version: !ruby/object:Gem::Requirement
         | 
| 249 | 
            +
              requirements:
         | 
| 250 | 
            +
              - - ">="
         | 
| 251 | 
            +
                - !ruby/object:Gem::Version
         | 
| 252 | 
            +
                  version: '0'
         | 
| 253 | 
            +
            requirements: []
         | 
| 254 | 
            +
            rubyforge_project: 
         | 
| 255 | 
            +
            rubygems_version: 2.5.1
         | 
| 256 | 
            +
            signing_key: 
         | 
| 257 | 
            +
            specification_version: 4
         | 
| 258 | 
            +
            summary: Easy settings engine for Rails app.
         | 
| 259 | 
            +
            test_files: []
         |