fixed_model 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (75) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +113 -0
  4. data/Rakefile +34 -0
  5. data/lib/fixed_model.rb +12 -0
  6. data/lib/fixed_model/base.rb +125 -0
  7. data/lib/fixed_model/loader.rb +41 -0
  8. data/lib/fixed_model/version.rb +3 -0
  9. data/lib/tasks/fixed_model_tasks.rake +4 -0
  10. data/test/compliance_test.rb +9 -0
  11. data/test/dummy/README.rdoc +28 -0
  12. data/test/dummy/Rakefile +6 -0
  13. data/test/dummy/app/assets/javascripts/application.js +13 -0
  14. data/test/dummy/app/assets/stylesheets/application.css +15 -0
  15. data/test/dummy/app/controllers/application_controller.rb +5 -0
  16. data/test/dummy/app/controllers/countries_controller.rb +5 -0
  17. data/test/dummy/app/helpers/application_helper.rb +2 -0
  18. data/test/dummy/app/models/country.rb +2 -0
  19. data/test/dummy/app/views/countries/index.html.erb +6 -0
  20. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  21. data/test/dummy/bin/bundle +3 -0
  22. data/test/dummy/bin/rails +4 -0
  23. data/test/dummy/bin/rake +4 -0
  24. data/test/dummy/bin/setup +29 -0
  25. data/test/dummy/config.ru +4 -0
  26. data/test/dummy/config/application.rb +13 -0
  27. data/test/dummy/config/boot.rb +5 -0
  28. data/test/dummy/config/custom_directory/countries.yml +4 -0
  29. data/test/dummy/config/environment.rb +5 -0
  30. data/test/dummy/config/environments/development.rb +35 -0
  31. data/test/dummy/config/environments/production.rb +72 -0
  32. data/test/dummy/config/environments/test.rb +37 -0
  33. data/test/dummy/config/fixed_models/countries.yml +14 -0
  34. data/test/dummy/config/fixed_models/test/countries.yml +12 -0
  35. data/test/dummy/config/initializers/assets.rb +11 -0
  36. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  37. data/test/dummy/config/initializers/cookies_serializer.rb +3 -0
  38. data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  39. data/test/dummy/config/initializers/inflections.rb +16 -0
  40. data/test/dummy/config/initializers/mime_types.rb +4 -0
  41. data/test/dummy/config/initializers/session_store.rb +3 -0
  42. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  43. data/test/dummy/config/locales/en.yml +23 -0
  44. data/test/dummy/config/routes.rb +43 -0
  45. data/test/dummy/config/secrets.yml +22 -0
  46. data/test/dummy/db/development.sqlite3 +0 -0
  47. data/test/dummy/db/schema.rb +16 -0
  48. data/test/dummy/db/test.sqlite3 +0 -0
  49. data/test/dummy/log/development.log +4 -0
  50. data/test/dummy/log/test.log +38475 -0
  51. data/test/dummy/public/404.html +67 -0
  52. data/test/dummy/public/422.html +67 -0
  53. data/test/dummy/public/500.html +66 -0
  54. data/test/dummy/public/favicon.ico +0 -0
  55. data/test/dummy/tmp/cache/assets/test/sprockets/v3.0/-6Aexu4-9zXP7fttMrwdcp6ut5W4svYiwYaN1S11p9U.cache +0 -0
  56. data/test/dummy/tmp/cache/assets/test/sprockets/v3.0/APhcRkuSUag3sAMrv147SiqtfM-n4sIyfNkqkP5p69M.cache +1 -0
  57. data/test/dummy/tmp/cache/assets/test/sprockets/v3.0/AQff0XbTiXjznwSo3fs-LpH7XQV9hwLzDr7Hn6w4RQQ.cache +1 -0
  58. data/test/dummy/tmp/cache/assets/test/sprockets/v3.0/EXAQ6pLNH4aYOxoJcmNdZAmsPZQsxPhQe8rlbRUOeos.cache +2 -0
  59. data/test/dummy/tmp/cache/assets/test/sprockets/v3.0/TncLlV4b8r5OvOLRIye-1hdOy8VRPZug5cZ_MA3IsHQ.cache +0 -0
  60. data/test/dummy/tmp/cache/assets/test/sprockets/v3.0/V-KescSabX7T78JV-ihOuiwXQAAaK6TutFbWePxQF1E.cache +1 -0
  61. data/test/dummy/tmp/cache/assets/test/sprockets/v3.0/XZgUSUY6JX5YiIG-pYsSkUr7ydqJlk8XruCemoMycV0.cache +1 -0
  62. data/test/dummy/tmp/cache/assets/test/sprockets/v3.0/ZGNTCr85QzLaZulY-capAFXvkDEEr6N81d3bn6Iz4cA.cache +3 -0
  63. data/test/dummy/tmp/cache/assets/test/sprockets/v3.0/cOXTJSqqUSSZiPnW-HtRwyBPMvwE4ha2qZLSbmaxbIw.cache +1 -0
  64. data/test/dummy/tmp/cache/assets/test/sprockets/v3.0/gBGP0GxcODm_c-aOQnAahOusWVfj-YYTkmGUAj-E6JM.cache +0 -0
  65. data/test/dummy/tmp/cache/assets/test/sprockets/v3.0/hHaCdnnepQTM2egBk_nNjx2qtE0nJ9AoCURFgFCimW0.cache +1 -0
  66. data/test/dummy/tmp/cache/assets/test/sprockets/v3.0/o6ueeBxouoMB3i0BojV9csF7Nv7vj-VOG_uQuI50Bsw.cache +0 -0
  67. data/test/dummy/tmp/cache/assets/test/sprockets/v3.0/qU-CZGfXFumxCXsfIRmNyVkxQbynRnZU9MB3VCPUBOI.cache +2 -0
  68. data/test/dummy/tmp/cache/assets/test/sprockets/v3.0/sRGS14pPtrh56KyQ_kl9N-zL1uB9ioBkupw2fsFv5Qg.cache +3 -0
  69. data/test/dummy/tmp/cache/assets/test/sprockets/v3.0/uQRTceYLrl73R0pgWCxVOnYpJ45bJLISR0_4UDPlJz4.cache +0 -0
  70. data/test/dummy/tmp/cache/assets/test/sprockets/v3.0/ySK7zTiTeC0Yma0kl-Eg7cApzKz-5tN6Y5p6ljvFAV8.cache +1 -0
  71. data/test/fixed_model/base_test.rb +7 -0
  72. data/test/fixed_model_test.rb +127 -0
  73. data/test/integration/navigation_test.rb +14 -0
  74. data/test/test_helper.rb +18 -0
  75. metadata +198 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 9c9db27f13b391e27b06f3deee776d0cc8b35a5e
4
+ data.tar.gz: 4e090077c600752a89fd77b46615e8a5b72b7d54
5
+ SHA512:
6
+ metadata.gz: a86581d29697003d548e319606dadc70a17c42f3fff044209e2c6739a4dceb9301d013d22d42435cfdae83b1e4e877fd43338d03095481b8a84d8a27a2d0290f
7
+ data.tar.gz: e4079963dfbf17016c01b69c6b49ca99d44ef02e8b7e8f27abf4b4902e30f6500e5e0cf170e3c7651a6113caeb71983bf183d3f23ecf6f79f98d3cc4dcaaf528
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2015 mollerhoj
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,113 @@
1
+ Fixed Model
2
+ ==========
3
+
4
+ Fixed Models act like ActiveRecord models, but they are immutable and read from data files.
5
+
6
+ Sometimes we need static data in our applications. It can often be modeled as models in the MVC pattern. Instead of storing such data in the database, it can be useful to store the data in simple datafiles.
7
+
8
+ This has several advantages:
9
+
10
+ - It makes it easier for developers to quickly inspect and edit the data.
11
+ - It is trivial to make sure the data is never modified.
12
+ - It makes it easier to make assumptions about what records exists.
13
+
14
+ Installing
15
+ -----------
16
+ Include `gem fixed_model` in your `Gemfile` and run `bundle install`
17
+
18
+ Usage
19
+ -----------
20
+
21
+ To use a FixedModel, inherit from `FixedModel::Base`:
22
+
23
+ ```ruby
24
+ class Country < FixedModel::Base
25
+
26
+ end
27
+ ```
28
+
29
+ The model will read from `config/fixed_models/countries.yml`, an example of such a file could be:
30
+
31
+ ```yaml
32
+ ---
33
+ denmark:
34
+ name: Denmark
35
+ capital: Copenhagen
36
+
37
+ england:
38
+ name: England
39
+ language: English
40
+ capital: London
41
+ center:
42
+ - 53.562925
43
+ - 1.806361
44
+ features:
45
+ - :drive_on_left_side
46
+ cities:
47
+ :london:
48
+ :name: London
49
+ :status: :capital
50
+ ```
51
+
52
+ The outermost keys (in this case `denmark` and `england` will be discarded. They are only used as primary keys for merging multiple data files. (See the section "Merge files from multiple file paths").
53
+
54
+ Merge files from multiple file paths
55
+ ------------------------------------
56
+
57
+ The file paths can be changed by setting `FixedModel.file_paths`. The default is:
58
+
59
+ ```
60
+ FixedModel.file_paths = ['config/fixed_models']
61
+ ```
62
+
63
+ The data in latter file paths will override values in the former. This makes it possible to set values specific to different environments. For example, the `countries.yml` file from the previous section might have different names in production. The file `config/fixed_models/production/countries.yml` can specify that:
64
+
65
+ ```yaml
66
+ ---
67
+ england:
68
+ name: ENGLAND
69
+ ```
70
+
71
+ In the case of arrays and hashes, the former files will be appended to the latter. So in the example below England would end up with both the `:rail_roads` and the `:drive_on_left_side` features. As well as the cities `:london` and `:manchester`:
72
+
73
+ ```yaml
74
+ ---
75
+ england:
76
+ name: England
77
+ features:
78
+ - :rail_roads
79
+ cities:
80
+ :manchester:
81
+ :name: Manchester
82
+ ```
83
+
84
+ The default file paths depend on the environment. In the development environment, the data will be read from `config/fixed_models` and `config/fixed_models/development`. To change this behavior, set:
85
+
86
+ ```ruby
87
+ FixedModel.use_environment_file_paths = false
88
+ ```
89
+
90
+ Supported methods
91
+ -----------------
92
+
93
+ Fixed Models support the most common methods used on ActiveRecord models:
94
+
95
+ Class methods:
96
+ ```
97
+ find, first, second, third, fourth, last, all, count, each, attribute_names
98
+ ```
99
+
100
+ Dynamic class methods. (here, attribute name is `capital`)
101
+ ```
102
+ find_by_capital, find_by_capital!
103
+ ```
104
+
105
+ Instance methods:
106
+ ```
107
+ persisted?, new_record?, read_attribute
108
+ ```
109
+
110
+ Dynamic instance methods:
111
+ ```
112
+ capital, capital?, attribute_names
113
+ ```
data/Rakefile ADDED
@@ -0,0 +1,34 @@
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 = 'FixedModel'
12
+ rdoc.options << '--line-numbers'
13
+ rdoc.rdoc_files.include('README.rdoc')
14
+ rdoc.rdoc_files.include('lib/**/*.rb')
15
+ end
16
+
17
+
18
+
19
+
20
+
21
+
22
+ Bundler::GemHelper.install_tasks
23
+
24
+ require 'rake/testtask'
25
+
26
+ Rake::TestTask.new(:test) do |t|
27
+ t.libs << 'lib'
28
+ t.libs << 'test'
29
+ t.pattern = 'test/**/*_test.rb'
30
+ t.verbose = false
31
+ end
32
+
33
+
34
+ task default: :test
@@ -0,0 +1,12 @@
1
+ module FixedModel
2
+ autoload :Base, "fixed_model/base"
3
+ autoload :Loader, "fixed_model/loader"
4
+
5
+ cattr_accessor :file_paths
6
+ cattr_accessor :use_environment_paths
7
+
8
+ @@file_paths = ['config/fixed_models']
9
+ @@use_environment_paths = true
10
+
11
+ class RecordNotFound < StandardError; end
12
+ end
@@ -0,0 +1,125 @@
1
+ module FixedModel
2
+ class Base
3
+ include ActiveModel::Model
4
+ include ActiveModel::AttributeMethods
5
+
6
+ attribute_method_suffix '?'
7
+
8
+ class << self
9
+ delegate :find, :first, :first!, :last, :second, :second!, :third,
10
+ :third!, :fourth, :fourth!, :fifth, :fifth!, :forty_two, :forty_two!,
11
+ :count, to: :all
12
+ end
13
+
14
+ def initialize(row={})
15
+ @row = row
16
+ define_attribute_readers
17
+ end
18
+
19
+ def persisted?
20
+ true
21
+ end
22
+
23
+ def new_record?
24
+ false
25
+ end
26
+
27
+ def attributes
28
+ @row.dup.freeze
29
+ end
30
+
31
+ def self.inherited(subclass)
32
+ subclass.define_find_by_attributes
33
+ subclass.define_find_by_attributes!
34
+ define_attribute_methods(subclass.attribute_names)
35
+ end
36
+
37
+ def attribute?(attr)
38
+ send(attr.to_sym).present?
39
+ end
40
+
41
+ def self.attribute_names
42
+ names = []
43
+ data.each do |row|
44
+ names.concat(row.keys)
45
+ end
46
+ names.uniq.map(&:to_s)
47
+ end
48
+
49
+ def self.all
50
+ data.map { |row| new(row) }
51
+ end
52
+
53
+ def self.each(&block)
54
+ all.each(&block)
55
+ end
56
+
57
+ def attribute_names
58
+ @row.keys.map(&:to_s)
59
+ end
60
+
61
+ def read_attribute(attribute)
62
+ @row[attribute]
63
+ end
64
+
65
+ def ==(other)
66
+ other.instance_of?(self.class) && other.attributes == attributes
67
+ end
68
+
69
+ private
70
+
71
+ # Seems to be called when denmark.language is not defined and called.
72
+ # Look in rails source for explanation...
73
+ def attribute(attribute)
74
+ nil
75
+ end
76
+
77
+ def define_attribute_readers
78
+ attribute_names.each do |attribute_name|
79
+ define_singleton_method attribute_name do
80
+ read_attribute(attribute_name)
81
+ end
82
+ end
83
+ end
84
+
85
+ def self.singleton_class
86
+ class << self
87
+ self
88
+ end
89
+ end
90
+
91
+ def self.define_find_by_attribute(attribute)
92
+ singleton_class.send :define_method, "find_by_#{attribute}" do |arg|
93
+ row = data.find { |i| i[attribute] == arg } # or sym?
94
+ row ? self.new(row) : nil
95
+ end
96
+ end
97
+
98
+ def self.define_find_by_attribute!(attribute)
99
+ singleton_class.send :define_method, "find_by_#{attribute}!" do |arg|
100
+ row = data.find { |i| i[attribute] == arg } # or sym?
101
+ if row
102
+ self.new(row)
103
+ else
104
+ raise FixedModel::RecordNotFound
105
+ end
106
+ end
107
+ end
108
+
109
+ def self.define_find_by_attributes
110
+ attribute_names.each do |attribute|
111
+ define_find_by_attribute(attribute)
112
+ end
113
+ end
114
+
115
+ def self.define_find_by_attributes!
116
+ attribute_names.each do |attribute|
117
+ define_find_by_attribute!(attribute)
118
+ end
119
+ end
120
+
121
+ def self.data
122
+ @data ||= FixedModel::Loader.new(self).load
123
+ end
124
+ end
125
+ end
@@ -0,0 +1,41 @@
1
+ module FixedModel
2
+ class Loader
3
+ def initialize(model_name)
4
+ @model_name = model_name
5
+ end
6
+
7
+ def load
8
+ data = {}
9
+ files.each do |file|
10
+ merge_data(data, YAML.load(File.read(file)))
11
+ end
12
+ data.values
13
+ end
14
+
15
+ def merge_data(row, new_row)
16
+ new_row.each do |key, value|
17
+ if row[key].is_a?(Hash) && value.is_a?(Hash)
18
+ merge_data(row[key], value)
19
+ elsif row[key].is_a?(Array) && value.is_a?(Array)
20
+ row[key].concat(value)
21
+ else
22
+ row[key] = value
23
+ end
24
+ end
25
+ end
26
+
27
+ def files
28
+ plural_model_name = ActiveModel::Naming.plural(@model_name)
29
+ files = []
30
+ FixedModel.file_paths.each do |file_path|
31
+ files << File.join(Rails.root, file_path, "#{plural_model_name}.yml")
32
+ if FixedModel.use_environment_paths
33
+ file = File.join(Rails.root, file_path, 'test',"#{plural_model_name}.yml")
34
+ files << file if File.exists?(file)
35
+ end
36
+ end
37
+ files
38
+ end
39
+ end
40
+ end
41
+
@@ -0,0 +1,3 @@
1
+ module FixedModel
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :fixed_model do
3
+ # # Task goes here
4
+ # end
@@ -0,0 +1,9 @@
1
+ require 'test_helper'
2
+
3
+ # class ComplianceTest < ActiveSupport::TestCase
4
+ # include ActiveModel::Lint::Tests
5
+ #
6
+ # def setup
7
+ # @model = Country.new
8
+ # end
9
+ # end
@@ -0,0 +1,28 @@
1
+ == README
2
+
3
+ This README would normally document whatever steps are necessary to get the
4
+ application up and running.
5
+
6
+ Things you may want to cover:
7
+
8
+ * Ruby version
9
+
10
+ * System dependencies
11
+
12
+ * Configuration
13
+
14
+ * Database creation
15
+
16
+ * Database initialization
17
+
18
+ * How to run the test suite
19
+
20
+ * Services (job queues, cache servers, search engines, etc.)
21
+
22
+ * Deployment instructions
23
+
24
+ * ...
25
+
26
+
27
+ Please feel free to use a different markup language if you do not plan to run
28
+ <tt>rake doc:app</tt>.
@@ -0,0 +1,6 @@
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 File.expand_path('../config/application', __FILE__)
5
+
6
+ Rails.application.load_tasks
@@ -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.
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 styles
10
+ * defined in the other CSS/SCSS files in this directory. It is generally better to create a new
11
+ * file per style scope.
12
+ *
13
+ *= require_tree .
14
+ *= require_self
15
+ */